Skip to content

feature: Update Tree Config Instruction - #156

Open
KartikSoneji wants to merge 9 commits into
metaplex-foundation:mainfrom
KartikSoneji:feature/update-tree
Open

feature: Update Tree Config Instruction#156
KartikSoneji wants to merge 9 commits into
metaplex-foundation:mainfrom
KartikSoneji:feature/update-tree

Conversation

@KartikSoneji

Copy link
Copy Markdown

Adds an updateTreeConfig instruction that allows updating:

  • creator
  • delegate
  • is decompressible
  • is public

in a single operation.

Additionally, this is the only instruction that allows updating the tree creator after creation.
Supports both V1 and V2 trees.

@vercel

vercel Bot commented Mar 21, 2026

Copy link
Copy Markdown

@KartikSoneji is attempting to deploy a commit to the Metaplex Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Add instruction to update Merkle tree configuration: change tree creator, tree delegate, public flag, and decompression state. V2 trees disallow certain decompression changes and return a clear unsupported-update error.
  • Tests

    • Add tests exercising configuration updates for V1 and V2 trees, including a negative test asserting failure for the forbidden V2 update.

Walkthrough

Adds a new on-chain instruction updateTreeConfig to Bubblegum that accepts optional updates for tree_creator, tree_delegate, is_public, and is_decompressible; validates tree schema version and rejects unsupported update operations (e.g., enabling decompressible on V2).

Changes

