Skip to content

Commit 45fabba

Browse files
refactor: extract instruction handlers for memo-transfer, mint-close-authority, transfer-hook (account-data-as-seed, counter, hello-world)
Move instruction handlers into src/instructions/ directory following Anchor 1.0 conventions. Shared types and helpers remain in lib.rs.
1 parent cd809a5 commit 45fabba

21 files changed

Lines changed: 627 additions & 527 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use anchor_lang::prelude::*;
2+
use anchor_spl::token_interface::{memo_transfer_disable, MemoTransfer, Token2022, TokenAccount};
3+
4+
#[derive(Accounts)]
5+
pub struct Disable<'info> {
6+
#[account(mut)]
7+
pub owner: Signer<'info>,
8+
9+
#[account(
10+
mut,
11+
token::authority = owner,
12+
)]
13+
pub token_account: InterfaceAccount<'info, TokenAccount>,
14+
pub token_program: Program<'info, Token2022>,
15+
}
16+
17+
pub fn handler(context: Context<Disable>) -> Result<()> {
18+
memo_transfer_disable(CpiContext::new(
19+
context.accounts.token_program.key(),
20+
MemoTransfer {
21+
token_program_id: context.accounts.token_program.to_account_info(),
22+
account: context.accounts.token_account.to_account_info(),
23+
owner: context.accounts.owner.to_account_info(),
24+
},
25+
))?;
26+
Ok(())
27+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use anchor_lang::prelude::*;
2+
use anchor_lang::system_program::{create_account, CreateAccount};
3+
use anchor_spl::{
4+
token_2022::{
5+
initialize_account3,
6+
spl_token_2022::{extension::ExtensionType, pod::PodAccount},
7+
InitializeAccount3,
8+
},
9+
token_interface::{memo_transfer_initialize, MemoTransfer, Mint, Token2022},
10+
};
11+
12+
#[derive(Accounts)]
13+
pub struct Initialize<'info> {
14+
#[account(mut)]
15+
pub payer: Signer<'info>,
16+
17+
#[account(mut)]
18+
pub token_account: Signer<'info>,
19+
pub mint_account: InterfaceAccount<'info, Mint>,
20+
pub token_program: Program<'info, Token2022>,
21+
pub system_program: Program<'info, System>,
22+
}
23+
24+
pub fn handler(context: Context<Initialize>) -> Result<()> {
25+
// Calculate space required for token and extension data
26+
let token_account_size =
27+
ExtensionType::try_calculate_account_len::<PodAccount>(&[ExtensionType::MemoTransfer])?;
28+
29+
// Calculate minimum lamports required for size of token account with extensions
30+
let lamports = (Rent::get()?).minimum_balance(token_account_size);
31+
32+
// Invoke System Program to create new account with space for token account and extension data
33+
create_account(
34+
CpiContext::new(
35+
context.accounts.system_program.key(),
36+
CreateAccount {
37+
from: context.accounts.payer.to_account_info(),
38+
to: context.accounts.token_account.to_account_info(),
39+
},
40+
),
41+
lamports, // Lamports
42+
token_account_size as u64, // Space
43+
&context.accounts.token_program.key(), // Owner Program
44+
)?;
45+
46+
// Initialize the standard token account data
47+
initialize_account3(CpiContext::new(
48+
context.accounts.token_program.key(),
49+
InitializeAccount3 {
50+
account: context.accounts.token_account.to_account_info(),
51+
mint: context.accounts.mint_account.to_account_info(),
52+
authority: context.accounts.payer.to_account_info(),
53+
},
54+
))?;
55+
56+
// Initialize the memo transfer extension
57+
// This instruction must come after the token account initialization
58+
memo_transfer_initialize(CpiContext::new(
59+
context.accounts.token_program.key(),
60+
MemoTransfer {
61+
token_program_id: context.accounts.token_program.to_account_info(),
62+
account: context.accounts.token_account.to_account_info(),
63+
owner: context.accounts.payer.to_account_info(),
64+
},
65+
))?;
66+
Ok(())
67+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub mod disable;
2+
pub mod initialize;
3+
4+
pub use disable::*;
5+
pub use initialize::*;
Lines changed: 5 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
use anchor_lang::prelude::*;
2-
use anchor_lang::system_program::{create_account, CreateAccount};
3-
use anchor_spl::{
4-
token_2022::{
5-
initialize_account3,
6-
spl_token_2022::{extension::ExtensionType, pod::PodAccount},
7-
InitializeAccount3,
8-
},
9-
token_interface::{
10-
memo_transfer_disable, memo_transfer_initialize, MemoTransfer, Mint, Token2022,
11-
TokenAccount,
12-
},
13-
};
2+
3+
mod instructions;
4+
use instructions::*;
145

156
declare_id!("5BQyC7y2Pc283woThq11uZRqsgcRbBRLKz4yQ8BJadi2");
167

@@ -19,84 +10,10 @@ pub mod memo_transfer {
1910
use super::*;
2011

2112
pub fn initialize(context: Context<Initialize>) -> Result<()> {
22-
// Calculate space required for token and extension data
23-
let token_account_size =
24-
ExtensionType::try_calculate_account_len::<PodAccount>(&[ExtensionType::MemoTransfer])?;
25-
26-
// Calculate minimum lamports required for size of token account with extensions
27-
let lamports = (Rent::get()?).minimum_balance(token_account_size);
28-
29-
// Invoke System Program to create new account with space for token account and extension data
30-
create_account(
31-
CpiContext::new(
32-
context.accounts.system_program.key(),
33-
CreateAccount {
34-
from: context.accounts.payer.to_account_info(),
35-
to: context.accounts.token_account.to_account_info(),
36-
},
37-
),
38-
lamports, // Lamports
39-
token_account_size as u64, // Space
40-
&context.accounts.token_program.key(), // Owner Program
41-
)?;
42-
43-
// Initialize the standard token account data
44-
initialize_account3(CpiContext::new(
45-
context.accounts.token_program.key(),
46-
InitializeAccount3 {
47-
account: context.accounts.token_account.to_account_info(),
48-
mint: context.accounts.mint_account.to_account_info(),
49-
authority: context.accounts.payer.to_account_info(),
50-
},
51-
))?;
52-
53-
// Initialize the memo transfer extension
54-
// This instruction must come after the token account initialization
55-
memo_transfer_initialize(CpiContext::new(
56-
context.accounts.token_program.key(),
57-
MemoTransfer {
58-
token_program_id: context.accounts.token_program.to_account_info(),
59-
account: context.accounts.token_account.to_account_info(),
60-
owner: context.accounts.payer.to_account_info(),
61-
},
62-
))?;
63-
Ok(())
13+
instructions::initialize::handler(context)
6414
}
6515

6616
pub fn disable(context: Context<Disable>) -> Result<()> {
67-
memo_transfer_disable(CpiContext::new(
68-
context.accounts.token_program.key(),
69-
MemoTransfer {
70-
token_program_id: context.accounts.token_program.to_account_info(),
71-
account: context.accounts.token_account.to_account_info(),
72-
owner: context.accounts.owner.to_account_info(),
73-
},
74-
))?;
75-
Ok(())
17+
instructions::disable::handler(context)
7618
}
7719
}
78-
79-
#[derive(Accounts)]
80-
pub struct Initialize<'info> {
81-
#[account(mut)]
82-
pub payer: Signer<'info>,
83-
84-
#[account(mut)]
85-
pub token_account: Signer<'info>,
86-
pub mint_account: InterfaceAccount<'info, Mint>,
87-
pub token_program: Program<'info, Token2022>,
88-
pub system_program: Program<'info, System>,
89-
}
90-
91-
#[derive(Accounts)]
92-
pub struct Disable<'info> {
93-
#[account(mut)]
94-
pub owner: Signer<'info>,
95-
96-
#[account(
97-
mut,
98-
token::authority = owner,
99-
)]
100-
pub token_account: InterfaceAccount<'info, TokenAccount>,
101-
pub token_program: Program<'info, Token2022>,
102-
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use anchor_lang::prelude::*;
2+
use anchor_spl::{
3+
token_2022::{close_account, CloseAccount},
4+
token_interface::{Mint, Token2022},
5+
};
6+
7+
#[derive(Accounts)]
8+
pub struct Close<'info> {
9+
#[account(mut)]
10+
pub authority: Signer<'info>,
11+
12+
#[account(
13+
mut,
14+
extensions::close_authority::authority = authority,
15+
)]
16+
pub mint_account: InterfaceAccount<'info, Mint>,
17+
pub token_program: Program<'info, Token2022>,
18+
}
19+
20+
pub fn handler(context: Context<Close>) -> Result<()> {
21+
// cpi to token extensions programs to close mint account
22+
// alternatively, this can also be done in the client
23+
close_account(CpiContext::new(
24+
context.accounts.token_program.key(),
25+
CloseAccount {
26+
account: context.accounts.mint_account.to_account_info(),
27+
destination: context.accounts.authority.to_account_info(),
28+
authority: context.accounts.authority.to_account_info(),
29+
},
30+
))?;
31+
Ok(())
32+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use anchor_lang::prelude::*;
2+
use anchor_spl::token_interface::{
3+
spl_pod::optional_keys::OptionalNonZeroPubkey,
4+
spl_token_2022::{
5+
extension::{
6+
mint_close_authority::MintCloseAuthority, BaseStateWithExtensions,
7+
StateWithExtensions,
8+
},
9+
state::Mint as MintState,
10+
},
11+
Mint, Token2022,
12+
};
13+
14+
#[derive(Accounts)]
15+
pub struct Initialize<'info> {
16+
#[account(mut)]
17+
pub payer: Signer<'info>,
18+
19+
#[account(
20+
init,
21+
payer = payer,
22+
mint::decimals = 2,
23+
mint::authority = payer,
24+
extensions::close_authority::authority = payer,
25+
)]
26+
pub mint_account: InterfaceAccount<'info, Mint>,
27+
pub token_program: Program<'info, Token2022>,
28+
pub system_program: Program<'info, System>,
29+
}
30+
31+
pub fn handler(mut context: Context<Initialize>) -> Result<()> {
32+
handle_check_mint_data(&mut context.accounts)?;
33+
Ok(())
34+
}
35+
36+
// helper to check mint data, and demonstrate how to read mint extension data within a program
37+
fn handle_check_mint_data(accounts: &mut Initialize) -> Result<()> {
38+
let mint = &accounts.mint_account.to_account_info();
39+
let mint_data = mint.data.borrow();
40+
let mint_with_extension = StateWithExtensions::<MintState>::unpack(&mint_data)?;
41+
let extension_data = mint_with_extension.get_extension::<MintCloseAuthority>()?;
42+
43+
assert_eq!(
44+
extension_data.close_authority,
45+
OptionalNonZeroPubkey::try_from(Some(accounts.payer.key()))?
46+
);
47+
48+
msg!("{:?}", extension_data);
49+
Ok(())
50+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub mod close;
2+
pub mod initialize;
3+
4+
pub use close::*;
5+
pub use initialize::*;
Lines changed: 7 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,19 @@
11
use anchor_lang::prelude::*;
2-
use anchor_spl::{
3-
token_2022::{close_account, CloseAccount},
4-
token_interface::{
5-
spl_pod::optional_keys::OptionalNonZeroPubkey,
6-
spl_token_2022::{
7-
extension::{
8-
mint_close_authority::MintCloseAuthority, BaseStateWithExtensions,
9-
StateWithExtensions,
10-
},
11-
state::Mint as MintState,
12-
},
13-
Mint, Token2022,
14-
},
15-
};
2+
3+
mod instructions;
4+
use instructions::*;
5+
166
declare_id!("AcfQLsYKuzprcCNH1n96pKKgAbAnZchwpbr3gbVN742n");
177

188
#[program]
199
pub mod mint_close_authority {
2010
use super::*;
2111

22-
pub fn initialize(mut context: Context<Initialize>) -> Result<()> {
23-
handle_check_mint_data(&mut context.accounts)?;
24-
Ok(())
12+
pub fn initialize(context: Context<Initialize>) -> Result<()> {
13+
instructions::initialize::handler(context)
2514
}
2615

2716
pub fn close(context: Context<Close>) -> Result<()> {
28-
// cpi to token extensions programs to close mint account
29-
// alternatively, this can also be done in the client
30-
close_account(CpiContext::new(
31-
context.accounts.token_program.key(),
32-
CloseAccount {
33-
account: context.accounts.mint_account.to_account_info(),
34-
destination: context.accounts.authority.to_account_info(),
35-
authority: context.accounts.authority.to_account_info(),
36-
},
37-
))?;
38-
Ok(())
17+
instructions::close::handler(context)
3918
}
4019
}
41-
42-
#[derive(Accounts)]
43-
pub struct Initialize<'info> {
44-
#[account(mut)]
45-
pub payer: Signer<'info>,
46-
47-
#[account(
48-
init,
49-
payer = payer,
50-
mint::decimals = 2,
51-
mint::authority = payer,
52-
extensions::close_authority::authority = payer,
53-
)]
54-
pub mint_account: InterfaceAccount<'info, Mint>,
55-
pub token_program: Program<'info, Token2022>,
56-
pub system_program: Program<'info, System>,
57-
}
58-
59-
// helper to check mint data, and demonstrate how to read mint extension data within a program
60-
pub fn handle_check_mint_data(accounts: &mut Initialize) -> Result<()> {
61-
let mint = &accounts.mint_account.to_account_info();
62-
let mint_data = mint.data.borrow();
63-
let mint_with_extension = StateWithExtensions::<MintState>::unpack(&mint_data)?;
64-
let extension_data = mint_with_extension.get_extension::<MintCloseAuthority>()?;
65-
66-
assert_eq!(
67-
extension_data.close_authority,
68-
OptionalNonZeroPubkey::try_from(Some(accounts.payer.key()))?
69-
);
70-
71-
msg!("{:?}", extension_data);
72-
Ok(())
73-
}
74-
75-
76-
#[derive(Accounts)]
77-
pub struct Close<'info> {
78-
#[account(mut)]
79-
pub authority: Signer<'info>,
80-
81-
#[account(
82-
mut,
83-
extensions::close_authority::authority = authority,
84-
)]
85-
pub mint_account: InterfaceAccount<'info, Mint>,
86-
pub token_program: Program<'info, Token2022>,
87-
}

0 commit comments

Comments
 (0)