-
Notifications
You must be signed in to change notification settings - Fork 69
Implement close tree instruction to program and expose in SDKs #153
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: main
Are you sure you want to change the base?
Implement close tree instruction to program and expose in SDKs #153
Conversation
…+ rust SDK. Generated clients through Kinobi.
|
@imetandy is attempting to deploy a commit to the Metaplex Foundation Team on Vercel. A member of the Team first needs to authorize it. |
Summary by CodeRabbitRelease Notes
WalkthroughA new CloseTree instruction is added to the Bubblegum program to close an empty Merkle tree and its config account PDA, reclaiming rent. The implementation includes account validation, schema version checks, authority verification, and a CPI call to mpl_account_compression. Changes
Sequence DiagramsequenceDiagram
participant User
participant Bubblegum
participant Processor as Processor: close_tree
participant MplCompression as MplAccountCompression
participant System
User->>Bubblegum: close_tree instruction
Bubblegum->>Processor: dispatch CloseTree context
rect rgb(200, 220, 255)
Note over Processor: Validations
Processor->>Processor: Verify schema version = V2
Processor->>Processor: Check authority is tree_creator or tree_delegate
Processor->>Processor: Check recipient is tree_creator or tree_delegate
end
rect rgb(220, 255, 220)
Note over Processor: Close empty tree via CPI
Processor->>MplCompression: cpi::close_empty_tree<br/>(tree_authority as signer)
MplCompression->>System: close merkle_tree account
end
rect rgb(255, 240, 220)
Note over Processor: Reclaim rent
Processor->>System: transfer TreeConfig lamports to recipient
Processor->>System: close tree_authority PDA
end
Processor-->>User: Result<()>
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. 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: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (4)
clients/js/src/generated/instructions/closeTree.tsis excluded by!**/generated/**clients/js/src/generated/instructions/index.tsis excluded by!**/generated/**clients/rust/src/generated/instructions/close_tree.rsis excluded by!**/generated/**clients/rust/src/generated/instructions/mod.rsis excluded by!**/generated/**
📒 Files selected for processing (4)
idls/bubblegum.json(2 hunks)programs/bubblegum/program/src/lib.rs(3 hunks)programs/bubblegum/program/src/processor/close_tree.rs(1 hunks)programs/bubblegum/program/src/processor/mod.rs(2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: danenbm
Repo: metaplex-foundation/mpl-bubblegum PR: 134
File: clients/js/src/createTree.ts:85-88
Timestamp: 2025-05-07T21:05:10.245Z
Learning: For Bubblegum V2 trees, custom compression program IDs are not supported by design. The createTreeV2 function intentionally uses only the default mplAccountCompression program ID, unlike createTree which allows custom compression program IDs.
📚 Learning: 2025-05-29T09:26:11.104Z
Learnt from: danenbm
Repo: metaplex-foundation/mpl-bubblegum PR: 125
File: clients/rust/tests/setup/tree_manager.rs:102-110
Timestamp: 2025-05-29T09:26:11.104Z
Learning: In mpl-bubblegum Rust crate, mpl_account_compression::ID can be used without explicitly importing mpl_account_compression, likely because it's re-exported or available through other imports in the codebase.
Applied to files:
programs/bubblegum/program/src/processor/mod.rs
📚 Learning: 2025-05-07T21:05:10.245Z
Learnt from: danenbm
Repo: metaplex-foundation/mpl-bubblegum PR: 134
File: clients/js/src/createTree.ts:85-88
Timestamp: 2025-05-07T21:05:10.245Z
Learning: For Bubblegum V2 trees, custom compression program IDs are not supported by design. The createTreeV2 function intentionally uses only the default mplAccountCompression program ID, unlike createTree which allows custom compression program IDs.
Applied to files:
idls/bubblegum.jsonprograms/bubblegum/program/src/processor/close_tree.rs
🧬 Code graph analysis (3)
programs/bubblegum/program/src/processor/mod.rs (2)
programs/bubblegum/program/src/lib.rs (1)
close_tree(640-642)programs/bubblegum/program/src/processor/close_tree.rs (1)
close_tree(31-77)
programs/bubblegum/program/src/processor/close_tree.rs (2)
clients/rust/src/generated/instructions/close_tree.rs (6)
merkle_tree(120-123)merkle_tree(378-384)authority(115-118)authority(370-376)recipient(127-130)recipient(388-394)programs/bubblegum/program/src/lib.rs (1)
close_tree(640-642)
programs/bubblegum/program/src/lib.rs (1)
programs/bubblegum/program/src/processor/close_tree.rs (1)
close_tree(31-77)
🔇 Additional comments (6)
idls/bubblegum.json (2)
3387-3437: IDL accounts forcloseTreematch the on-chain account structThe account list (treeAuthority, authority, merkleTree, recipient, compressionProgram, logWrapper, systemProgram) and their mut/signer flags line up with
CloseTree<'info>in Rust and the documented behavior (authority + creator/delegate recipient). I don’t see any schema issues here; the IDL looks consistent with the program implementation.
4133-4136:InstructionName::CloseTreeenum variant is correctly addedAdding
CloseTreeat the end of theInstructionNameenum keeps existing discriminants stable and aligns with the new instruction in the IDL andget_instruction_typewiring elsewhere.programs/bubblegum/program/src/processor/mod.rs (1)
31-31: Module wiring forclose_treeis correctThe new
mod close_tree;declaration is paired withpub(crate) use close_tree::*;, making the CloseTree context and handler available to the crate without altering existing behavior.programs/bubblegum/program/src/lib.rs (2)
22-60: Enum variant addition forCloseTreekeeps InstructionName stableAppending
CloseTreetoInstructionNamepreserves all existing variant ordinals and cleanly extends the internal instruction taxonomy to cover the new close-tree operation.
62-106: Ensure theCloseTreediscriminator matches the generated Anchor IDThe new match arm mapping
[9, 124, 164, 131, 238, 218, 148, 212]toInstructionName::CloseTreelooks fine, but it’s critical that these bytes are exactly the Anchor-generated discriminator for the newclose_treeinstruction so tools and off-chain decoders stay in sync.You can confirm this by regenerating the IDL/clients and comparing the discriminator, or by running a small script that computes the Anchor discriminator for
global:close_treeand checking it equals[9, 124, 164, 131, 238, 218, 148, 212].programs/bubblegum/program/src/processor/close_tree.rs (1)
31-77:close_treelogic correctly gates by version and authority and safely chains CPIsThe core flow looks solid:
- Restricting to
Version::V2trees ensures this only applies to V2 configs (and, by design, V2 trees that were created with the canonical compression program).- Requiring the signer
authorityto be eithertree_creatorortree_delegate, and separately requiringrecipientto be one of those two, prevents a delegate from redirecting rent to arbitrary accounts while still allowing creator/delegate to choose where reclaimed lamports go.- Using the TreeConfig PDA as the signer for
mpl_account_compression::cpi::close_empty_treevia seeds[merkle_tree.key().as_ref(), bump]matches the account derivation on the Bubblegum side and ensures only the correct authority PDA can close the Merkle tree.- The order “CPI to close_empty_tree, then close tree_authority” guarantees the config PDA is only closed after the underlying tree is successfully closed, so failures leave state intact.
The only thing I’d suggest double-checking is that these seeds exactly match the seeds used when creating
TreeConfigincreate_tree_v2, so the PDA signer derivation can never succeed for a mismatched (tree, config) pair.You can verify the seed consistency by inspecting the
TreeConfigPDA derivation increate_tree_v2and ensuring it also uses[merkle_tree.key().as_ref()]as seeds with the same bump.
| /// Closes an empty tree and its config PDA to reclaim rent. | ||
| pub fn close_tree(ctx: Context<CloseTree>) -> Result<()> { | ||
| processor::close_tree(ctx) | ||
| } |
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
close_tree RPC wiring is correct; consider clarifying V2-only behavior in docs
The new close_tree(ctx: Context<CloseTree>) entrypoint correctly delegates to processor::close_tree and matches the IDL. Since the processor enforces Version::V2, you might call out “V2 trees only” explicitly in the doc comment to reduce surprises for integrators trying this on legacy trees.
🤖 Prompt for AI Agents
programs/bubblegum/program/src/lib.rs lines 639-642: the doc comment for
close_tree says "Closes an empty tree and its config PDA to reclaim rent." but
doesn't state this operation is V2-only; update the doc comment to explicitly
state that this RPC only works for Version::V2 trees (processor enforces
Version::V2) so integrators aren’t surprised when calling it on legacy trees,
and optionally add a short note about expected failure behavior for non-V2
trees.
| #[derive(Accounts)] | ||
| pub struct CloseTree<'info> { | ||
| #[account( | ||
| mut, | ||
| seeds = [merkle_tree.key().as_ref()], | ||
| bump, | ||
| )] | ||
| pub tree_authority: Account<'info, TreeConfig>, | ||
| /// Tree creator or delegate. | ||
| pub authority: Signer<'info>, | ||
| /// CHECK: This account is modified in the downstream program. | ||
| #[account(mut, owner = mpl_account_compression::ID)] | ||
| pub merkle_tree: UncheckedAccount<'info>, | ||
| /// Recipient for reclaimed lamports (tree + config PDA). Must be the creator | ||
| /// or the delegate. | ||
| /// CHECK: This account is validated in the instruction. | ||
| #[account(mut)] | ||
| pub recipient: UncheckedAccount<'info>, | ||
| pub compression_program: Program<'info, MplAccountCompression>, | ||
| pub log_wrapper: Program<'info, MplNoop>, | ||
| pub system_program: Program<'info, System>, | ||
| } |
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
Accounts definition for CloseTree is consistent and safe, with minor hardening opportunities
The CloseTree<'info> accounts look well-structured:
tree_authorityas a PDA overmerkle_tree.key().as_ref()withbumpmatches the usual TreeConfig derivation pattern.merkle_treeis constrained toowner = mpl_account_compression::ID, ensuring only genuine compression trees can be targeted.recipientis kept unchecked but is constrained later in the handler to be creator or delegate, which is appropriate for lamport reclamation.compression_program,log_wrapperandsystem_programare correctly typed to the expected programs.
If you want to align fully with other Bubblegum instructions and tighten invariants further, you could also add explicit runtime checks that:
compression_program.key() == mpl_account_compression::ID(using the existingInvalidCompressionProgramerror), andlog_wrappermatches the canonical noop program (usingInvalidLogWrapper),
even though the types already strongly suggest the intended IDs.
Based on learnings
🤖 Prompt for AI Agents
In programs/bubblegum/program/src/processor/close_tree.rs around lines 8 to 29,
add explicit runtime checks in the instruction handler to validate that
compression_program.key() == mpl_account_compression::ID and log_wrapper.key()
== mpl_noop::ID (or the canonical noop program ID used elsewhere), returning the
existing InvalidCompressionProgram and InvalidLogWrapper errors respectively;
place these checks early in the handler before any CPI or downstream calls so
the instruction fails fast if the provided program IDs are incorrect.
Key Changes
close_treeinstruction to close the merkle tree and reclaim the rent. We also handle closing the config account.Potential Improvements
Motivation
Testing