Skip to content

Commit 9b6972f

Browse files
mikemaccanaclaude
andcommitted
Fix Quasar token-2022 extension opcodes, sizes, and instruction formats
- immutable-owner: opcode 34→22 (InitializeImmutableOwner), size 301→170 - non-transferable: opcode 35→32 (InitializeNonTransferableMint) - permanent-delegate: opcode 38→35 (InitializePermanentDelegate), size 218→202 - transfer-fee: add missing sub-instruction byte 0 + COption flags in InitializeTransferFeeConfig (75→78 byte instruction) - metadata: fix mint_size formula missing AccountType byte (82+82+1 → 165+1) - mint-close-authority: add COption::Some flag before pubkey in InitializeMintCloseAuthority (33→34 byte instruction) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ed20d28 commit 9b6972f

6 files changed

Lines changed: 29 additions & 25 deletions

File tree

  • tokens/token-2022
    • immutable-owner/quasar/src
    • metadata/quasar/src
    • mint-close-authority/quasar/src
    • non-transferable/quasar/src
    • permanent-delegate/quasar/src
    • transfer-fee/quasar/src

tokens/token-2022/immutable-owner/quasar/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ pub struct Initialize<'info> {
4444

4545
#[inline(always)]
4646
pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> {
47-
// Token account + ImmutableOwner extension = 301 bytes
48-
let account_size: u64 = 301;
47+
// 165 (base) + 1 (account type) + 4 (TLV header, ImmutableOwner is zero-size) = 170 bytes
48+
let account_size: u64 = 170;
4949
let lamports = Rent::get()?.try_minimum_balance(account_size as usize)?;
5050

5151
// 1. Create account
@@ -59,14 +59,14 @@ pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> {
5959
)
6060
.invoke()?;
6161

62-
// 2. Initialize ImmutableOwner extension: opcode 34
62+
// 2. Initialize ImmutableOwner extension: opcode 22 (no additional data)
6363
CpiCall::new(
6464
accounts.token_program.to_account_view().address(),
6565
[InstructionAccount::writable(
6666
accounts.token_account.to_account_view().address(),
6767
)],
6868
[accounts.token_account.to_account_view()],
69-
[34u8],
69+
[22u8],
7070
)
7171
.invoke()?;
7272

tokens/token-2022/metadata/quasar/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ pub fn handle_initialize(
8484
// + 4 + name.len + 4 + symbol.len + 4 + uri.len + 4 + 0 (additional metadata)
8585
let metadata_data_len = 32 + 32 + 4 + name.len() + 4 + symbol.len() + 4 + uri.len() + 4;
8686
let total_ext_data = 4 + metadata_data_len; // TLV: 2 type + 2 length + data
87-
// Mint base (82) + padding (82) + AccountType (1) + MetadataPointer ext (68) + metadata TLV
88-
let mint_size = 82 + 82 + 1 + 68 + total_ext_data;
87+
// 165 (base with padding) + 1 (AccountType) + 68 (MetadataPointer TLV) + metadata TLV
88+
let mint_size = 165 + 1 + 68 + total_ext_data;
8989
let lamports = Rent::get()?.try_minimum_balance(mint_size)?;
9090

9191
accounts.system_program

tokens/token-2022/mint-close-authority/quasar/src/lib.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,11 @@ pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> {
6464
)
6565
.invoke()?;
6666

67-
// InitializeMintCloseAuthority: opcode 25, close_authority pubkey
68-
let mut ext_data = [0u8; 33];
69-
ext_data[0] = 25;
70-
ext_data[1..33].copy_from_slice(accounts.payer.to_account_view().address().as_ref());
67+
// InitializeMintCloseAuthority: opcode 25, COption::Some flag (1 byte), close_authority pubkey (32 bytes)
68+
let mut ext_data = [0u8; 34];
69+
ext_data[0] = 25; // InitializeMintCloseAuthority
70+
ext_data[1] = 1; // COption::Some
71+
ext_data[2..34].copy_from_slice(accounts.payer.to_account_view().address().as_ref());
7172

7273
CpiCall::new(
7374
accounts.token_program.to_account_view().address(),

tokens/token-2022/non-transferable/quasar/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> {
5858
)
5959
.invoke()?;
6060

61-
// 2. Initialize NonTransferable extension: opcode 35
61+
// 2. Initialize NonTransferable extension: opcode 32 (InitializeNonTransferableMint, no data)
6262
CpiCall::new(
6363
accounts.token_program.to_account_view().address(),
6464
[InstructionAccount::writable(
6565
accounts.mint_account.to_account_view().address(),
6666
)],
6767
[accounts.mint_account.to_account_view()],
68-
[35u8],
68+
[32u8],
6969
)
7070
.invoke()?;
7171

tokens/token-2022/permanent-delegate/quasar/src/lib.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ pub struct Initialize<'info> {
4343

4444
#[inline(always)]
4545
pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> {
46-
// Mint + PermanentDelegate extension = 218 bytes
47-
let mint_size: u64 = 218;
46+
// 165 (base) + 1 (account type) + 4 (TLV header) + 32 (PermanentDelegate data) = 202 bytes
47+
let mint_size: u64 = 202;
4848
let lamports = Rent::get()?.try_minimum_balance(mint_size as usize)?;
4949

5050
accounts.system_program
@@ -57,10 +57,9 @@ pub fn handle_initialize(accounts: &Initialize) -> Result<(), ProgramError> {
5757
)
5858
.invoke()?;
5959

60-
// InitializePermanentDelegate: opcode 35, delegate pubkey
61-
// Actually the correct opcode is 38 (PermanentDelegate)
60+
// InitializePermanentDelegate: opcode 35, delegate pubkey (32 bytes, not COption)
6261
let mut ext_data = [0u8; 33];
63-
ext_data[0] = 38;
62+
ext_data[0] = 35;
6463
ext_data[1..33].copy_from_slice(accounts.payer.to_account_view().address().as_ref());
6564

6665
CpiCall::new(

tokens/token-2022/transfer-fee/quasar/src/lib.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,18 @@ pub fn handle_initialize(accounts: &Initialize, basis_points: u16, max_fee: u64)
8484
)
8585
.invoke()?;
8686

87-
// TransferFeeInitialize: opcode 26
88-
// Data: [26, config_authority (32), withdraw_authority (32), basis_points (u16 LE), max_fee (u64 LE)]
89-
let mut ext_data = [0u8; 75];
90-
ext_data[0] = 26;
91-
ext_data[1..33].copy_from_slice(accounts.payer.to_account_view().address().as_ref());
92-
ext_data[33..65].copy_from_slice(accounts.payer.to_account_view().address().as_ref());
93-
ext_data[65..67].copy_from_slice(&basis_points.to_le_bytes());
94-
ext_data[67..75].copy_from_slice(&max_fee.to_le_bytes());
87+
// TransferFeeExtension opcode 26, sub-instruction 0 = InitializeTransferFeeConfig
88+
// Data: [26, 0, COption_flag(1), config_authority(32), COption_flag(1), withdraw_authority(32),
89+
// basis_points(u16 LE), max_fee(u64 LE)]
90+
let mut ext_data = [0u8; 78];
91+
ext_data[0] = 26; // TransferFeeExtension
92+
ext_data[1] = 0; // InitializeTransferFeeConfig sub-instruction
93+
ext_data[2] = 1; // COption::Some for config_authority
94+
ext_data[3..35].copy_from_slice(accounts.payer.to_account_view().address().as_ref());
95+
ext_data[35] = 1; // COption::Some for withdraw_authority
96+
ext_data[36..68].copy_from_slice(accounts.payer.to_account_view().address().as_ref());
97+
ext_data[68..70].copy_from_slice(&basis_points.to_le_bytes());
98+
ext_data[70..78].copy_from_slice(&max_fee.to_le_bytes());
9599

96100
CpiCall::new(
97101
accounts.token_program.to_account_view().address(),

0 commit comments

Comments
 (0)