Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 00f28ee

Browse files
committed
token: Add InitializeAccount2 instruction
Passes the owner as instruction data rather than on the accounts list, improving CPI ergonomics where the owner's `AccountInfo` isn't otherwise required
1 parent 99d7ba1 commit 00f28ee

File tree

2 files changed

+130
-5
lines changed

2 files changed

+130
-5
lines changed

token/program/src/instruction.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,20 @@ pub enum TokenInstruction {
349349
/// Expected number of base 10 digits to the right of the decimal place.
350350
decimals: u8,
351351
},
352+
/// Like InitializeAccount, but the owner pubkey is passed via instruction data
353+
/// rather than the accounts list. This variant may be preferable when using
354+
/// Cross Program Invocation from an instruction that does not need the owner's
355+
/// `AccountInfo` otherwise.
356+
///
357+
/// Accounts expected by this instruction:
358+
///
359+
/// 0. `[writable]` The account to initialize.
360+
/// 1. `[]` The mint this account will be associated with.
361+
/// 3. `[]` Rent sysvar
362+
InitializeAccount2 {
363+
/// The new account's owner/multisignature.
364+
owner: Pubkey,
365+
},
352366
}
353367
impl TokenInstruction {
354368
/// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html).
@@ -446,6 +460,10 @@ impl TokenInstruction {
446460

447461
Self::BurnChecked { amount, decimals }
448462
}
463+
16 => {
464+
let (owner, _rest) = Self::unpack_pubkey(rest)?;
465+
Self::InitializeAccount2 { owner }
466+
}
449467

450468
_ => return Err(TokenError::InvalidInstruction.into()),
451469
})
@@ -518,6 +536,10 @@ impl TokenInstruction {
518536
buf.extend_from_slice(&amount.to_le_bytes());
519537
buf.push(decimals);
520538
}
539+
&Self::InitializeAccount2 { owner } => {
540+
buf.push(16);
541+
buf.extend_from_slice(owner.as_ref());
542+
}
521543
};
522544
buf
523545
}
@@ -625,7 +647,7 @@ pub fn initialize_account(
625647
mint_pubkey: &Pubkey,
626648
owner_pubkey: &Pubkey,
627649
) -> Result<Instruction, ProgramError> {
628-
let data = TokenInstruction::InitializeAccount.pack(); // TODO do we need to return result?
650+
let data = TokenInstruction::InitializeAccount.pack();
629651

630652
let accounts = vec![
631653
AccountMeta::new(*account_pubkey, false),
@@ -641,6 +663,31 @@ pub fn initialize_account(
641663
})
642664
}
643665

666+
/// Creates a `InitializeAccount2` instruction.
667+
pub fn initialize_account2(
668+
token_program_id: &Pubkey,
669+
account_pubkey: &Pubkey,
670+
mint_pubkey: &Pubkey,
671+
owner_pubkey: &Pubkey,
672+
) -> Result<Instruction, ProgramError> {
673+
let data = TokenInstruction::InitializeAccount2 {
674+
owner: *owner_pubkey,
675+
}
676+
.pack();
677+
678+
let accounts = vec![
679+
AccountMeta::new(*account_pubkey, false),
680+
AccountMeta::new_readonly(*mint_pubkey, false),
681+
AccountMeta::new_readonly(sysvar::rent::id(), false),
682+
];
683+
684+
Ok(Instruction {
685+
program_id: *token_program_id,
686+
accounts,
687+
data,
688+
})
689+
}
690+
644691
/// Creates a `InitializeMultisig` instruction.
645692
pub fn initialize_multisig(
646693
token_program_id: &Pubkey,
@@ -1214,5 +1261,15 @@ mod test {
12141261
assert_eq!(packed, expect);
12151262
let unpacked = TokenInstruction::unpack(&expect).unwrap();
12161263
assert_eq!(unpacked, check);
1264+
1265+
let check = TokenInstruction::InitializeAccount2 {
1266+
owner: Pubkey::new(&[2u8; 32]),
1267+
};
1268+
let packed = check.pack();
1269+
let mut expect = vec![16u8];
1270+
expect.extend_from_slice(&[2u8; 32]);
1271+
assert_eq!(packed, expect);
1272+
let unpacked = TokenInstruction::unpack(&expect).unwrap();
1273+
assert_eq!(unpacked, check);
12171274
}
12181275
}

