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
This commit is contained in:
parent
99d7ba1563
commit
00f28eeb0c
|
@ -349,6 +349,20 @@ pub enum TokenInstruction {
|
|||
/// Expected number of base 10 digits to the right of the decimal place.
|
||||
decimals: u8,
|
||||
},
|
||||
/// Like InitializeAccount, but the owner pubkey is passed via instruction data
|
||||
/// rather than the accounts list. This variant may be preferable when using
|
||||
/// Cross Program Invocation from an instruction that does not need the owner's
|
||||
/// `AccountInfo` otherwise.
|
||||
///
|
||||
/// Accounts expected by this instruction:
|
||||
///
|
||||
/// 0. `[writable]` The account to initialize.
|
||||
/// 1. `[]` The mint this account will be associated with.
|
||||
/// 3. `[]` Rent sysvar
|
||||
InitializeAccount2 {
|
||||
/// The new account's owner/multisignature.
|
||||
owner: Pubkey,
|
||||
},
|
||||
}
|
||||
impl TokenInstruction {
|
||||
/// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html).
|
||||
|
@ -446,6 +460,10 @@ impl TokenInstruction {
|
|||
|
||||
Self::BurnChecked { amount, decimals }
|
||||
}
|
||||
16 => {
|
||||
let (owner, _rest) = Self::unpack_pubkey(rest)?;
|
||||
Self::InitializeAccount2 { owner }
|
||||
}
|
||||
|
||||
_ => return Err(TokenError::InvalidInstruction.into()),
|
||||
})
|
||||
|
@ -518,6 +536,10 @@ impl TokenInstruction {
|
|||
buf.extend_from_slice(&amount.to_le_bytes());
|
||||
buf.push(decimals);
|
||||
}
|
||||
&Self::InitializeAccount2 { owner } => {
|
||||
buf.push(16);
|
||||
buf.extend_from_slice(owner.as_ref());
|
||||
}
|
||||
};
|
||||
buf
|
||||
}
|
||||
|
@ -625,7 +647,7 @@ pub fn initialize_account(
|
|||
mint_pubkey: &Pubkey,
|
||||
owner_pubkey: &Pubkey,
|
||||
) -> Result<Instruction, ProgramError> {
|
||||
let data = TokenInstruction::InitializeAccount.pack(); // TODO do we need to return result?
|
||||
let data = TokenInstruction::InitializeAccount.pack();
|
||||
|
||||
let accounts = vec![
|
||||
AccountMeta::new(*account_pubkey, false),
|
||||
|
@ -641,6 +663,31 @@ pub fn initialize_account(
|
|||
})
|
||||
}
|
||||
|
||||
/// Creates a `InitializeAccount2` instruction.
|
||||
pub fn initialize_account2(
|
||||
token_program_id: &Pubkey,
|
||||
account_pubkey: &Pubkey,
|
||||
mint_pubkey: &Pubkey,
|
||||
owner_pubkey: &Pubkey,
|
||||
) -> Result<Instruction, ProgramError> {
|
||||
let data = TokenInstruction::InitializeAccount2 {
|
||||
owner: *owner_pubkey,
|
||||
}
|
||||
.pack();
|
||||
|
||||
let accounts = vec![
|
||||
AccountMeta::new(*account_pubkey, false),
|
||||
AccountMeta::new_readonly(*mint_pubkey, false),
|
||||
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
||||
];
|
||||
|
||||
Ok(Instruction {
|
||||
program_id: *token_program_id,
|
||||
accounts,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a `InitializeMultisig` instruction.
|
||||
pub fn initialize_multisig(
|
||||
token_program_id: &Pubkey,
|
||||
|
@ -1214,5 +1261,15 @@ mod test {
|
|||
assert_eq!(packed, expect);
|
||||
let unpacked = TokenInstruction::unpack(&expect).unwrap();
|
||||
assert_eq!(unpacked, check);
|
||||
|
||||
let check = TokenInstruction::InitializeAccount2 {
|
||||
owner: Pubkey::new(&[2u8; 32]),
|
||||
};
|
||||
let packed = check.pack();
|
||||
let mut expect = vec![16u8];
|
||||
expect.extend_from_slice(&[2u8; 32]);
|
||||
assert_eq!(packed, expect);
|
||||
let unpacked = TokenInstruction::unpack(&expect).unwrap();
|
||||
assert_eq!(unpacked, check);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,12 +52,18 @@ impl Processor {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction.
|
||||
pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
|
||||
fn _process_initialize_account(
|
||||
accounts: &[AccountInfo],
|
||||
owner: Option<&Pubkey>,
|
||||
) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
let new_account_info = next_account_info(account_info_iter)?;
|
||||
let mint_info = next_account_info(account_info_iter)?;
|
||||
let owner_info = next_account_info(account_info_iter)?;
|
||||
let owner = if let Some(owner) = owner {
|
||||
owner
|
||||
} else {
|
||||
next_account_info(account_info_iter)?.key
|
||||
};
|
||||
let new_account_info_data_len = new_account_info.data_len();
|
||||
let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?;
|
||||
|
||||
|
@ -76,7 +82,7 @@ impl Processor {
|
|||
}
|
||||
|
||||
account.mint = *mint_info.key;
|
||||
account.owner = *owner_info.key;
|
||||
account.owner = *owner;
|
||||
account.delegate = COption::None;
|
||||
account.delegated_amount = 0;
|
||||
account.state = AccountState::Initialized;
|
||||
|
@ -97,6 +103,16 @@ impl Processor {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction.
|
||||
pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
|
||||
Self::_process_initialize_account(accounts, None)
|
||||
}
|
||||
|
||||
/// Processes an [InitializeAccount2](enum.TokenInstruction.html) instruction.
|
||||
pub fn process_initialize_account2(accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult {
|
||||
Self::_process_initialize_account(accounts, Some(&owner))
|
||||
}
|
||||
|
||||
/// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction.
|
||||
pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
|
@ -641,6 +657,10 @@ impl Processor {
|
|||
msg!("Instruction: InitializeAccount");
|
||||
Self::process_initialize_account(accounts)
|
||||
}
|
||||
TokenInstruction::InitializeAccount2 { owner } => {
|
||||
msg!("Instruction: InitializeAccount2");
|
||||
Self::process_initialize_account2(accounts, owner)
|
||||
}
|
||||
TokenInstruction::InitializeMultisig { m } => {
|
||||
msg!("Instruction: InitializeMultisig");
|
||||
Self::process_initialize_multisig(accounts, m)
|
||||
|
@ -5660,4 +5680,52 @@ mod tests {
|
|||
let account = Account::unpack_unchecked(&account_account.data).unwrap();
|
||||
assert_eq!(account.state, AccountState::Initialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_initialize_account2() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let account_key = Pubkey::new_unique();
|
||||
let mut account_account = SolanaAccount::new(
|
||||
account_minimum_balance(),
|
||||
Account::get_packed_len(),
|
||||
&program_id,
|
||||
);
|
||||
let mut account2_account = SolanaAccount::new(
|
||||
account_minimum_balance(),
|
||||
Account::get_packed_len(),
|
||||
&program_id,
|
||||
);
|
||||
let owner_key = Pubkey::new_unique();
|
||||
let mut owner_account = SolanaAccount::default();
|
||||
let mint_key = Pubkey::new_unique();
|
||||
let mut mint_account =
|
||||
SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id);
|
||||
let mut rent_sysvar = rent_sysvar();
|
||||
|
||||
// create mint
|
||||
do_process_instruction(
|
||||
initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(),
|
||||
vec![&mut mint_account, &mut rent_sysvar],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
do_process_instruction(
|
||||
initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
|
||||
vec![
|
||||
&mut account_account,
|
||||
&mut mint_account,
|
||||
&mut owner_account,
|
||||
&mut rent_sysvar,
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
do_process_instruction(
|
||||
initialize_account2(&program_id, &account_key, &mint_key, &owner_key).unwrap(),
|
||||
vec![&mut account2_account, &mut mint_account, &mut rent_sysvar],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(account_account, account2_account);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue