Skip to content

Commit cd809a5

Browse files
refactor: extract instruction handlers for cnft-vault, external-delegate, token-2022 basics/default-account-state/interest-bearing
Move instruction handlers into src/instructions/ directory following Anchor 1.0 conventions. Shared types and helpers remain in lib.rs.
1 parent 0245f42 commit cd809a5

25 files changed

Lines changed: 880 additions & 673 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub mod withdraw_cnft;
2+
pub mod withdraw_two_cnfts;
3+
4+
pub use withdraw_cnft::*;
5+
pub use withdraw_two_cnfts::*;
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use anchor_lang::prelude::*;
2+
use anchor_lang::solana_program::{
3+
instruction::AccountMeta,
4+
program::invoke_signed,
5+
};
6+
7+
use crate::{build_transfer_instruction, TransferArgs, SPLCompression};
8+
9+
#[derive(Accounts)]
10+
pub struct Withdraw<'info> {
11+
#[account(mut)]
12+
#[account(
13+
seeds = [merkle_tree.key().as_ref()],
14+
bump,
15+
seeds::program = bubblegum_program.key()
16+
)]
17+
/// CHECK: This account is modified in the downstream program
18+
pub tree_authority: UncheckedAccount<'info>,
19+
#[account(
20+
seeds = [b"cNFT-vault"],
21+
bump,
22+
)]
23+
/// CHECK: This account doesnt even exist (it is just the pda to sign)
24+
pub leaf_owner: UncheckedAccount<'info>,
25+
/// CHECK: This account is neither written to nor read from.
26+
pub new_leaf_owner: UncheckedAccount<'info>,
27+
#[account(mut)]
28+
/// CHECK: This account is modified in the downstream program
29+
pub merkle_tree: UncheckedAccount<'info>,
30+
/// CHECK: This account is neither written to nor read from.
31+
pub log_wrapper: UncheckedAccount<'info>,
32+
pub compression_program: Program<'info, SPLCompression>,
33+
/// CHECK: This account is neither written to nor read from.
34+
pub bubblegum_program: UncheckedAccount<'info>,
35+
pub system_program: Program<'info, System>,
36+
}
37+
38+
pub fn handler<'info>(
39+
context: Context<'info, Withdraw<'info>>,
40+
root: [u8; 32],
41+
data_hash: [u8; 32],
42+
creator_hash: [u8; 32],
43+
nonce: u64,
44+
index: u32,
45+
) -> Result<()> {
46+
msg!(
47+
"attempting to send nft {} from tree {}",
48+
index,
49+
context.accounts.merkle_tree.key()
50+
);
51+
52+
let proof_metas: Vec<AccountMeta> = context
53+
.remaining_accounts
54+
.iter()
55+
.map(|acc| AccountMeta::new_readonly(acc.key(), false))
56+
.collect();
57+
58+
let instruction = build_transfer_instruction(
59+
context.accounts.tree_authority.key(),
60+
context.accounts.leaf_owner.key(),
61+
context.accounts.leaf_owner.key(),
62+
context.accounts.new_leaf_owner.key(),
63+
context.accounts.merkle_tree.key(),
64+
context.accounts.log_wrapper.key(),
65+
context.accounts.compression_program.key(),
66+
context.accounts.system_program.key(),
67+
&proof_metas,
68+
TransferArgs {
69+
root,
70+
data_hash,
71+
creator_hash,
72+
nonce,
73+
index,
74+
},
75+
)?;
76+
77+
// Gather all account infos for the CPI
78+
let mut account_infos = vec![
79+
context.accounts.bubblegum_program.to_account_info(),
80+
context.accounts.tree_authority.to_account_info(),
81+
context.accounts.leaf_owner.to_account_info(),
82+
context.accounts.new_leaf_owner.to_account_info(),
83+
context.accounts.merkle_tree.to_account_info(),
84+
context.accounts.log_wrapper.to_account_info(),
85+
context.accounts.compression_program.to_account_info(),
86+
context.accounts.system_program.to_account_info(),
87+
];
88+
for acc in context.remaining_accounts.iter() {
89+
account_infos.push(acc.to_account_info());
90+
}
91+
92+
invoke_signed(
93+
&instruction,
94+
&account_infos,
95+
&[&[b"cNFT-vault", &[context.bumps.leaf_owner]]],
96+
)?;
97+
98+
Ok(())
99+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
use anchor_lang::prelude::*;
2+
use anchor_lang::solana_program::{
3+
instruction::AccountMeta,
4+
program::invoke_signed,
5+
};
6+
7+
use crate::{build_transfer_instruction, TransferArgs, SPLCompression};
8+
9+
#[derive(Accounts)]
10+
pub struct WithdrawTwo<'info> {
11+
#[account(mut)]
12+
#[account(
13+
seeds = [merkle_tree1.key().as_ref()],
14+
bump,
15+
seeds::program = bubblegum_program.key()
16+
)]
17+
/// CHECK: This account is modified in the downstream program
18+
pub tree_authority1: UncheckedAccount<'info>,
19+
#[account(
20+
seeds = [b"cNFT-vault"],
21+
bump,
22+
)]
23+
/// CHECK: This account doesnt even exist (it is just the pda to sign)
24+
pub leaf_owner: UncheckedAccount<'info>,
25+
/// CHECK: This account is neither written to nor read from.
26+
pub new_leaf_owner1: UncheckedAccount<'info>,
27+
#[account(mut)]
28+
/// CHECK: This account is modified in the downstream program
29+
pub merkle_tree1: UncheckedAccount<'info>,
30+
31+
#[account(mut)]
32+
#[account(
33+
seeds = [merkle_tree2.key().as_ref()],
34+
bump,
35+
seeds::program = bubblegum_program.key()
36+
)]
37+
/// CHECK: This account is modified in the downstream program
38+
pub tree_authority2: UncheckedAccount<'info>,
39+
/// CHECK: This account is neither written to nor read from.
40+
pub new_leaf_owner2: UncheckedAccount<'info>,
41+
#[account(mut)]
42+
/// CHECK: This account is modified in the downstream program
43+
pub merkle_tree2: UncheckedAccount<'info>,
44+
45+
/// CHECK: This account is neither written to nor read from.
46+
pub log_wrapper: UncheckedAccount<'info>,
47+
pub compression_program: Program<'info, SPLCompression>,
48+
/// CHECK: This account is neither written to nor read from.
49+
pub bubblegum_program: UncheckedAccount<'info>,
50+
pub system_program: Program<'info, System>,
51+
}
52+
53+
#[allow(clippy::too_many_arguments)]
54+
pub fn handler<'info>(
55+
context: Context<'info, WithdrawTwo<'info>>,
56+
root1: [u8; 32],
57+
data_hash1: [u8; 32],
58+
creator_hash1: [u8; 32],
59+
nonce1: u64,
60+
index1: u32,
61+
proof_1_length: u8,
62+
root2: [u8; 32],
63+
data_hash2: [u8; 32],
64+
creator_hash2: [u8; 32],
65+
nonce2: u64,
66+
index2: u32,
67+
_proof_2_length: u8,
68+
) -> Result<()> {
69+
let merkle_tree1 = context.accounts.merkle_tree1.key();
70+
let merkle_tree2 = context.accounts.merkle_tree2.key();
71+
msg!(
72+
"attempting to send nfts from trees {} and {}",
73+
merkle_tree1,
74+
merkle_tree2
75+
);
76+
77+
let signer_seeds: &[&[u8]] = &[b"cNFT-vault", &[context.bumps.leaf_owner]];
78+
79+
// Split remaining accounts into proof1 and proof2
80+
let (proof1_accounts, proof2_accounts) =
81+
context.remaining_accounts.split_at(proof_1_length as usize);
82+
83+
let proof1_metas: Vec<AccountMeta> = proof1_accounts
84+
.iter()
85+
.map(|acc| AccountMeta::new_readonly(acc.key(), false))
86+
.collect();
87+
88+
let proof2_metas: Vec<AccountMeta> = proof2_accounts
89+
.iter()
90+
.map(|acc| AccountMeta::new_readonly(acc.key(), false))
91+
.collect();
92+
93+
// Withdraw cNFT#1
94+
msg!("withdrawing cNFT#1");
95+
let instruction1 = build_transfer_instruction(
96+
context.accounts.tree_authority1.key(),
97+
context.accounts.leaf_owner.key(),
98+
context.accounts.leaf_owner.key(),
99+
context.accounts.new_leaf_owner1.key(),
100+
context.accounts.merkle_tree1.key(),
101+
context.accounts.log_wrapper.key(),
102+
context.accounts.compression_program.key(),
103+
context.accounts.system_program.key(),
104+
&proof1_metas,
105+
TransferArgs {
106+
root: root1,
107+
data_hash: data_hash1,
108+
creator_hash: creator_hash1,
109+
nonce: nonce1,
110+
index: index1,
111+
},
112+
)?;
113+
114+
let mut account_infos1 = vec![
115+
context.accounts.bubblegum_program.to_account_info(),
116+
context.accounts.tree_authority1.to_account_info(),
117+
context.accounts.leaf_owner.to_account_info(),
118+
context.accounts.new_leaf_owner1.to_account_info(),
119+
context.accounts.merkle_tree1.to_account_info(),
120+
context.accounts.log_wrapper.to_account_info(),
121+
context.accounts.compression_program.to_account_info(),
122+
context.accounts.system_program.to_account_info(),
123+
];
124+
for acc in proof1_accounts.iter() {
125+
account_infos1.push(acc.to_account_info());
126+
}
127+
128+
invoke_signed(&instruction1, &account_infos1, &[signer_seeds])?;
129+
130+
// Withdraw cNFT#2
131+
msg!("withdrawing cNFT#2");
132+
let instruction2 = build_transfer_instruction(
133+
context.accounts.tree_authority2.key(),
134+
context.accounts.leaf_owner.key(),
135+
context.accounts.leaf_owner.key(),
136+
context.accounts.new_leaf_owner2.key(),
137+
context.accounts.merkle_tree2.key(),
138+
context.accounts.log_wrapper.key(),
139+
context.accounts.compression_program.key(),
140+
context.accounts.system_program.key(),
141+
&proof2_metas,
142+
TransferArgs {
143+
root: root2,
144+
data_hash: data_hash2,
145+
creator_hash: creator_hash2,
146+
nonce: nonce2,
147+
index: index2,
148+
},
149+
)?;
150+
151+
let mut account_infos2 = vec![
152+
context.accounts.bubblegum_program.to_account_info(),
153+
context.accounts.tree_authority2.to_account_info(),
154+
context.accounts.leaf_owner.to_account_info(),
155+
context.accounts.new_leaf_owner2.to_account_info(),
156+
context.accounts.merkle_tree2.to_account_info(),
157+
context.accounts.log_wrapper.to_account_info(),
158+
context.accounts.compression_program.to_account_info(),
159+
context.accounts.system_program.to_account_info(),
160+
];
161+
for acc in proof2_accounts.iter() {
162+
account_infos2.push(acc.to_account_info());
163+
}
164+
165+
invoke_signed(&instruction2, &account_infos2, &[signer_seeds])?;
166+
167+
msg!("successfully sent cNFTs");
168+
Ok(())
169+
}

0 commit comments

Comments
 (0)