token/program/src/processor.rs

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,18 @@ impl Processor {
5252
Ok(())
5353
}
5454

55-
/// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction.
56-
pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
55+
fn _process_initialize_account(
56+
accounts: &[AccountInfo],
57+
owner: Option<&Pubkey>,
58+
) -> ProgramResult {
5759
let account_info_iter = &mut accounts.iter();
5860
let new_account_info = next_account_info(account_info_iter)?;
5961
let mint_info = next_account_info(account_info_iter)?;
60-
let owner_info = next_account_info(account_info_iter)?;
62+
let owner = if let Some(owner) = owner {
63+
owner
64+
} else {
65+
next_account_info(account_info_iter)?.key
66+
};
6167
let new_account_info_data_len = new_account_info.data_len();
6268
let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?;
6369

@@ -76,7 +82,7 @@ impl Processor {
7682
}
7783

7884
account.mint = *mint_info.key;
79-
account.owner = *owner_info.key;
85+
account.owner = *owner;
8086
account.delegate = COption::None;
8187
account.delegated_amount = 0;
8288
account.state = AccountState::Initialized;
@@ -97,6 +103,16 @@ impl Processor {
97103
Ok(())
98104
}
99105

106+
/// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction.
107+
pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
108+
Self::_process_initialize_account(accounts, None)
109+
}
110+
111+
/// Processes an [InitializeAccount2](enum.TokenInstruction.html) instruction.
112+
pub fn process_initialize_account2(accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult {
113+
Self::_process_initialize_account(accounts, Some(&owner))
114+
}
115+
100116
/// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction.
101117
pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
102118
let account_info_iter = &mut accounts.iter();
@@ -641,6 +657,10 @@ impl Processor {
641657
msg!("Instruction: InitializeAccount");
642658
Self::process_initialize_account(accounts)
643659
}
660+
TokenInstruction::InitializeAccount2 { owner } => {
661+
msg!("Instruction: InitializeAccount2");
662+
Self::process_initialize_account2(accounts, owner)
663+
}
644664
TokenInstruction::InitializeMultisig { m } => {
645665
msg!("Instruction: InitializeMultisig");
646666
Self::process_initialize_multisig(accounts, m)
@@ -5660,4 +5680,52 @@ mod tests {
56605680
let account = Account::unpack_unchecked(&account_account.data).unwrap();
56615681
assert_eq!(account.state, AccountState::Initialized);
56625682
}
5683+
5684+
#[test]
5685+
fn test_initialize_account2() {
5686+
let program_id = Pubkey::new_unique();
5687+
let account_key = Pubkey::new_unique();
5688+
let mut account_account = SolanaAccount::new(
5689+
account_minimum_balance(),
5690+
Account::get_packed_len(),
5691+
&program_id,
5692+
);
5693+
let mut account2_account = SolanaAccount::new(
5694+
account_minimum_balance(),
5695+
Account::get_packed_len(),
5696+
&program_id,
5697+
);
5698+
let owner_key = Pubkey::new_unique();
5699+
let mut owner_account = SolanaAccount::default();
5700+
let mint_key = Pubkey::new_unique();
5701+
let mut mint_account =
5702+
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
5703+
let mut rent_sysvar = rent_sysvar();
5704+
5705+
// create mint
5706+
do_process_instruction(
5707+
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
5708+
vec![&mut mint_account, &mut rent_sysvar],
5709+
)
5710+
.unwrap();
5711+
5712+
do_process_instruction(
5713+
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
5714+
vec![
5715+
&mut account_account,
5716+
&mut mint_account,
5717+
&mut owner_account,
5718+
&mut rent_sysvar,
5719+
],
5720+
)
5721+
.unwrap();
5722+
5723+
do_process_instruction(
5724+
initialize_account2(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
5725+
vec![&mut account2_account, &mut mint_account, &mut rent_sysvar],
5726+
)
5727+
.unwrap();
5728+
5729+
assert_eq!(account_account, account2_account);
5730+
}
56635731
}

0 commit comments

Comments
 (0)