feat: add decompress_v2 to convert Bubblegum v2 leaves into MPL Core assets - #159
feat: add decompress_v2 to convert Bubblegum v2 leaves into MPL Core assets#159blockiosaurus wants to merge 2 commits into
Conversation
…assets Adds a `decompress_v2` instruction that lets the holder of a v2 compressed leaf turn it into a regular MPL Core asset that lives in the same MPL Core collection the leaf was associated with. Verifies the leaf via merkle proof, zeroes it in the tree (same path as burn_v2), then CPIs into mpl-core to decrement the compressed-asset counter and create the new asset. Requires the matching mpl-core change so the bubblegum cpi-signer PDA is authorized to create assets into a collection gated by the BubblegumV2 plugin. Without that change, the CreateV2 CPI will be rejected at runtime by mpl-core's collection authority check. https://claude.ai/code/session_01PpQaFSbCYHCqsTb2hwuBwq
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughAdds a new V2 decompression instruction to the Bubblegum program. The instruction includes a new identifier and program entrypoint in the main library file, alongside processor logic that validates ownership, handles V2-specific metadata, manages merkle tree operations, and coordinates with MPL Core for asset creation. Changes
Sequence Diagram(s)sequenceDiagram
actor Caller
participant BubblegumProgram as Bubblegum Program
participant MerkleTree as Merkle Tree
participant MPLCore as MPL Core
Caller->>BubblegumProgram: decompress_v2(root, nonce, index, metadata, ...)
BubblegumProgram->>BubblegumProgram: Validate caller is owner/delegate
BubblegumProgram->>BubblegumProgram: Verify V2 tree
BubblegumProgram->>BubblegumProgram: Validate metadata collection
BubblegumProgram->>BubblegumProgram: Hash/reconstruct V2 leaf
BubblegumProgram->>MerkleTree: Verify leaf proof & burn leaf
MerkleTree-->>BubblegumProgram: Leaf verified & removed
BubblegumProgram->>MPLCore: Verify collection has BubblegumV2 plugin
MPLCore-->>BubblegumProgram: Plugin verified
BubblegumProgram->>BubblegumProgram: Assemble royalty plugins
BubblegumProgram->>MPLCore: CPI to decrement collection counter
MPLCore-->>BubblegumProgram: Counter decremented
BubblegumProgram->>MPLCore: CPI to create asset with plugins
MPLCore-->>BubblegumProgram: Asset created
BubblegumProgram->>BubblegumProgram: Emit V2 leaf event
BubblegumProgram-->>Caller: Success
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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/decompress.rs`:
- Around line 425-457: The CreateV2CpiBuilder call in decompress.rs currently
sets owner but never sets a delegate, so any delegate on the compressed leaf is
dropped during materialization; either preserve the delegate by reading the
leaf's delegate and passing it into CreateV2CpiBuilder::delegate(...) (using the
same ctx.accounts/new_asset and any relevant leaf metadata), or if you intend to
drop delegations, add a clear note to the decompress instruction's docstring (in
the function that performs the materialization) stating that delegate
authorization on compressed assets is not preserved when creating the Core
asset; reference CreateV2CpiBuilder, .owner(...), and add or use .delegate(...)
or the docstring in the decompress function accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: af7c0262-29b0-4349-a9ed-548823a5a22d
📒 Files selected for processing (2)
programs/bubblegum/program/src/lib.rsprograms/bubblegum/program/src/processor/decompress.rs
| // Decrement the compressed-asset counter on the collection. The matching | ||
| // increment for the materialized Core asset happens inside CreateV2 itself. | ||
| UpdateCollectionInfoV1CpiBuilder::new(&ctx.accounts.mpl_core_program) | ||
| .collection(&ctx.accounts.core_collection) | ||
| .bubblegum_signer(&ctx.accounts.mpl_core_cpi_signer) | ||
| .update_type(UpdateType::Remove) | ||
| .amount(1) | ||
| .invoke_signed(&[&[ | ||
| MPL_CORE_CPI_SIGNER_PREFIX.as_bytes(), | ||
| &[ctx.bumps.mpl_core_cpi_signer], | ||
| ]])?; | ||
|
|
||
| // Materialize the Core asset inside the same collection. The bubblegum cpi | ||
| // signer is passed as the `authority` so collections gated by the | ||
| // BubblegumV2 plugin can authorize the create. NOTE: requires a matching | ||
| // mpl-core change that recognizes the bubblegum cpi signer for CreateV2 on | ||
| // a BubblegumV2-plugged collection (see PR description). | ||
| CreateV2CpiBuilder::new(&ctx.accounts.mpl_core_program) | ||
| .asset(&ctx.accounts.new_asset) | ||
| .collection(Some(&ctx.accounts.core_collection)) | ||
| .authority(Some(&ctx.accounts.mpl_core_cpi_signer)) | ||
| .payer(&ctx.accounts.payer) | ||
| .owner(Some(&ctx.accounts.leaf_owner)) | ||
| .system_program(&ctx.accounts.system_program) | ||
| .log_wrapper(Some(&ctx.accounts.log_wrapper)) | ||
| .data_state(DataState::AccountState) | ||
| .name(metadata.name.clone()) | ||
| .uri(metadata.uri.clone()) | ||
| .plugins(plugins) | ||
| .invoke_signed(&[&[ | ||
| MPL_CORE_CPI_SIGNER_PREFIX.as_bytes(), | ||
| &[ctx.bumps.mpl_core_cpi_signer], | ||
| ]])?; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Delegate not preserved on decompressed asset.
The CreateV2CpiBuilder sets owner but does not set a delegate. If a leaf had a delegate, that delegation is lost post-decompression. This appears intentional (delegate authorization was for the compressed asset, not the new Core asset), but consider documenting this behavior in the instruction's docstring.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@programs/bubblegum/program/src/processor/decompress.rs` around lines 425 -
457, The CreateV2CpiBuilder call in decompress.rs currently sets owner but never
sets a delegate, so any delegate on the compressed leaf is dropped during
materialization; either preserve the delegate by reading the leaf's delegate and
passing it into CreateV2CpiBuilder::delegate(...) (using the same
ctx.accounts/new_asset and any relevant leaf metadata), or if you intend to drop
delegations, add a clear note to the decompress instruction's docstring (in the
function that performs the materialization) stating that delegate authorization
on compressed assets is not preserved when creating the Core asset; reference
CreateV2CpiBuilder, .owner(...), and add or use .delegate(...) or the docstring
in the decompress function accordingly.
…ient and tests
Program changes:
* Carry the leaf delegate over to the new Core asset as TransferDelegate +
BurnDelegate plugins (a Bubblegum leaf delegate can do both).
* Carry the leaf-level frozen flag over as FreezeDelegate { frozen: true },
scoped to the leaf delegate so they retain thaw authority. Only reject
collection-permanent-frozen and non-transferable leaves (no clean Core
primitive for those).
JS:
* Add decompressV2 instruction client (mirrors the kinobi-generated style).
* Re-export from generated/instructions/index.
* Add a decompressV2 ava test suite covering:
- happy path (owner decompress, asset lands in same collection)
- leaf delegate as authority
- non-owner / non-delegate rejection
- collection mismatch / leaf without collection
- delegate preservation (TransferDelegate + BurnDelegate plugins)
- frozen state preservation (FreezeDelegate plugin)
- soulbound + collection-permanent-frozen rejection
- royalties / creator splits preservation
- v1 tree rejection
- tampered metadata, double-decompress
- payer / leafAuthority separation
https://claude.ai/code/session_01PpQaFSbCYHCqsTb2hwuBwq
Adds a
decompress_v2instruction that lets the holder of a v2 compressedleaf turn it into a regular MPL Core asset that lives in the same MPL Core
collection the leaf was associated with. Verifies the leaf via merkle proof,
zeroes it in the tree (same path as burn_v2), then CPIs into mpl-core to
decrement the compressed-asset counter and create the new asset.
Requires the matching mpl-core change so the bubblegum cpi-signer PDA is
authorized to create assets into a collection gated by the BubblegumV2
plugin. Without that change, the CreateV2 CPI will be rejected at runtime
by mpl-core's collection authority check.
https://claude.ai/code/session_01PpQaFSbCYHCqsTb2hwuBwq