-
Notifications
You must be signed in to change notification settings - Fork 18
🔐 New address #215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/bundle
Are you sure you want to change the base?
🔐 New address #215
Conversation
WalkthroughThis pull request refactors the Bolt framework's architecture by consolidating attribute macros into a unified Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant BundleProgram as Bundle Program<br/>(example_bundle)
participant WorldProgram as World Program
participant Sysvar as Instruction Sysvar
rect rgb(200, 220, 255)
Note over Client,WorldProgram: Old Flow: CpiContext-based
Client->>WorldProgram: InitializeComponent<br/>(via CpiContext)
WorldProgram->>BundleProgram: CPI (auto-generated context)
end
rect rgb(220, 240, 220)
Note over Client,WorldProgram: New Flow: Discriminator-based
Client->>WorldProgram: InitializeComponent<br/>(with discriminator)
WorldProgram->>Sysvar: Verify caller via<br/>instruction sysvar
Sysvar-->>WorldProgram: Instruction validated
WorldProgram->>BundleProgram: CPI (explicit instruction<br/>+ accounts + discriminator)
BundleProgram-->>WorldProgram: Component initialized
end
rect rgb(240, 240, 220)
Note over Client,WorldProgram: Bundle Apply Flow
Client->>WorldProgram: ApplySystem<br/>(system_id, entities, args)
WorldProgram->>BundleProgram: Execute system<br/>(bolt_execute)
BundleProgram-->>WorldProgram: Execution result
WorldProgram->>BundleProgram: Update component<br/>(update with discriminator)
BundleProgram-->>WorldProgram: Component updated
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Areas requiring extra attention:
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 20
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DestroyComponent.cs (1)
50-65: Add discriminator handling to match InitializeComponent pattern and TS behavior.The implementation references
DestroyComponentWithDiscriminatorin its XML comment ("Mirrors TS: discriminator derived from component name if provided") but calls the non-discriminator variant instead. UnlikeInitializeComponent, which retrieves the discriminator viacomponent.GetMethodDiscriminator("initialize"), validates it, and callsWorldProgram.InitializeComponentWithDiscriminator, theDestroyComponentoverload skips this entirely.The TypeScript implementation uses
destroyComponentWithDiscriminatorwith a discriminator, but the C# variant lacks this critical step. Either the generatedDestroyComponentWithDiscriminatormethod needs to be created, or the implementation must be updated to:
- Retrieve:
var discriminator = component.GetMethodDiscriminator("destroy");- Validate:
if (discriminator is null || discriminator.Length != 8) throw...- Call:
WorldProgram.DestroyComponentWithDiscriminator(destroyComponent, discriminator);crates/programs/world/src/lib.rs (1)
461-591: Add the target program account to update/system CPIs
apply_impl,apply_with_session_impl, andsystem_executeall callinvokewithout the callee programAccountInfo. As soon as you hit these paths on-chain the CPI aborts. Append the program handle alongside the existing account infos—for example:- invoke( - &ix, - &[ - component.clone(), - ctx.accounts.authority.to_account_info(), - ctx.accounts.instruction_sysvar_account.to_account_info(), - ], - )?; + invoke( + &ix, + &[ + component.clone(), + ctx.accounts.authority.to_account_info(), + ctx.accounts.instruction_sysvar_account.to_account_info(), + program.clone(), + ], + )?;and in
apply_with_session_impladdprogram.clone(), while insystem_executepushbolt_system.to_account_info()toaccount_infosbefore invoking. Without these additions, none of the new CPI flows can succeed.crates/bolt-lang/attribute/extra-accounts/src/lib.rs (1)
8-34: Update documentation to reflect the new required attribute parameter.The macro now requires a
key = "TypePath"attribute argument (lines 27-34), but the example in the documentation (lines 14-24) shows#[extra_accounts]without any parameters. Update the example to demonstrate the new syntax, e.g.,#[extra_accounts(components = "YourComponentsType")].clients/typescript/src/world/transactions.ts (1)
359-384: Restore legacy component flow when metadata is missing
Component.fromsimply wraps the supplied id, so every existing caller that still passes a barePublicKey(the pattern we document today) ends up withcomponent.name === undefined. The new implementation still callsgetMethodDiscriminator("destroy")/("initialize")regardless, and that helper throws when the name is absent. In practice this means bothDestroyComponentandInitializeComponentnow crash before issuing any Anchor instruction, breaking the backward compatibility this PR is supposed to preserve.Please gate the discriminator path on
component.nameand retain the legacy instruction flow when the name/discriminator isn’t available (i.e. the exact logic we had before this change). Without that fallback, upgrading the SDK bricks every existing project that still uses the legacy world program.Also applies to: 416-434
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (16)
Cargo.lockis excluded by!**/*.lockclients/typescript/src/generated/accounts/Entity.tsis excluded by!**/generated/**clients/typescript/src/generated/accounts/Registry.tsis excluded by!**/generated/**clients/typescript/src/generated/accounts/World.tsis excluded by!**/generated/**clients/typescript/src/generated/idl/world.jsonis excluded by!**/generated/**clients/typescript/src/generated/index.tsis excluded by!**/generated/**clients/typescript/src/generated/instructions/addEntity.tsis excluded by!**/generated/**clients/typescript/src/generated/instructions/apply.tsis excluded by!**/generated/**clients/typescript/src/generated/instructions/apply2.tsis excluded by!**/generated/**clients/typescript/src/generated/instructions/apply3.tsis excluded by!**/generated/**clients/typescript/src/generated/instructions/apply4.tsis excluded by!**/generated/**clients/typescript/src/generated/instructions/apply5.tsis excluded by!**/generated/**clients/typescript/src/generated/instructions/initializeComponent.tsis excluded by!**/generated/**clients/typescript/src/generated/instructions/initializeNewWorld.tsis excluded by!**/generated/**clients/typescript/src/generated/instructions/initializeRegistry.tsis excluded by!**/generated/**clients/typescript/src/generated/types/world.tsis excluded by!**/generated/**
📒 Files selected for processing (82)
.github/workflows/publish-bolt-crates.yml(2 hunks).github/workflows/publish-bolt-sdk.yml(1 hunks).github/workflows/run-tests.yml(1 hunks)Anchor.toml(2 hunks)Cargo.toml(3 hunks)clients/csharp/Solana.Unity.Bolt.Test/AccelerationTest.cs(2 hunks)clients/csharp/Solana.Unity.Bolt.Test/ECSTest.cs(5 hunks)clients/csharp/Solana.Unity.Bolt.Test/Framework.cs(2 hunks)clients/csharp/Solana.Unity.Bolt/ECS/Component.cs(1 hunks)clients/csharp/Solana.Unity.Bolt/ECS/Identifier.cs(1 hunks)clients/csharp/Solana.Unity.Bolt/ECS/System.cs(1 hunks)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt.cs(2 hunks)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/ApplySystem.cs(2 hunks)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DelegateComponent.cs(1 hunks)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DestroyComponent.cs(1 hunks)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs(2 hunks)clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs(1 hunks)clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs(3 hunks)clients/typescript/.solitarc.js(1 hunks)clients/typescript/idl/world.json(1 hunks)clients/typescript/src/delegation/delegate.ts(3 hunks)clients/typescript/src/ecs/component.ts(1 hunks)clients/typescript/src/ecs/identifier.ts(1 hunks)clients/typescript/src/ecs/index.ts(1 hunks)clients/typescript/src/ecs/system.ts(1 hunks)clients/typescript/src/index.ts(2 hunks)clients/typescript/src/world/transactions.ts(9 hunks)clients/typescript/test/framework.ts(3 hunks)clients/typescript/test/intermediate-level/acceleration.ts(3 hunks)clients/typescript/test/intermediate-level/ecs.ts(4 hunks)crates/bolt-cli/src/bundle.rs(1 hunks)crates/bolt-cli/src/lib.rs(5 hunks)crates/bolt-cli/src/rust_template.rs(3 hunks)crates/bolt-cli/src/templates/bundle/lib.rs.template(1 hunks)crates/bolt-cli/src/templates/bundle/mod.rs(1 hunks)crates/bolt-cli/src/templates/mod.rs(1 hunks)crates/bolt-cli/src/templates/workspace/workspace.toml.template(1 hunks)crates/bolt-lang/Cargo.toml(1 hunks)crates/bolt-lang/attribute/Cargo.toml(1 hunks)crates/bolt-lang/attribute/bolt-program/src/lib.rs(0 hunks)crates/bolt-lang/attribute/bundle/Cargo.toml(2 hunks)crates/bolt-lang/attribute/bundle/src/lib.rs(1 hunks)crates/bolt-lang/attribute/component-deserialize/Cargo.toml(1 hunks)crates/bolt-lang/attribute/component-deserialize/src/lib.rs(3 hunks)crates/bolt-lang/attribute/component/Cargo.toml(1 hunks)crates/bolt-lang/attribute/component/src/lib.rs(1 hunks)crates/bolt-lang/attribute/delegate/src/lib.rs(0 hunks)crates/bolt-lang/attribute/extra-accounts/Cargo.toml(1 hunks)crates/bolt-lang/attribute/extra-accounts/src/lib.rs(4 hunks)crates/bolt-lang/attribute/src/bundle/mod.rs(1 hunks)crates/bolt-lang/attribute/src/common/mod.rs(1 hunks)crates/bolt-lang/attribute/src/component/attributes.rs(1 hunks)crates/bolt-lang/attribute/src/component/generate/mod.rs(1 hunks)crates/bolt-lang/attribute/src/component/generate/program.rs(1 hunks)crates/bolt-lang/attribute/src/component/mod.rs(1 hunks)crates/bolt-lang/attribute/src/delegate/mod.rs(1 hunks)crates/bolt-lang/attribute/src/lib.rs(1 hunks)crates/bolt-lang/attribute/src/system/mod.rs(1 hunks)crates/bolt-lang/attribute/system-input/src/lib.rs(2 hunks)crates/bolt-lang/attribute/system/Cargo.toml(1 hunks)crates/bolt-lang/attribute/system/src/lib.rs(1 hunks)crates/bolt-lang/src/cpi/mod.rs(1 hunks)crates/bolt-lang/src/errors.rs(1 hunks)crates/bolt-lang/src/instructions/destroy.rs(1 hunks)crates/bolt-lang/src/instructions/initialize.rs(1 hunks)crates/bolt-lang/src/instructions/mod.rs(1 hunks)crates/bolt-lang/src/instructions/update.rs(1 hunks)crates/bolt-lang/src/lib.rs(3 hunks)crates/bolt-lang/utils/src/lib.rs(2 hunks)crates/programs/bolt-component/Cargo.toml(0 hunks)crates/programs/bolt-component/src/lib.rs(0 hunks)crates/programs/bolt-system/Cargo.toml(0 hunks)crates/programs/bolt-system/Xargo.toml(0 hunks)crates/programs/bolt-system/src/lib.rs(0 hunks)crates/programs/world/Cargo.toml(1 hunks)crates/programs/world/src/lib.rs(5 hunks)crates/programs/world/src/utils.rs(1 hunks)docs/REPORT.md(1 hunks)examples/bundle/Cargo.toml(1 hunks)examples/bundle/src/lib.rs(1 hunks)scripts/test-publish.sh(1 hunks)tests/test-validator.sh(1 hunks)
💤 Files with no reviewable changes (7)
- crates/programs/bolt-system/Xargo.toml
- crates/programs/bolt-system/src/lib.rs
- crates/bolt-lang/attribute/bolt-program/src/lib.rs
- crates/programs/bolt-component/src/lib.rs
- crates/programs/bolt-system/Cargo.toml
- crates/programs/bolt-component/Cargo.toml
- crates/bolt-lang/attribute/delegate/src/lib.rs
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: notdanilo
Repo: magicblock-labs/bolt PR: 207
File: crates/bolt-lang/attribute/src/component/attributes.rs:35-56
Timestamp: 2025-10-15T16:28:50.695Z
Learning: In the Bolt Lang attribute system (crates/bolt-lang/attribute), component_id in the #[component] attribute is optional and can be empty by design. When not provided, it defaults to an empty string in the Attributes struct, with the code generator providing fallback logic (e.g., using the type name) at a later stage rather than during attribute parsing.
📚 Learning: 2025-10-15T16:28:50.695Z
Learnt from: notdanilo
Repo: magicblock-labs/bolt PR: 207
File: crates/bolt-lang/attribute/src/component/attributes.rs:35-56
Timestamp: 2025-10-15T16:28:50.695Z
Learning: In the Bolt Lang attribute system (crates/bolt-lang/attribute), component_id in the #[component] attribute is optional and can be empty by design. When not provided, it defaults to an empty string in the Attributes struct, with the code generator providing fallback logic (e.g., using the type name) at a later stage rather than during attribute parsing.
Applied to files:
crates/bolt-lang/attribute/bundle/src/lib.rscrates/bolt-lang/attribute/component-deserialize/src/lib.rscrates/bolt-lang/attribute/component/src/lib.rscrates/bolt-lang/attribute/src/component/generate/program.rscrates/bolt-lang/attribute/Cargo.tomlcrates/bolt-lang/attribute/system-input/src/lib.rscrates/bolt-lang/attribute/src/component/attributes.rscrates/bolt-lang/attribute/src/lib.rsclients/csharp/Solana.Unity.Bolt/ECS/Component.cscrates/bolt-lang/attribute/src/component/generate/mod.rscrates/bolt-cli/src/templates/bundle/lib.rs.templatecrates/bolt-lang/src/lib.rscrates/bolt-lang/attribute/system/src/lib.rscrates/bolt-lang/attribute/component/Cargo.tomlCargo.tomlcrates/bolt-lang/Cargo.tomlcrates/bolt-lang/attribute/system/Cargo.tomlcrates/bolt-lang/attribute/bundle/Cargo.tomlcrates/bolt-lang/attribute/src/component/mod.rsexamples/bundle/src/lib.rscrates/bolt-lang/attribute/component-deserialize/Cargo.toml
📚 Learning: 2025-10-15T15:51:36.753Z
Learnt from: notdanilo
Repo: magicblock-labs/bolt PR: 207
File: crates/programs/world/src/lib.rs:519-519
Timestamp: 2025-10-15T15:51:36.753Z
Learning: In the Bolt codebase, discriminator parameters should remain as `Vec<u8>` rather than `[u8; 8]` because the client code generation tooling (TypeScript/C#) cannot generate code from fixed-size array types.
Applied to files:
crates/programs/world/src/utils.rsclients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt.cscrates/bolt-lang/attribute/component-deserialize/src/lib.rscrates/bolt-lang/src/lib.rs
🧬 Code graph analysis (46)
crates/bolt-cli/src/templates/mod.rs (1)
crates/bolt-lang/attribute/bundle/src/lib.rs (1)
bundle(9-11)
clients/csharp/Solana.Unity.Bolt.Test/Framework.cs (1)
clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (14)
PublicKey(36-46)PublicKey(48-55)PublicKey(57-66)PublicKey(68-77)PublicKey(79-91)PublicKey(93-96)PublicKey(98-105)PublicKey(106-111)PublicKey(113-119)PublicKey(121-132)PublicKey(136-143)PublicKey(145-152)PublicKey(188-191)PublicKey(192-200)
crates/bolt-lang/attribute/bundle/src/lib.rs (3)
crates/bolt-lang/attribute/src/system/mod.rs (1)
process(37-84)crates/bolt-lang/attribute/src/bundle/mod.rs (1)
process(10-80)crates/bolt-lang/attribute/src/component/mod.rs (1)
process(13-31)
crates/bolt-lang/attribute/src/bundle/mod.rs (6)
crates/bolt-lang/attribute/src/common/mod.rs (1)
generate_program(5-14)crates/bolt-lang/attribute/src/system/mod.rs (1)
transform_module_for_bundle(86-195)crates/bolt-lang/attribute/src/component/generate/program.rs (3)
generate_update(191-248)generate_instructions(13-23)remove_component_attributes(9-11)crates/bolt-lang/attribute/src/component/attributes.rs (3)
from(14-20)from(24-32)from(36-56)crates/bolt-lang/attribute/src/component/generate/mod.rs (2)
generate_implementation(17-24)enrich_type(9-15)crates/bolt-lang/attribute/src/delegate/mod.rs (1)
inject_delegate_items(6-36)
clients/csharp/Solana.Unity.Bolt/ECS/Identifier.cs (6)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/ApplySystem.cs (5)
Solana(14-25)Solana(27-38)Solana(40-50)Solana(58-140)World(10-167)clients/typescript/src/index.ts (2)
Program(18-18)GetDiscriminator(26-28)clients/typescript/src/ecs/identifier.ts (1)
Identifier(4-18)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DestroyComponent.cs (1)
World(10-66)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs (1)
World(11-74)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt.cs (2)
World(13-41)GetDiscriminator(30-40)
crates/bolt-cli/src/bundle.rs (2)
crates/bolt-cli/src/rust_template.rs (1)
create_bundle(32-45)crates/bolt-cli/src/workspace.rs (1)
with_workspace(9-24)
clients/typescript/test/intermediate-level/acceleration.ts (3)
clients/typescript/src/world/transactions.ts (1)
InitializeComponent(397-442)clients/typescript/src/ecs/component.ts (1)
Component(5-27)clients/typescript/src/delegation/delegate.ts (1)
DelegateComponent(154-199)
clients/csharp/Solana.Unity.Bolt.Test/AccelerationTest.cs (3)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/ApplySystem.cs (4)
World(10-167)EntityType(143-166)EntityType(148-154)EntityType(156-165)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt.cs (2)
World(13-41)SerializeArgs(25-28)clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (16)
EntityType(164-170)EntityType(172-181)PublicKey(36-46)PublicKey(48-55)PublicKey(57-66)PublicKey(68-77)PublicKey(79-91)PublicKey(93-96)PublicKey(98-105)PublicKey(106-111)PublicKey(113-119)PublicKey(121-132)PublicKey(136-143)PublicKey(145-152)PublicKey(188-191)PublicKey(192-200)
clients/typescript/src/ecs/system.ts (1)
clients/typescript/src/ecs/identifier.ts (1)
Identifier(4-18)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt.cs (3)
clients/typescript/src/index.ts (2)
System(20-20)GetDiscriminator(26-28)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/ApplySystem.cs (1)
World(10-167)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs (1)
World(11-74)
crates/bolt-lang/src/instructions/initialize.rs (1)
crates/bolt-lang/src/cpi/mod.rs (1)
check(7-19)
crates/bolt-lang/src/instructions/update.rs (1)
crates/bolt-lang/src/cpi/mod.rs (1)
check(7-19)
crates/bolt-lang/src/cpi/mod.rs (2)
clients/typescript/test/low-level/permissioning/world.ts (1)
world(4-234)clients/typescript/src/generated/types/world.ts (1)
World(7-610)
crates/bolt-lang/attribute/component/src/lib.rs (1)
crates/bolt-lang/attribute/src/component/mod.rs (1)
process(13-31)
crates/bolt-lang/attribute/src/component/generate/program.rs (4)
crates/bolt-lang/src/lib.rs (3)
seed(61-61)size(62-62)try_from_account_info(89-97)crates/bolt-lang/src/instructions/destroy.rs (1)
destroy(5-39)crates/bolt-lang/src/instructions/initialize.rs (1)
initialize(4-14)crates/bolt-lang/src/instructions/update.rs (2)
update(6-31)update_with_session(33-78)
clients/typescript/src/ecs/component.ts (2)
clients/typescript/src/index.ts (2)
Component(20-20)FindComponentPda(109-122)clients/typescript/src/ecs/identifier.ts (1)
Identifier(4-18)
clients/typescript/test/intermediate-level/ecs.ts (3)
clients/typescript/src/world/transactions.ts (2)
InitializeComponent(397-442)ApplySystem(581-612)clients/typescript/src/ecs/component.ts (1)
Component(5-27)clients/typescript/src/ecs/system.ts (1)
System(4-12)
crates/bolt-lang/src/instructions/destroy.rs (1)
crates/bolt-lang/src/cpi/mod.rs (1)
check(7-19)
clients/typescript/test/framework.ts (2)
clients/csharp/Solana.Unity.Bolt.Test/Framework.cs (2)
Framework(32-146)Framework(64-76)clients/typescript/src/generated/accounts/World.ts (1)
World(30-170)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DelegateComponent.cs (4)
clients/typescript/src/delegation/delegate.ts (1)
DelegateComponent(154-199)clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (15)
PublicKey(36-46)PublicKey(48-55)PublicKey(57-66)PublicKey(68-77)PublicKey(79-91)PublicKey(93-96)PublicKey(98-105)PublicKey(106-111)PublicKey(113-119)PublicKey(121-132)PublicKey(136-143)PublicKey(145-152)PublicKey(188-191)PublicKey(192-200)WorldProgram(17-308)clients/typescript/src/index.ts (1)
FindComponentPda(109-122)clients/csharp/Solana.Unity.Bolt/ECS/Component.cs (4)
Component(9-34)Component(15-15)Component(20-25)Seeds(30-33)
crates/bolt-lang/attribute/src/system/mod.rs (3)
crates/bolt-lang/attribute/system-input/src/lib.rs (2)
syn(43-43)system_input(20-133)crates/bolt-lang/attribute/src/bundle/mod.rs (1)
process(10-80)crates/bolt-lang/src/lib.rs (1)
parse_args(50-55)
crates/bolt-lang/attribute/src/component/attributes.rs (1)
crates/bolt-lang/attribute/component-id/src/lib.rs (1)
component_id(9-15)
crates/bolt-lang/attribute/src/lib.rs (3)
crates/bolt-lang/attribute/bundle/src/lib.rs (1)
bundle(9-11)crates/bolt-lang/attribute/component/src/lib.rs (1)
component(17-19)crates/bolt-lang/attribute/system/src/lib.rs (1)
system(4-6)
clients/csharp/Solana.Unity.Bolt/ECS/System.cs (3)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/ApplySystem.cs (4)
Solana(14-25)Solana(27-38)Solana(40-50)Solana(58-140)clients/typescript/src/ecs/system.ts (1)
System(4-12)clients/csharp/Solana.Unity.Bolt/ECS/Identifier.cs (2)
Identifier(8-38)Identifier(24-27)
clients/typescript/src/ecs/identifier.ts (2)
clients/typescript/src/index.ts (1)
GetDiscriminator(26-28)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt.cs (1)
GetDiscriminator(30-40)
clients/csharp/Solana.Unity.Bolt/ECS/Component.cs (4)
clients/typescript/src/ecs/component.ts (1)
Component(5-27)clients/csharp/Solana.Unity.Bolt/ECS/Identifier.cs (2)
Identifier(8-38)Identifier(24-27)clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (14)
PublicKey(36-46)PublicKey(48-55)PublicKey(57-66)PublicKey(68-77)PublicKey(79-91)PublicKey(93-96)PublicKey(98-105)PublicKey(106-111)PublicKey(113-119)PublicKey(121-132)PublicKey(136-143)PublicKey(145-152)PublicKey(188-191)PublicKey(192-200)clients/csharp/Solana.Unity.Bolt/ECS/System.cs (3)
System(9-26)System(15-15)System(20-25)
crates/bolt-lang/attribute/src/component/generate/mod.rs (2)
crates/bolt-lang/utils/src/lib.rs (1)
add_bolt_metadata(4-22)crates/bolt-lang/src/lib.rs (2)
seed(61-61)size(62-62)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/ApplySystem.cs (6)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs (17)
Solana(386-400)Solana(402-429)Solana(431-447)Solana(449-465)Solana(467-479)Solana(481-493)Solana(495-507)Solana(509-526)Solana(528-540)Solana(542-554)Solana(556-570)Solana(572-584)WorldProgram(383-585)ApplyWithSessionAccounts(284-294)World(75-122)World(90-121)ApplyAccounts(274-282)clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (19)
Solana(24-28)Solana(30-34)Solana(254-279)Solana(281-307)PublicKey(36-46)PublicKey(48-55)PublicKey(57-66)PublicKey(68-77)PublicKey(79-91)PublicKey(93-96)PublicKey(98-105)PublicKey(106-111)PublicKey(113-119)PublicKey(121-132)PublicKey(136-143)PublicKey(145-152)PublicKey(188-191)PublicKey(192-200)WorldProgram(17-308)clients/csharp/Solana.Unity.Bolt/ECS/System.cs (3)
System(9-26)System(15-15)System(20-25)clients/csharp/Solana.Unity.Bolt/ECS/Component.cs (4)
Component(9-34)Component(15-15)Component(20-25)Seeds(30-33)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs (1)
World(11-74)clients/csharp/Solana.Unity.Bolt/ECS/Identifier.cs (1)
GetMethodDiscriminator(33-37)
crates/bolt-lang/src/instructions/mod.rs (3)
crates/bolt-lang/src/instructions/destroy.rs (1)
destroy(5-39)crates/bolt-lang/src/instructions/initialize.rs (1)
initialize(4-14)crates/bolt-lang/src/instructions/update.rs (1)
update(6-31)
crates/bolt-lang/attribute/src/delegate/mod.rs (2)
crates/bolt-lang/src/lib.rs (2)
seed(61-61)discriminator_from_account_info(99-104)crates/bolt-lang/attribute/src/component/generate/program.rs (2)
vec(36-41)vec(236-244)
crates/bolt-lang/src/lib.rs (2)
crates/bolt-lang/attribute/arguments/src/lib.rs (1)
arguments(6-14)crates/bolt-lang/attribute/bundle/src/lib.rs (1)
bundle(9-11)
clients/typescript/src/world/transactions.ts (3)
clients/typescript/src/index.ts (5)
Component(20-20)Program(18-18)FindComponentProgramDataPda(97-106)System(20-20)SerializeArgs(129-138)clients/typescript/src/ecs/component.ts (2)
Component(5-27)pda(16-22)clients/typescript/src/ecs/system.ts (1)
System(4-12)
clients/csharp/Solana.Unity.Bolt.Test/ECSTest.cs (5)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs (6)
Task(17-20)Task(22-25)Task(27-30)Task(32-45)Task(56-73)World(11-74)clients/csharp/Solana.Unity.Bolt.Test/Framework.cs (7)
Task(78-88)Task(90-125)Task(127-130)Task(132-140)Task(142-145)Framework(32-146)Framework(64-76)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/ApplySystem.cs (4)
World(10-167)EntityType(143-166)EntityType(148-154)EntityType(156-165)clients/csharp/Solana.Unity.Bolt/ECS/Component.cs (3)
Component(9-34)Component(15-15)Component(20-25)clients/csharp/Solana.Unity.Bolt.Test/Position.cs (3)
Position(56-90)Position(69-89)Deserialize(150-157)
crates/bolt-lang/attribute/system/src/lib.rs (1)
crates/bolt-lang/attribute/src/system/mod.rs (1)
process(37-84)
clients/typescript/src/delegation/delegate.ts (1)
clients/typescript/src/ecs/component.ts (1)
Component(5-27)
clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (3)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs (16)
Solana(386-400)Solana(402-429)Solana(431-447)Solana(449-465)Solana(467-479)Solana(481-493)Solana(495-507)Solana(509-526)Solana(528-540)Solana(542-554)Solana(556-570)Solana(572-584)ApplyAccounts(274-282)World(75-122)World(90-121)ApplyWithSessionAccounts(284-294)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/ApplySystem.cs (5)
Solana(14-25)Solana(27-38)Solana(40-50)Solana(58-140)World(10-167)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt.cs (1)
World(13-41)
crates/bolt-lang/attribute/extra-accounts/src/lib.rs (1)
crates/bolt-lang/attribute/system-input/src/lib.rs (1)
syn(43-43)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs (6)
clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (15)
PublicKey(36-46)PublicKey(48-55)PublicKey(57-66)PublicKey(68-77)PublicKey(79-91)PublicKey(93-96)PublicKey(98-105)PublicKey(106-111)PublicKey(113-119)PublicKey(121-132)PublicKey(136-143)PublicKey(145-152)PublicKey(188-191)PublicKey(192-200)WorldProgram(17-308)clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs (4)
WorldProgram(383-585)InitializeComponentAccounts(325-339)Entity(27-49)Entity(34-48)clients/typescript/src/world/transactions.ts (1)
InitializeComponent(397-442)clients/typescript/src/ecs/component.ts (1)
Component(5-27)clients/csharp/Solana.Unity.Bolt/ECS/Component.cs (4)
Component(9-34)Component(15-15)Component(20-25)Seeds(30-33)clients/csharp/Solana.Unity.Bolt/ECS/Identifier.cs (1)
GetMethodDiscriminator(33-37)
crates/bolt-cli/src/lib.rs (1)
crates/bolt-cli/src/bundle.rs (1)
new_bundle(7-42)
crates/programs/world/src/lib.rs (3)
crates/programs/world/src/utils.rs (1)
discriminator_for(19-36)clients/typescript/src/world/transactions.ts (2)
InitializeComponent(397-442)DestroyComponent(343-385)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/ApplySystem.cs (1)
World(10-167)
clients/typescript/src/index.ts (2)
clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (14)
PublicKey(36-46)PublicKey(48-55)PublicKey(57-66)PublicKey(68-77)PublicKey(79-91)PublicKey(93-96)PublicKey(98-105)PublicKey(106-111)PublicKey(113-119)PublicKey(121-132)PublicKey(136-143)PublicKey(145-152)PublicKey(188-191)PublicKey(192-200)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt.cs (1)
GetDiscriminator(30-40)
crates/bolt-lang/attribute/src/component/mod.rs (5)
crates/bolt-lang/attribute/src/common/mod.rs (1)
generate_program(5-14)crates/bolt-lang/attribute/src/delegate/mod.rs (2)
inject_delegate_items(6-36)vec(22-32)crates/bolt-lang/attribute/src/component/attributes.rs (3)
from(14-20)from(24-32)from(36-56)crates/bolt-lang/attribute/src/component/generate/mod.rs (2)
generate_implementation(17-24)enrich_type(9-15)crates/bolt-lang/attribute/src/component/generate/program.rs (4)
generate_instructions(13-23)vec(36-41)vec(236-244)generate_update(191-248)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DestroyComponent.cs (5)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs (13)
WorldProgram(383-585)Task(148-157)Task(159-168)Task(170-179)Task(181-188)Task(190-197)Task(199-206)Task(208-218)Task(220-230)Task(232-242)DestroyComponentAccounts(307-323)Entity(27-49)Entity(34-48)clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (15)
WorldProgram(17-308)PublicKey(36-46)PublicKey(48-55)PublicKey(57-66)PublicKey(68-77)PublicKey(79-91)PublicKey(93-96)PublicKey(98-105)PublicKey(106-111)PublicKey(113-119)PublicKey(121-132)PublicKey(136-143)PublicKey(145-152)PublicKey(188-191)PublicKey(192-200)clients/typescript/src/world/transactions.ts (1)
DestroyComponent(343-385)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs (5)
Task(17-20)Task(22-25)Task(27-30)Task(32-45)Task(56-73)clients/csharp/Solana.Unity.Bolt/ECS/Component.cs (4)
Component(9-34)Component(15-15)Component(20-25)Seeds(30-33)
clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs (3)
clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs (19)
Solana(24-28)Solana(30-34)Solana(254-279)Solana(281-307)PublicKey(36-46)PublicKey(48-55)PublicKey(57-66)PublicKey(68-77)PublicKey(79-91)PublicKey(93-96)PublicKey(98-105)PublicKey(106-111)PublicKey(113-119)PublicKey(121-132)PublicKey(136-143)PublicKey(145-152)PublicKey(188-191)PublicKey(192-200)WorldProgram(17-308)clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs (6)
World(11-74)Task(17-20)Task(22-25)Task(27-30)Task(32-45)Task(56-73)clients/typescript/src/generated/accounts/World.ts (1)
World(30-170)
crates/bolt-cli/src/rust_template.rs (2)
crates/bolt-cli/src/templates/bundle/mod.rs (1)
create_bundle_template(5-16)crates/bolt-cli/src/templates/workspace/mod.rs (3)
workspace_manifest(4-6)cargo_toml_with_serde(47-55)xargo_toml(57-59)
examples/bundle/src/lib.rs (4)
crates/bolt-lang/attribute/bundle/src/lib.rs (1)
bundle(9-11)crates/bolt-lang/attribute/component/src/lib.rs (1)
component(17-19)crates/bolt-lang/attribute/system/src/lib.rs (1)
system(4-6)crates/bolt-lang/attribute/system-input/src/lib.rs (1)
system_input(20-133)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: lint (yarn)
- GitHub Check: lint (clippy)
- GitHub Check: test
🔇 Additional comments (65)
crates/bolt-cli/src/templates/workspace/workspace.toml.template (1)
2-7: Workspace member patterns reference non-existent directories—clarification needed.The template syntax is valid, but verification reveals that the referenced directories don't exist in the repository:
programs/— not foundprograms-ecs/bundles/— not foundprograms-ecs/— not foundThe glob patterns are syntactically correct for Cargo, but currently unmatched. Clarify whether:
- This is intentional setup for future bundle/program implementations (acceptable for forward-looking template), or
- These patterns should be added only when actual crates are created, or
- The patterns are erroneous and should be removed
crates/bolt-lang/attribute/extra-accounts/Cargo.toml (1)
17-18: LGTM: Dependency additions align with attribute macro requirements.The addition of
heck(typically used for case conversions) and explicit declaration ofproc-macro2are appropriate for procedural macro crates. Both correctly reference workspace versions.clients/typescript/idl/world.json (1)
203-203: LGTM: IDL metadata address updated consistently.The metadata address field has been updated to match the new World program ID. This is consistent with the program ID change in
.solitarc.js.scripts/test-publish.sh (1)
17-17: LGTM: bolt-attribute-bolt-bundle added to publish list.The new bundle attribute crate has been correctly added to the workspace publish command, aligning with the broader reorganization mentioned in the PR summary.
crates/bolt-lang/attribute/component-deserialize/Cargo.toml (1)
19-19: LGTM: sha2 dependency supports discriminator generation.The
sha2dependency addition aligns with the PR's introduction of SHA-256 based discriminators for components. This is the standard crate for SHA-2 hashing in Rust.crates/programs/world/Cargo.toml (1)
30-30: LGTM: const-crypto dependency added for compile-time cryptographic operations.The addition of
const-cryptolikely supports compile-time discriminator or hash computations, aligning with the architectural changes in this PR.tests/test-validator.sh (1)
10-10: LGTM: Test validator configuration updated with new World program ID.The
--bpf-programargument has been updated to match the new World program ID, maintaining consistency with the changes in.solitarc.jsandworld.json.clients/typescript/.solitarc.js (1)
10-10: Program ID migration verified—all references successfully updated.The old World program ID (WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n) has been completely removed from the codebase. The new program ID (wor1DcaDr8AeBdaf5bqeBQUY9My2sgZwtanRcaALE9L) is correctly set across all locations: configuration files, Rust program declaration, generated TypeScript bindings, test scripts, and CLI templates. The breaking change has been applied consistently.
.github/workflows/publish-bolt-sdk.yml (1)
147-147: Ensure EXAMPLE_BUNDLE secret is configured in GitHub repository settings.The workflow references the
EXAMPLE_BUNDLEsecret alongside other deployment key secrets (WORLD, BOLT_COMPONENT, BOLT_SYSTEM, COMPONENT_POSITION, COMPONENT_VELOCITY, SYSTEM_APPLY_VELOCITY, SYSTEM_FLY, SYSTEM_SIMPLE_MOVEMENT). This follows the established pattern across all three workflows (run-tests.yml, publish-bolt-crates.yml, publish-bolt-sdk.yml). Verify that this secret has been added to the repository's GitHub Actions secrets settings before merging.crates/bolt-cli/src/templates/mod.rs (1)
1-1: LGTM! Clean module declaration.The new
bundlesubmodule is properly exposed, aligning with the PR's bundle-based architecture changes.docs/REPORT.md (1)
7-8: LGTM! Updated benchmark data.The CU (Compute Unit) values have been updated to reflect the new bundle-based architecture's performance characteristics. The chart structure remains consistent.
.github/workflows/publish-bolt-crates.yml (1)
184-184: LGTM! Publishing list updated correctly.The addition of
bolt-attribute-bolt-bundlealigns with the PR's consolidation of attribute macros into the unified bolt-attribute crate..github/workflows/run-tests.yml (1)
92-92: LGTM! Test workflow updated consistently.The example bundle keypair provisioning aligns with the changes in the publish workflow, ensuring consistent deployment key handling across workflows.
crates/bolt-cli/src/lib.rs (5)
4-4: LGTM! New module declaration.The bundle module is properly integrated into the CLI structure.
14-14: LGTM! Import added correctly.The
new_bundlefunction import follows the same pattern as the existingnew_componentandnew_systemimports.
44-45: LGTM! Command variant added with clear documentation.The Bundle command variant is properly documented and follows the existing CLI command pattern.
76-79: LGTM! Command struct follows existing pattern.The
BundleCommandstruct is consistent withComponentCommandandSystemCommandstructures.
198-198: LGTM! Command wiring is correct.The Bundle command is properly dispatched to the
new_bundlefunction, following the same pattern as Component and System commands.crates/programs/world/src/utils.rs (1)
1-18: Excellent documentation of collision risk.The documentation clearly explains the 8-byte discriminator collision probability and its practical acceptability. This transparency is valuable for maintainers.
crates/bolt-lang/attribute/bundle/src/lib.rs (2)
3-7: Excellent macro documentation.The documentation clearly explains that the
#[bundle]macro combines a#[component]and#[system]into a unified Anchor#[program]module, making the behavior transparent to users.
8-11: LGTM! Clean macro delegation pattern.The procedural macro correctly delegates to
bolt_attribute::bundle::process, following the established pattern used by other attribute macros in the codebase (component, system).clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs (2)
1-1: LGTM! Appropriate pragma for generated code.The
#pragma warning disable CS1591is appropriate for generated code where XML documentation comments may not be present.
385-385: Breaking change properly coordinated across all clients.Verification confirms the World program ID update is consistently applied across all language clients. The old ID (
WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n) is completely removed, and the new ID (wor1DcaDr8AeBdaf5bqeBQUY9My2sgZwtanRcaALE9L) is present in Rust, TypeScript, C#, and configuration files without gaps or inconsistencies. The C# file at line 385 correctly reflects this change.clients/csharp/Solana.Unity.Bolt/ECS/System.cs (2)
1-15: LGTM!The class structure properly mirrors the TypeScript implementation and correctly extends Identifier. The constructor appropriately delegates to the base class.
20-25: LGTM!The static factory method correctly implements polymorphic input handling, matching the TypeScript implementation. The use of
global::System.ArgumentExceptionproperly avoids naming conflicts with theBolt.Systemclass.crates/bolt-cli/src/bundle.rs (1)
34-37: LGTM!The config serialization and file writing logic is correct. The success message clearly indicates completion.
Anchor.toml (3)
10-28: LGTM! Architectural refactoring reflected correctly.The removal of standalone
bolt-componentandbolt-systemprograms and the addition ofexample-bundleand related systems align with the PR's objective to introduce bundle-based architecture. The configuration changes are consistent with the broader refactoring.
37-38: LGTM! Workspace members updated.The workspace members have been appropriately updated to reflect the architectural changes, removing standalone component/system entries and adding bundle-related examples. The reorganization is consistent with the PR's objectives.
8-8: Verification confirmed: World program ID updated consistently across codebase.The new program ID
wor1DcaDr8AeBdaf5bqeBQUY9My2sgZwtanRcaALE9Lis properly referenced throughout the codebase, including in Anchor.toml, Rust program declarations, TypeScript and C# generated clients, test scripts, and IDL files. The old program ID has been completely removed with no remaining references. Deployment coordination is complete.crates/bolt-lang/attribute/component/src/lib.rs (1)
1-19: LGTM! Clean delegation refactoring.The refactoring successfully delegates component attribute processing to the centralized
bolt_attribute::component::processfunction, eliminating local macro expansion logic while preserving the public API. This aligns with the broader architectural shift toward consolidated attribute processing.crates/bolt-lang/attribute/component-deserialize/src/lib.rs (2)
42-44: LGTM! Proper discriminator generation.The discriminator generation correctly uses SHA-256 hashing with the Anchor-standard
"account:{name}"format, replacing the placeholder discriminator. This ensures unique and collision-resistant discriminators for component types.
76-78: LGTM! Discriminator correctly applied.The
Discriminatortrait implementation now uses the computed discriminator instead of a hardcoded placeholder. The quote macro expansion correctly converts theVec<u8>into individual byte literals.crates/bolt-lang/attribute/system/src/lib.rs (1)
1-6: LGTM! Consistent delegation refactoring.The refactoring mirrors the component attribute changes, successfully delegating system attribute processing to the centralized
bolt_attribute::system::processfunction. The parameter rename from_attrtoattris appropriate since the attribute tokens are now consumed by the delegated processor.examples/bundle/Cargo.toml (1)
1-25: LGTM! Standard bundle manifest setup.The Cargo manifest follows Solana program conventions with appropriate crate types, features, and workspace dependencies.
crates/bolt-lang/attribute/component/Cargo.toml (1)
14-15: LGTM! Clean dependency consolidation.Replacing multiple explicit dependencies with a single workspace dependency simplifies maintenance and aligns with the PR's attribute crate consolidation.
clients/typescript/src/index.ts (2)
26-28: LGTM! Discriminator computation matches Anchor convention.The GetDiscriminator function correctly implements the Anchor discriminator pattern (first 8 bytes of SHA256 hash) and aligns with the C# implementation added in this PR.
20-20: LGTM! Public API expansion for ECS support.Re-exporting Component and System makes them available to consumers as part of the bundle/ECS feature set introduced in this PR.
crates/bolt-lang/src/errors.rs (1)
11-16: LGTM! Clear error variants for validation checks.The new error codes provide specific feedback for account validation and delegateability checks, supporting the expanded CPI validation introduced in this PR.
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt.cs (2)
30-40: LGTM! Discriminator computation consistent across languages.The GetDiscriminator implementation correctly matches the Anchor pattern and aligns with the TypeScript version. Proper use of
usingstatement ensures SHA256 disposal.
27-27: LGTM! Explicit encoding improves clarity.Using
Encoding.UTF8.GetBytesmakes the encoding explicit and consistent with the GetDiscriminator implementation.crates/bolt-lang/utils/src/lib.rs (1)
5-19: Review field order change for serialization compatibility.The change from
push()toinsert(0, ...)is a valid concern. Structs decorated with#[component_deserialize]deriveAnchorDeserializeandAnchorSerialize, which serialize in field declaration order. Movingbolt_metadatafrom the end to the beginning changes the byte layout of serialized accounts.If existing components with
bolt_metadataat the end have already been deployed and serialized on-chain, deserializing them with the field at the beginning will cause all fields to misalign during deserialization, resulting in data corruption or deserialization failures.Verification needed:
- Is this a new feature or a reordering of an existing field?
- Are there any existing serialized component instances that would be affected?
- If this is a breaking change to existing data, migration or versioning logic should be implemented.
clients/csharp/Solana.Unity.Bolt.Test/Framework.cs (1)
59-75: Code is correct as-is.The verification confirms that
BundlePositionEntity1PdaandBundleVelocityEntity1Pdaare properly initialized before use. Both properties are assigned in test setup (ECSTest.cs lines 100 and 117) before being accessed (lines 103, 120, 214, 236). The bundle test infrastructure additions are sound.crates/bolt-lang/Cargo.toml (1)
19-19: LGTM! Consolidation of attribute dependencies.The introduction of
bolt-attribute-bolt-bundleto replace the previousbolt-attribute-bolt-programandbolt-attribute-bolt-delegatedependencies aligns with the PR's bundle-based refactoring approach.crates/bolt-lang/attribute/src/lib.rs (1)
1-7: LGTM! Clean proc macro crate structure.The file properly sets up the procedural macro infrastructure with appropriate module visibility. The public exports (
bundle,component,delegate,system) and privatecommonmodule align well with the attribute consolidation architecture.crates/bolt-cli/src/rust_template.rs (2)
31-45: LGTM! Consistent bundle creation pattern.The
create_bundlefunction follows the established pattern used bycreate_componentandcreate_system, appropriately usingcargo_toml_with_serdeto include serde support and placing bundles underprograms-ecs/bundles.
90-90: World program ID update is consistently applied across the codebase.Verification confirms the old program ID has been completely removed and the new ID (
wor1DcaDr8AeBdaf5bqeBQUY9My2sgZwtanRcaALE9L) is consistently present in all required locations: Anchor configuration, Rust program declaration, all TypeScript client files (instructions, accounts, IDL), C# client, test setup, and the specified template file.Cargo.toml (2)
22-23: LGTM! New attribute dependencies for bundle support.The addition of
bolt-attributeandbolt-attribute-bolt-bundleworkspace dependencies properly supports the new unified attribute architecture.
64-65: LGTM! Cryptographic dependencies for discriminator computation.The addition of
const-cryptoandsha2dependencies appropriately supports the discriminator generation functionality introduced in this PR (e.g., forGetDiscriminatormethods in the client libraries).clients/csharp/Solana.Unity.Bolt/ECS/Identifier.cs (1)
1-39: LGTM! Well-structured ECS identifier abstraction.The
Identifierclass correctly mirrors the TypeScript implementation, with proper discriminator generation logic that matches the format"global:" + (name? name + "_" : "") + method. The delegation toWorld.GetDiscriminatorfor SHA256 computation ensures consistency across language bindings.clients/typescript/test/intermediate-level/acceleration.ts (2)
47-71: LGTM! Comprehensive test for bundled component initialization.The test properly validates bundled component creation using the new
Componentclass with both program ID and name parameters. The position coordinate assertions (x=0, y=0, z=0) appropriately verify initial state.
93-114: LGTM! Delegation test mirrors existing pattern.The bundled component delegation test follows the established pattern from the non-bundled test above it, appropriately verifying that the delegated component's owner is updated to the delegation program.
crates/bolt-cli/src/templates/bundle/mod.rs (1)
5-16: LGTM! Standard template generation implementation.The
create_bundle_templatefunction follows the established pattern used by component and system templates, properly usinganchor_cli::rust_template::get_or_create_program_idand formatting the template with derived identifiers.clients/typescript/src/delegation/delegate.ts (1)
167-181: LGTM! Enhanced flexibility with backward compatibility.The parameter type change from
PublicKeytoPublicKey | Componentand the use ofComponent.from()provide a more flexible API while maintaining backward compatibility. Thecomponent.pda()method properly encapsulates PDA derivation logic for both standalone and bundled components.examples/bundle/src/lib.rs (4)
8-14: LGTM!The Position component is correctly configured with the
delegateattribute andDefaultderive. The default values (0, 0, 0) align with test expectations.
34-51: LGTM!The movement system correctly increments the position by the velocity values. The logic is straightforward and matches the expected behavior verified in tests.
53-66: LGTM!The stop system correctly resets velocity to zero. The system input appropriately includes only the velocity component since position is not needed.
23-32: No issues found—bolt_metadatafield is correctly injected by the macro.The
#[component]macro (viaadd_bolt_metadata()incrates/bolt-lang/utils/src/lib.rs) automatically injects thebolt_metadatafield into the struct during macro expansion. This happens before theDefaultimpl is compiled, so the field is available when the impl references it. The pattern is intentional and consistent across the codebase—other code (e.g., inprogram.rs) also accessesbolt_metadataon component instances, confirming this is the designed behavior.crates/bolt-lang/attribute/src/component/attributes.rs (1)
1-101: LGTM!The attribute parsing logic is well-structured and handles various meta attribute forms (Path, List, NameValue) defensively. The use of unwrap/expect in macro code is acceptable since syntax errors should fail at compile time. The component_id defaulting to an empty string aligns with the design noted in learnings.
Based on learnings
clients/csharp/Solana.Unity.Bolt.Test/ECSTest.cs (4)
94-109: LGTM!The test correctly initializes a bundled position component and verifies the default values (0, 0, 0) match the Position struct's Default implementation.
158-177: LGTM!The refactor to use
Bolt.World.ApplySystemwith the bundled API is cleaner, and the corrected assertion (Y=1 on line 175) properly reflects the "Up" movement incrementing the Y coordinate.
199-220: LGTM!The bundled movement system test correctly verifies that position is incremented by velocity (0+1, 0+2, 0+3 → 1, 2, 3), and properly supplies both required components (position and velocity) to the system.
222-245: LGTM!The bundled stop system test correctly verifies that velocity is zeroed out, and appropriately supplies only the velocity component since the stop system doesn't require position.
crates/bolt-lang/attribute/extra-accounts/src/lib.rs (1)
78-99: LGTM!The refactor to dynamic trait naming and explicit type parameter improves type safety. The change from
Self::NUMBER_OF_COMPONENTSto<#type_path as bolt_lang::NumberOfComponents>::NUMBER_OF_COMPONENTS(line 90) correctly references the component count from the specified type, ensuring proper indexing intoremaining_accounts.crates/bolt-lang/attribute/src/delegate/mod.rs (2)
49-84: LGTM!The delegate function correctly matches component discriminators and builds PDA seeds from the component's seed, name, and entity key. The error handling for non-delegateable components is appropriate.
153-192: LGTM!The undelegate function correctly calls the Bolt Lang undelegation helper with the appropriate accounts. The generated structs have proper Anchor account constraints.
| echo ${{ secrets.SYSTEM_FLY }} > target/deploy/system_fly-keypair.json | ||
| echo ${{ secrets.SYSTEM_SIMPLE_MOVEMENT }} > target/deploy/system_simple_movement-keypair.json | ||
| echo ${{ secrets.EXAMPLE_BUNDLE }} > target/deploy/example_bundle-keypair.json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify the new deployment secret is configured.
Ensure the EXAMPLE_BUNDLE secret is properly configured in the repository settings to avoid deployment failures.
🏁 Script executed:
#!/bin/bash
# Check if the secret reference is used consistently across all workflow files
rg -n "EXAMPLE_BUNDLE" .github/workflows/Length of output: 390
---
🏁 Script executed:
```shell
#!/bin/bash
# Check if EXAMPLE_BUNDLE secret is configured in the repository
gh secret list --repo "$GITHUB_REPOSITORY" 2>/dev/null | grep -i "EXAMPLE_BUNDLE" || echo "Secret not found in list"
Length of output: 24
Configure the EXAMPLE_BUNDLE secret in repository settings before deployment.
The secret is referenced in three workflow files (run-tests.yml:92, publish-bolt-sdk.yml:147, publish-bolt-crates.yml:142) but is not configured in repository settings. Add this secret to avoid runtime deployment failures.
| public static async Task InitializeBundledVelocityOnEntity1(Framework framework) { | ||
| var initializeComponent = await Bolt.World.InitializeComponent( | ||
| framework.Wallet.Account.PublicKey, | ||
| framework.Entity1Pda, | ||
| new Bolt.Component(framework.ExampleBundleProgramId, "velocity") | ||
| ); | ||
| framework.BundleVelocityEntity1Pda = initializeComponent.Pda; | ||
| await framework.SendAndConfirmInstruction(initializeComponent.Instruction); | ||
|
|
||
| var accountInfo = await framework.GetAccountInfo(framework.BundleVelocityEntity1Pda); | ||
| var data = Convert.FromBase64String(accountInfo.Data[0]); | ||
| int offset = 8; // skip discriminator | ||
| long x = BitConverter.ToInt64(data, offset); offset += 8; | ||
| long y = BitConverter.ToInt64(data, offset); offset += 8; | ||
| long z = BitConverter.ToInt64(data, offset); offset += 8; | ||
| Debug.Assert(1 == x, "X is not equal to 1"); | ||
| Debug.Assert(2 == y, "Y is not equal to 2"); | ||
| Debug.Assert(3 == z, "Z is not equal to 3"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider consistent deserialization approach.
This test manually deserializes velocity data (lines 122-125) with explicit byte offset handling, while InitializeBundledPositionOnEntity1 (line 105) uses Position.Deserialize. If a Velocity.Deserialize method exists or could be generated, using it would improve consistency and reduce manual offset calculations. If the bundle program's velocity doesn't have a generated deserializer, consider documenting why manual deserialization is necessary here.
🤖 Prompt for AI Agents
In clients/csharp/Solana.Unity.Bolt.Test/ECSTest.cs around lines 111 to 129 the
test manually parses velocity fields by byte offsets instead of using a
deserializer; replace the manual offset/BitConverter logic with a call to
Velocity.Deserialize (or the generated deserializer for the bundle velocity
type) and assert on the deserialized X/Y/Z properties, ensuring discriminator
handling is done by the deserializer; if no deserializer exists,
generate/implement Velocity.Deserialize in the bundle types (or add a brief
comment explaining why manual deserialization is required) and keep the
assertions identical.
| public string Seeds(string seed = null) | ||
| { | ||
| return (seed ?? "") + (Name ?? ""); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix seed concatenation order to match on-chain PDA derivation.
Seeds() currently returns (seed ?? "") + (Name ?? ""), which is reversed compared with the TypeScript implementation ((name ?? "") + (seed ?? "")). When both a component name and an additional seed are supplied (e.g., bundle component with an extra user seed), the C# client derives a different PDA than the TypeScript client and the on-chain program, causing instructions to fail. Mirror the TS ordering so the name prefix always precedes the optional seed.
- return (seed ?? "") + (Name ?? "");
+ return (Name ?? "") + (seed ?? "");🤖 Prompt for AI Agents
In clients/csharp/Solana.Unity.Bolt/ECS/Component.cs around lines 30 to 33, the
Seeds method concatenates the optional seed before the component name causing
PDAs to be derived in the wrong order; change the concatenation so the component
name prefix comes first by returning (Name ?? "") + (seed ?? "") instead of
(seed ?? "") + (Name ?? "") so the C# client matches the TypeScript/on-chain PDA
derivation.
| var data = Concat(discriminator, commitFrequencyBytes, validatorNoneTag); | ||
|
|
||
| TransactionInstruction instruction = new TransactionInstruction() { | ||
| ProgramId = componentId, | ||
| Keys = new List<AccountMeta>() { | ||
| AccountMeta.ReadOnly(payer, true), | ||
| AccountMeta.ReadOnly(entity, false), | ||
| AccountMeta.Writable(account, false), | ||
| AccountMeta.ReadOnly(componentId, false), | ||
| AccountMeta.Writable(bufferPda, false), | ||
| AccountMeta.Writable(delegationRecord, false), | ||
| AccountMeta.Writable(delegationMetadata, false), | ||
| AccountMeta.ReadOnly(WorldProgram.DelegationProgram, false), | ||
| AccountMeta.ReadOnly(SystemProgram.ProgramIdKey, false), | ||
| }, | ||
| Data = data, | ||
| }; | ||
| return new DelegateComponentInstruction() { | ||
| Pda = account, | ||
| Instruction = instruction, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix delegate instruction payload serialization.
The delegate Beet layout still ends with pdaSeeds: Vec<Bytes>, but we only write the discriminator, the commitFrequencyMs u32 and the COption tag. On chain the decoder immediately tries to pull the vector length, hits EOF, and returns InvalidInstructionData, so both overloads fail as soon as the world program inspects the payload (bundle seeds never arrive). Please emit the Vec encoding (length + element length/value) using the same seed/component.Seeds(seed) you rely on locally, and make sure every little‑endian field stays little‑endian regardless of host endianness.
@@
- uint commitFrequencyMs = 0;
- byte[] commitFrequencyBytes = BitConverter.GetBytes(commitFrequencyMs); // little-endian on most platforms
- byte[] validatorNoneTag = new byte[] { 0 }; // COption None
-
- var data = Concat(discriminator, commitFrequencyBytes, validatorNoneTag);
+ uint commitFrequencyMs = 0;
+ byte[] commitFrequencyBytes = BitConverter.GetBytes(commitFrequencyMs);
+ if (!BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(commitFrequencyBytes);
+ }
+ byte[] validatorNoneTag = new byte[] { 0 }; // COption None
+
+ byte[] data;
+ if (string.IsNullOrEmpty(seed))
+ {
+ data = Concat(discriminator, commitFrequencyBytes, validatorNoneTag, new byte[4]);
+ }
+ else
+ {
+ var seedsVecLength = BitConverter.GetBytes(1u);
+ var seedBytes = Encoding.UTF8.GetBytes(seed);
+ var seedLength = BitConverter.GetBytes((uint)seedBytes.Length);
+ if (!BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(seedsVecLength);
+ Array.Reverse(seedLength);
+ }
+ data = Concat(
+ discriminator,
+ commitFrequencyBytes,
+ validatorNoneTag,
+ seedsVecLength,
+ seedLength,
+ seedBytes
+ );
+ }
@@
- var account = WorldProgram.FindComponentPda(component.Program, entity, component.Seeds(seed));
+ var componentSeed = component.Seeds(seed) ?? string.Empty;
+ var account = WorldProgram.FindComponentPda(component.Program, entity, componentSeed);
@@
- uint commitFrequencyMs = 0;
- byte[] commitFrequencyBytes = BitConverter.GetBytes(commitFrequencyMs);
- byte[] validatorNoneTag = new byte[] { 0 };
-
- var data = Concat(discriminator, commitFrequencyBytes, validatorNoneTag);
+ uint commitFrequencyMs = 0;
+ byte[] commitFrequencyBytes = BitConverter.GetBytes(commitFrequencyMs);
+ if (!BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(commitFrequencyBytes);
+ }
+ byte[] validatorNoneTag = new byte[] { 0 };
+
+ byte[] data;
+ if (string.IsNullOrEmpty(componentSeed))
+ {
+ data = Concat(discriminator, commitFrequencyBytes, validatorNoneTag, new byte[4]);
+ }
+ else
+ {
+ var seedsVecLength = BitConverter.GetBytes(1u);
+ var seedBytes = Encoding.UTF8.GetBytes(componentSeed);
+ var seedLength = BitConverter.GetBytes((uint)seedBytes.Length);
+ if (!BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(seedsVecLength);
+ Array.Reverse(seedLength);
+ }
+ data = Concat(
+ discriminator,
+ commitFrequencyBytes,
+ validatorNoneTag,
+ seedsVecLength,
+ seedLength,
+ seedBytes
+ );
+ }Also applies to: 64-95
🤖 Prompt for AI Agents
In clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DelegateComponent.cs
around lines 36 to 56 (and similarly lines 64-95), the serialized instruction
Data only contains the discriminator, commitFrequencyBytes and the COption tag
but omits the Vec<Bytes> pdaSeeds payload and may use host endianness; update
the serialization to append the Vec encoding: write the vector length (as
little-endian uint32 or the Beet/BEET-expected integer size), then for each seed
use the same seed/component.Seeds(seed) routine to write element length + bytes,
and ensure all integer fields (commitFrequencyMs, lengths, tags) are serialized
in little-endian explicitly so the on-chain decoder can read the vector and all
fields correctly.
| public static async Task<InitializeComponentInstruction> InitializeComponent(PublicKey payer, PublicKey entity, Component component, string seed = "", PublicKey authority = null) { | ||
| if (component is null) throw new ArgumentNullException(nameof(component)); | ||
| var discriminator = component.GetMethodDiscriminator("initialize"); | ||
| if (discriminator is null || discriminator.Length != 8) throw new ArgumentException("Invalid discriminator", nameof(component)); | ||
| var componentPda = WorldProgram.FindComponentPda(component.Program, entity, component.Seeds(seed)); | ||
| var initializeComponent = new InitializeComponentAccounts() { | ||
| Payer = payer, | ||
| Entity = entity, | ||
| Data = componentPda, | ||
| ComponentProgram = component.Program, | ||
| Authority = authority ?? new PublicKey(WorldProgram.ID), | ||
| }; | ||
| var instruction = WorldProgram.InitializeComponentWithDiscriminator(initializeComponent, discriminator); | ||
| return new InitializeComponentInstruction() { | ||
| Pda = componentPda, | ||
| Instruction = instruction | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
LGTM! Consider simplifying discriminator validation.
The new overload correctly implements discriminator-based initialization for bundled components, mirroring the TypeScript implementation. The validation and PDA derivation logic are sound.
However, consider whether the discriminator validation at lines 58-59 is necessary. Based on the Identifier.GetMethodDiscriminator implementation, it should always return a valid 8-byte array. If this is guaranteed by design, the validation could be simplified or removed.
🤖 Prompt for AI Agents
In clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs
around lines 56 to 73, the explicit null/length validation for the discriminator
is redundant because Identifier.GetMethodDiscriminator always returns a valid
8-byte array; remove the two-line check (or replace it with a non-throwing
debug/assert) so the method relies on the guaranteed contract instead of
throwing ArgumentException—if you prefer a safety net, change the check to a
Debug.Assert(discriminator != null && discriminator.Length == 8) so it only runs
in debug builds and does not affect runtime behavior.
| pub fn check(instruction_sysvar_account: &AccountInfo<'_>) -> Result<()> { | ||
| let instruction = anchor_lang::solana_program::sysvar::instructions::get_instruction_relative( | ||
| 0, | ||
| &instruction_sysvar_account.to_account_info(), | ||
| ) | ||
| .map_err(|_| BoltError::InvalidCaller)?; | ||
| require_eq!( | ||
| instruction.program_id, | ||
| World::id(), | ||
| BoltError::InvalidCaller | ||
| ); | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: CPI validation checks wrong instruction index.
The function uses get_instruction_relative(0, ...) which retrieves the current instruction, not the calling instruction. This means:
- When called directly (not via CPI), it checks if the direct call is from World → passes if called as World instruction
- When called via CPI from World, it checks if the CPI target instruction is from World → fails incorrectly
To validate that a function is called via CPI from World, you need to check the previous instruction (the caller):
- let instruction = anchor_lang::solana_program::sysvar::instructions::get_instruction_relative(
- 0,
- &instruction_sysvar_account.to_account_info(),
- )
+ let instruction = anchor_lang::solana_program::sysvar::instructions::get_instruction_relative(
+ -1,
+ &instruction_sysvar_account.to_account_info(),
+ )
.map_err(|_| BoltError::InvalidCaller)?;With index -1, the function checks the calling instruction, ensuring that component/system methods can only be invoked via CPI from the World program.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| pub fn check(instruction_sysvar_account: &AccountInfo<'_>) -> Result<()> { | |
| let instruction = anchor_lang::solana_program::sysvar::instructions::get_instruction_relative( | |
| 0, | |
| &instruction_sysvar_account.to_account_info(), | |
| ) | |
| .map_err(|_| BoltError::InvalidCaller)?; | |
| require_eq!( | |
| instruction.program_id, | |
| World::id(), | |
| BoltError::InvalidCaller | |
| ); | |
| Ok(()) | |
| } | |
| pub fn check(instruction_sysvar_account: &AccountInfo<'_>) -> Result<()> { | |
| let instruction = anchor_lang::solana_program::sysvar::instructions::get_instruction_relative( | |
| -1, | |
| &instruction_sysvar_account.to_account_info(), | |
| ) | |
| .map_err(|_| BoltError::InvalidCaller)?; | |
| require_eq!( | |
| instruction.program_id, | |
| World::id(), | |
| BoltError::InvalidCaller | |
| ); | |
| Ok(()) | |
| } |
🤖 Prompt for AI Agents
In crates/bolt-lang/src/cpi/mod.rs around lines 7 to 19, the CPI validation is
using get_instruction_relative(0, ...) which inspects the current instruction
instead of the caller; change the index to -1 so it inspects the previous
(calling) instruction, keep the same error mapping to BoltError::InvalidCaller,
and leave the require_eq check against World::id() intact so the function
enforces that it was invoked via CPI from the World program.
| let program_account_data = component_program_data.try_borrow_data()?; | ||
| let upgrade_authority = if let crate::prelude::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState::ProgramData { | ||
| upgrade_authority_address, | ||
| .. | ||
| } = | ||
| crate::prelude::bincode::deserialize(&program_account_data).map_err(|_| BoltError::InvalidAuthority)? | ||
| { | ||
| Ok(upgrade_authority_address) | ||
| } else { | ||
| Err(anchor_lang::error::Error::from(BoltError::InvalidAuthority)) | ||
| }?.ok_or(BoltError::InvalidAuthority)?; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the loader’s deserializer so trailing program bytes don’t break us.
component_program_data for an upgradeable program stores the serialized UpgradeableLoaderState::ProgramData header followed by the program bytes. bincode::deserialize(&program_account_data) insists on consuming the entire slice and returns TrailingData, so we always fall back to InvalidAuthority. Switch to UpgradeableLoaderState::deserialize(&mut &[u8]), which consumes just the header and tolerates the trailing payload.
- let program_account_data = component_program_data.try_borrow_data()?;
- let upgrade_authority = if let crate::prelude::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState::ProgramData {
- upgrade_authority_address,
- ..
- } =
- crate::prelude::bincode::deserialize(&program_account_data).map_err(|_| BoltError::InvalidAuthority)?
- {
- Ok(upgrade_authority_address)
- } else {
- Err(anchor_lang::error::Error::from(BoltError::InvalidAuthority))
- }?.ok_or(BoltError::InvalidAuthority)?;
+ let program_account_data = component_program_data.try_borrow_data()?;
+ let mut cursor: &[u8] = &program_account_data;
+ let upgrade_authority = if let crate::prelude::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState::ProgramData {
+ upgrade_authority_address,
+ ..
+ } =
+ crate::prelude::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState::deserialize(&mut cursor)
+ .map_err(|_| BoltError::InvalidAuthority)?
+ {
+ Ok(upgrade_authority_address)
+ } else {
+ Err(anchor_lang::error::Error::from(BoltError::InvalidAuthority))
+ }?.ok_or(BoltError::InvalidAuthority)?;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let program_account_data = component_program_data.try_borrow_data()?; | |
| let upgrade_authority = if let crate::prelude::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState::ProgramData { | |
| upgrade_authority_address, | |
| .. | |
| } = | |
| crate::prelude::bincode::deserialize(&program_account_data).map_err(|_| BoltError::InvalidAuthority)? | |
| { | |
| Ok(upgrade_authority_address) | |
| } else { | |
| Err(anchor_lang::error::Error::from(BoltError::InvalidAuthority)) | |
| }?.ok_or(BoltError::InvalidAuthority)?; | |
| let program_account_data = component_program_data.try_borrow_data()?; | |
| let mut cursor: &[u8] = &program_account_data; | |
| let upgrade_authority = if let crate::prelude::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState::ProgramData { | |
| upgrade_authority_address, | |
| .. | |
| } = | |
| crate::prelude::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState::deserialize(&mut cursor) | |
| .map_err(|_| BoltError::InvalidAuthority)? | |
| { | |
| Ok(upgrade_authority_address) | |
| } else { | |
| Err(anchor_lang::error::Error::from(BoltError::InvalidAuthority)) | |
| }?.ok_or(BoltError::InvalidAuthority)?; |
🤖 Prompt for AI Agents
In crates/bolt-lang/src/instructions/destroy.rs around lines 22 to 33, the code
uses bincode::deserialize(&program_account_data) which fails because the
upgradeable program account contains header plus trailing program bytes; replace
the bincode call with the loader's deserializer by calling
UpgradeableLoaderState::deserialize on a &mut &[u8] that points to
program_account_data so only the header is consumed and trailing bytes are
ignored, then extract upgrade_authority_address as before and propagate errors
to BoltError::InvalidAuthority if deserialization or option unwrapping fails.
| if component_authority == world::id_const() { | ||
| require!( | ||
| Clock::get()?.unix_timestamp < session_token.valid_until, | ||
| crate::session_keys::SessionError::InvalidToken | ||
| ); | ||
| } else { | ||
| let validity_ctx = crate::session_keys::ValidityChecker { | ||
| session_token: session_token.clone(), | ||
| session_signer: authority.clone(), | ||
| authority: component_authority, | ||
| target_program: world::id_const(), | ||
| }; | ||
| require!( | ||
| session_token.validate(validity_ctx)?, | ||
| crate::session_keys::SessionError::InvalidToken | ||
| ); | ||
| require_eq!( | ||
| component_authority, | ||
| session_token.authority, | ||
| crate::session_keys::SessionError::InvalidToken | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Session token is never validated when the world owns the component
In the component_authority == world::id_const() branch we only check valid_until and skip SessionToken::validate, so any signer can reuse any still‑valid session token minted by the world and mutate this component. We still need to bind the provided signer and token together, just like in the non‑world branch.
Please invoke session_token.validate (with the same ValidityChecker context) and enforce the signer/authority checks for the world-owned path before proceeding. Doing so restores the intended authorization semantics.
Consider applying this diff:
- if component_authority == world::id_const() {
- require!(
- Clock::get()?.unix_timestamp < session_token.valid_until,
- crate::session_keys::SessionError::InvalidToken
- );
- } else {
- let validity_ctx = crate::session_keys::ValidityChecker {
- session_token: session_token.clone(),
- session_signer: authority.clone(),
- authority: component_authority,
- target_program: world::id_const(),
- };
- require!(
- session_token.validate(validity_ctx)?,
- crate::session_keys::SessionError::InvalidToken
- );
- require_eq!(
- component_authority,
- session_token.authority,
- crate::session_keys::SessionError::InvalidToken
- );
- }
+ let validity_ctx = crate::session_keys::ValidityChecker {
+ session_token: session_token.clone(),
+ session_signer: authority.clone(),
+ authority: component_authority,
+ target_program: world::id_const(),
+ };
+ require!(
+ session_token.validate(validity_ctx)?,
+ crate::session_keys::SessionError::InvalidToken
+ );
+
+ if component_authority == world::id_const() {
+ require!(
+ Clock::get()?.unix_timestamp < session_token.valid_until,
+ crate::session_keys::SessionError::InvalidToken
+ );
+ } else {
+ require_eq!(
+ component_authority,
+ session_token.authority,
+ crate::session_keys::SessionError::InvalidToken
+ );
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if component_authority == world::id_const() { | |
| require!( | |
| Clock::get()?.unix_timestamp < session_token.valid_until, | |
| crate::session_keys::SessionError::InvalidToken | |
| ); | |
| } else { | |
| let validity_ctx = crate::session_keys::ValidityChecker { | |
| session_token: session_token.clone(), | |
| session_signer: authority.clone(), | |
| authority: component_authority, | |
| target_program: world::id_const(), | |
| }; | |
| require!( | |
| session_token.validate(validity_ctx)?, | |
| crate::session_keys::SessionError::InvalidToken | |
| ); | |
| require_eq!( | |
| component_authority, | |
| session_token.authority, | |
| crate::session_keys::SessionError::InvalidToken | |
| ); | |
| } | |
| let validity_ctx = crate::session_keys::ValidityChecker { | |
| session_token: session_token.clone(), | |
| session_signer: authority.clone(), | |
| authority: component_authority, | |
| target_program: world::id_const(), | |
| }; | |
| require!( | |
| session_token.validate(validity_ctx)?, | |
| crate::session_keys::SessionError::InvalidToken | |
| ); | |
| if component_authority == world::id_const() { | |
| require!( | |
| Clock::get()?.unix_timestamp < session_token.valid_until, | |
| crate::session_keys::SessionError::InvalidToken | |
| ); | |
| } else { | |
| require_eq!( | |
| component_authority, | |
| session_token.authority, | |
| crate::session_keys::SessionError::InvalidToken | |
| ); | |
| } |
🤖 Prompt for AI Agents
In crates/bolt-lang/src/instructions/update.rs around lines 41 to 62, the branch
for component_authority == world::id_const() only checks unix_timestamp and
skips SessionToken::validate, allowing any signer to reuse a still-valid
world‑minted token; fix by constructing the same
crate::session_keys::ValidityChecker (session_token.clone(), session_signer:
authority.clone(), authority: component_authority, target_program:
world::id_const()), call session_token.validate(validity_ctx)? and require its
result (crate::session_keys::SessionError::InvalidToken), and then
require_eq!(component_authority, session_token.authority,
crate::session_keys::SessionError::InvalidToken) so the provided signer and
token are bound before proceeding (you may keep the existing unix_timestamp
check in addition if desired).
| // CPI: invoke the instruction | ||
| invoke( | ||
| &ix, | ||
| &[ | ||
| ctx.accounts.payer.to_account_info(), | ||
| ctx.accounts.data.to_account_info(), | ||
| ctx.accounts.entity.to_account_info(), | ||
| ctx.accounts.authority.to_account_info(), | ||
| ctx.accounts.instruction_sysvar_account.to_account_info(), | ||
| ctx.accounts.system_program.to_account_info(), | ||
| ], | ||
| )?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Include the invoked program account in every CPI
Each manual invoke must receive the callee’s program AccountInfo. Here we omit ctx.accounts.component_program, so the runtime will fail the CPI (ProgramError::NotEnoughAccountKeys). The same issue repeats in destroy_component_with_discriminator, apply_impl, apply_with_session_impl, and system_execute.
invoke(
&ix,
&[
ctx.accounts.payer.to_account_info(),
ctx.accounts.data.to_account_info(),
ctx.accounts.entity.to_account_info(),
ctx.accounts.authority.to_account_info(),
ctx.accounts.instruction_sysvar_account.to_account_info(),
ctx.accounts.system_program.to_account_info(),
+ ctx.accounts.component_program.to_account_info(),
],
)?;Please append the corresponding program account for every CPI site (component destroy/update, session update, and the system execute path).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // CPI: invoke the instruction | |
| invoke( | |
| &ix, | |
| &[ | |
| ctx.accounts.payer.to_account_info(), | |
| ctx.accounts.data.to_account_info(), | |
| ctx.accounts.entity.to_account_info(), | |
| ctx.accounts.authority.to_account_info(), | |
| ctx.accounts.instruction_sysvar_account.to_account_info(), | |
| ctx.accounts.system_program.to_account_info(), | |
| ], | |
| )?; | |
| // CPI: invoke the instruction | |
| invoke( | |
| &ix, | |
| &[ | |
| ctx.accounts.payer.to_account_info(), | |
| ctx.accounts.data.to_account_info(), | |
| ctx.accounts.entity.to_account_info(), | |
| ctx.accounts.authority.to_account_info(), | |
| ctx.accounts.instruction_sysvar_account.to_account_info(), | |
| ctx.accounts.system_program.to_account_info(), | |
| ctx.accounts.component_program.to_account_info(), | |
| ], | |
| )?; |
🤖 Prompt for AI Agents
In crates/programs/world/src/lib.rs around lines 304 to 315, the CPI invoke call
omits the callee program AccountInfo which causes
ProgramError::NotEnoughAccountKeys; for this and the other CPI sites
(destroy_component_with_discriminator, apply_impl, apply_with_session_impl, and
system_execute) update each invoke call to include the program account (e.g.
ctx.accounts.component_program.to_account_info()) as the last entry in the
accounts slice so the runtime receives the callee program AccountInfo for every
CPI.
| pub const fn discriminator_for(name: &str) -> [u8; 8] { | ||
| let mut discriminator = [0u8; 8]; | ||
|
|
||
| let hash = const_crypto::sha2::Sha256::new() | ||
| .update(name.as_bytes()) | ||
| .finalize(); | ||
|
|
||
| let hash_bytes = hash.as_slice(); | ||
|
|
||
| // Manual loop required for const fn compatibility | ||
| let mut i = 0; | ||
| while i < 8 { | ||
| discriminator[i] = hash_bytes[i]; | ||
| i += 1; | ||
| } | ||
|
|
||
| discriminator | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
LGTM! Solid implementation with const fn compatibility.
The discriminator computation is correctly implemented with:
- Proper use of
const_crypto::sha2::Sha256for compile-time evaluation - Manual loop to maintain const fn compatibility
- Correct 8-byte extraction from the hash
Based on learnings, discriminator parameters should remain as Vec<u8> in client-facing APIs, but this internal utility returning [u8; 8] is appropriate for const evaluation.
Consider adding unit tests to verify the discriminator computation:
🏁 Script executed:
#!/bin/bash
# Check if tests exist for the discriminator_for function
rg -n "discriminator_for" --type rust -A5 -B5 | rg -i "test|#\[cfg\(test\)\]"Length of output: 0
🏁 Script executed:
#!/bin/bash
# More comprehensive search: look for all occurrences of discriminator_for in rust files
echo "=== All references to discriminator_for ==="
rg -n "discriminator_for" --type rust
echo ""
echo "=== Check for test modules in utils.rs ==="
fd utils.rs --type rust | xargs rg -n "#\[cfg\(test\)\]|mod tests" -A 20
echo ""
echo "=== Look for test files that might contain discriminator tests ==="
fd "test.*\.rs|.*_test\.rs" --type rust | head -20Length of output: 1291
🏁 Script executed:
#!/bin/bash
# Corrected search for test modules
echo "=== Search for test modules in utils.rs ==="
cat crates/programs/world/src/utils.rs | grep -n "mod tests\|#\[cfg(test)\]" -A 10
echo ""
echo "=== Check entire utils.rs structure ==="
cat crates/programs/world/src/utils.rs
echo ""
echo "=== Search for test files in crates/programs/world ==="
find crates/programs/world -name "*test*" -o -name "*tests*" 2>/dev/null
echo ""
echo "=== Check for mod tests anywhere in world crate ==="
rg "#\[cfg\(test\)\]|mod tests" crates/programs/world/Length of output: 1236
Solid implementation with proper const fn compatibility.
The discriminator computation is correctly implemented with:
- Proper use of
const_crypto::sha2::Sha256for compile-time evaluation - Manual loop to maintain const fn compatibility
- Correct 8-byte extraction from the hash
- Appropriate
[u8; 8]return type for internal const contexts (aligned with the learning that client-facing APIs should useVec<u8>)
No unit tests currently exist for this function. Consider adding tests to verify the discriminator computation produces consistent results and correctly handles edge cases.
Problem
New version of Bolt introduces a breaking change, we want to keep a program that is retro-compatible while introducing a new one with the breaking changes.
Solution
New program with the new features
Summary by CodeRabbit
New Features
Chores