Cohort / File(s) Summary
IDL & Config
idls/bubblegum.json, configs/kinobi.cjs
Declared updateTreeConfig instruction and UpdateTreeConfigArgs type in IDL; added UnsupportedUpdateOperation error; registered instruction name in V1 instruction list.
Program API & Routing
programs/bubblegum/program/src/lib.rs, programs/bubblegum/program/src/error.rs
Added InstructionName::UpdateTreeConfig route and public update_tree_config entry; added UnsupportedUpdateOperation error variant.
Processor Implementation
programs/bubblegum/program/src/processor/mod.rs, programs/bubblegum/program/src/processor/update_tree_config.rs
New processor module and handler that loads TreeConfig PDA, enforces schema Version checks, rejects specific V2 updates, and conditionally writes provided fields.
State Types
programs/bubblegum/program/src/state/metaplex_adapter.rs
Introduced Anchor-serializable UpdateTreeConfigArgs struct with optional tree_creator, tree_delegate, is_public, and is_decompressible fields.
Client Tests
clients/js/test/updateTreeConfig.test.ts
New AVA tests for V1 and V2 trees: assert initial config, update individual fields via updateTreeConfig, re-fetch TreeConfig, and include negative test expecting UnsupportedUpdateOperation for disallowed V2 update.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client (off-chain)
    participant Program as Bubblegum Program
    participant TreeConfig as TreeConfig PDA (on-chain)
    participant Merkle as MerkleTree Account (on-chain)

    Client->>Program: send updateTreeConfig(UpdateTreeConfigArgs), signer=authority
    Program->>TreeConfig: derive & load PDA (check version & authority)
    Program->>Merkle: read merkle tree account (unchecked PDA reference)
    alt Version == V2 and is_decompressible == Enabled
        Program-->>Client: UnsupportedUpdateOperation error
    else Valid update
        Program->>TreeConfig: selectively update provided fields
        Program-->>Client: success / tx confirmed
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • danenbm
  • blockiosaurus
  • MarkSackerberg
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly describes the main change: adding a new updateTreeConfig instruction feature.
Description check ✅ Passed The description accurately explains what the instruction does and key capabilities, matching the changeset which adds updateTreeConfig functionality across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/js/test/updateTreeConfig.test.ts`:
- Around line 32-36: The test must explicitly assert that treeDelegate remains
unchanged when only rotating the creator: after calling fetchTreeConfigFromSeeds
and storing treeConfig, add an assertion comparing the previous treeDelegate to
the fetched one using string (base58) equality (use t.is rather than t.like) —
convert both treeDelegate values to strings if they are PublicKey objects; do
the same in the other similar assertion block (the one around the second check).
Reference fetchTreeConfigFromSeeds, treeConfig, treeCreator, treeDelegate, and
updateTreeConfig to locate and update the assertions.

In `@programs/bubblegum/program/src/processor/update_tree_config.rs`:
- Around line 19-20: The account constraint on authority in
update_tree_config.rs uses BubblegumError::TreeAuthorityIncorrect but this
instruction is creator-only; replace that error with a creator-specific variant
(e.g., BubblegumError::InvalidAuthority or add a new variant like
BubblegumError::TreeCreatorOnly) and update all references: add the new enum
variant to the BubblegumError definition (or reuse InvalidAuthority if already
appropriate) and change the attribute on the authority field (the
account(address = tree_authority.tree_creator @ ... ) constraint) to reference
the creator-specific error so delegate-signed calls produce the correct error
message.
- Around line 21-23: The merkle_tree account in the UpdateTreeConfig instruction
is marked mut but is only used to derive tree_authority and not read or mutated;
remove the mut annotation on the merkle_tree UncheckedAccount<'info> (the field
named merkle_tree) so it is declared without #[account(mut)] and update the
CHECK comment to reflect it is only used for PDA derivation (remove the
"modified in the downstream program" assertion); ensure any reference in the
handler or tests still derives tree_authority from merkle_tree and that client
account metadata will now mark merkle_tree as read-only.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dde55116-8c80-4258-9835-d08ba6de37a3

📥 Commits

Reviewing files that changed from the base of the PR and between b1296b0 and 43f7f6d.

⛔ Files ignored due to path filters (6)
  • clients/js/src/generated/errors/mplBubblegum.ts is excluded by !**/generated/**
  • clients/js/src/generated/instructions/index.ts is excluded by !**/generated/**
  • clients/js/src/generated/instructions/updateTreeConfig.ts is excluded by !**/generated/**
  • clients/rust/src/generated/errors/mpl_bubblegum.rs is excluded by !**/generated/**
  • clients/rust/src/generated/instructions/mod.rs is excluded by !**/generated/**
  • clients/rust/src/generated/instructions/update_tree_config.rs is excluded by !**/generated/**
📒 Files selected for processing (8)
  • clients/js/test/updateTreeConfig.test.ts
  • configs/kinobi.cjs
  • idls/bubblegum.json
  • programs/bubblegum/program/src/error.rs
  • programs/bubblegum/program/src/lib.rs
  • programs/bubblegum/program/src/processor/mod.rs
  • programs/bubblegum/program/src/processor/update_tree_config.rs
  • programs/bubblegum/program/src/state/metaplex_adapter.rs

Comment on lines +32 to +36
// Then the tree config account was updated accordingly.
treeConfig = await fetchTreeConfigFromSeeds(umi, { merkleTree });
t.like(treeConfig, <TreeConfig>{
treeCreator,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Assert that treeDelegate stays unchanged in the creator-only cases.

These assertions still pass if updateTreeConfig accidentally rewrites treeDelegate while rotating treeCreator. Since the contract here is partial updates, make the untouched field explicit.

Suggested assertion update
-  t.like(treeConfig, <TreeConfig>{
-    treeCreator,
-  });
+  t.is(treeConfig.treeCreator, treeCreator);
+  t.is(treeConfig.treeDelegate, umi.identity.publicKey);
...
-  t.like(treeConfig, <TreeConfig>{
-    treeCreator,
-  });
+  t.is(treeConfig.treeCreator, treeCreator);
+  t.is(treeConfig.treeDelegate, umi.identity.publicKey);

Based on learnings, in tests involving PublicKey values from the Umi framework, treat PublicKey as a string (base58) and use t.is() for equality checks.

Also applies to: 59-63

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/js/test/updateTreeConfig.test.ts` around lines 32 - 36, The test must
explicitly assert that treeDelegate remains unchanged when only rotating the
creator: after calling fetchTreeConfigFromSeeds and storing treeConfig, add an
assertion comparing the previous treeDelegate to the fetched one using string
(base58) equality (use t.is rather than t.like) — convert both treeDelegate
values to strings if they are PublicKey objects; do the same in the other
similar assertion block (the one around the second check). Reference
fetchTreeConfigFromSeeds, treeConfig, treeCreator, treeDelegate, and
updateTreeConfig to locate and update the assertions.

Comment on lines +19 to +20
#[account(address = tree_authority.tree_creator @ BubblegumError::TreeAuthorityIncorrect)]
pub authority: Signer<'info>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use a creator-specific auth error here.

A delegate-signed call will fail this address constraint but return TreeAuthorityIncorrect, whose message says the creator or delegate may sign. That is misleading for a creator-only instruction; use InvalidAuthority or add a dedicated creator-only error.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@programs/bubblegum/program/src/processor/update_tree_config.rs` around lines
19 - 20, The account constraint on authority in update_tree_config.rs uses
BubblegumError::TreeAuthorityIncorrect but this instruction is creator-only;
replace that error with a creator-specific variant (e.g.,
BubblegumError::InvalidAuthority or add a new variant like
BubblegumError::TreeCreatorOnly) and update all references: add the new enum
variant to the BubblegumError definition (or reuse InvalidAuthority if already
appropriate) and change the attribute on the authority field (the
account(address = tree_authority.tree_creator @ ... ) constraint) to reference
the creator-specific error so delegate-signed calls produce the correct error
message.

Comment thread programs/bubblegum/program/src/processor/update_tree_config.rs Outdated
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@programs/bubblegum/program/src/processor/update_tree_config.rs`:
- Line 54: The error message in update_tree_config.rs uses the wrong field name;
change the msg! invocation that currently says "is_decompressed must be None or
Disabled, got {:?}" to use the correct field name "is_decompressible" so it
reads "is_decompressible must be None or Disabled, got {:?}", updating the
string in the msg! call in the update_tree_config logic (referencing
UpdateTreeConfigArgs / TreeConfig and the msg! call that logs the
is_decompressible value).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f8bc9963-b3eb-4ff2-b9f6-b6cdfed84479

📥 Commits

Reviewing files that changed from the base of the PR and between 43f7f6d and 6c4c459.

📒 Files selected for processing (1)
  • programs/bubblegum/program/src/processor/update_tree_config.rs

Comment thread programs/bubblegum/program/src/processor/update_tree_config.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@programs/bubblegum/program/src/processor/update_tree_config.rs`:
- Around line 60-62: The code assigns tree_creator/tree_delegate directly into
tree_authority allowing Pubkey::default() (zero address); add explicit checks
before assignment to reject Pubkey::default() and return an error. In
update_tree_config (where tree_creator/tree_delegate are optional and assigned
to tree_authority), check if let Some(p) and if p != Pubkey::default() then
assign, otherwise return a clear Program error (e.g., InvalidArgument or a
Bubblegum-specific error) so zero-address values cannot be set.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6c6fe458-2ced-4890-b6bd-147ca0ef58cb

📥 Commits

Reviewing files that changed from the base of the PR and between 6c4c459 and fc7a229.

📒 Files selected for processing (1)
  • programs/bubblegum/program/src/processor/update_tree_config.rs

Comment on lines +60 to +62
if let Some(tree_creator) = tree_creator {
tree_authority.tree_creator = tree_creator;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for existing Pubkey::default() validation patterns in the codebase

# Check for existing patterns preventing zero pubkey assignment
rg -n 'Pubkey::default\(\)' --type rust -C 2

Repository: metaplex-foundation/mpl-bubblegum

Length of output: 59


🏁 Script executed:

cat -n programs/bubblegum/program/src/processor/update_tree_config.rs | head -80

Repository: metaplex-foundation/mpl-bubblegum

Length of output: 2880


🏁 Script executed:

# Search for validation patterns with Pubkey in the codebase
rg -n 'require!' --type rust -C 2 | head -100

Repository: metaplex-foundation/mpl-bubblegum

Length of output: 8311


🏁 Script executed:

# Check error definitions
fd -e rs -path "*/error*" | head -20

Repository: metaplex-foundation/mpl-bubblegum

Length of output: 245


🏁 Script executed:

fd -type f -name "*.rs" | xargs rg -l "BubblegumError" | head -5

Repository: metaplex-foundation/mpl-bubblegum

Length of output: 537


🏁 Script executed:

# Find and read error definitions
rg -A 2 "InvalidAuthority" --type rust

Repository: metaplex-foundation/mpl-bubblegum

Length of output: 2041


🏁 Script executed:

cat programs/bubblegum/program/src/error.rs | head -50

Repository: metaplex-foundation/mpl-bubblegum

Length of output: 1896


Consider validating non-zero tree_creator and tree_delegate.

Setting tree_creator or tree_delegate to Pubkey::default() (the zero address) creates an edge case where signature validation becomes unreliable. The zero address cannot sign transactions, and while the account constraint on line 19 would catch an invalid tree_creator, explicit validation prevents this footgun entirely.

🛡️ Optional validation to prevent zero-address assignment
     if let Some(tree_creator) = tree_creator {
+        require!(
+            tree_creator != Pubkey::default(),
+            BubblegumError::InvalidAuthority
+        );
         tree_authority.tree_creator = tree_creator;
     }

     if let Some(tree_delegate) = tree_delegate {
+        require!(
+            tree_delegate != Pubkey::default(),
+            BubblegumError::InvalidAuthority
+        );
         tree_authority.tree_delegate = tree_delegate;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@programs/bubblegum/program/src/processor/update_tree_config.rs` around lines
60 - 62, The code assigns tree_creator/tree_delegate directly into
tree_authority allowing Pubkey::default() (zero address); add explicit checks
before assignment to reject Pubkey::default() and return an error. In
update_tree_config (where tree_creator/tree_delegate are optional and assigned
to tree_authority), check if let Some(p) and if p != Pubkey::default() then
assign, otherwise return a clear Program error (e.g., InvalidArgument or a
Bubblegum-specific error) so zero-address values cannot be set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant