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.
|
/// Expected number of base 10 digits to the right of the decimal place.
|
||||||
decimals: u8,
|
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 {
|
impl TokenInstruction {
|
||||||
/// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html).
|
/// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html).
|
||||||
|
@ -446,6 +460,10 @@ impl TokenInstruction {
|
||||||
|
|
||||||
Self::BurnChecked { amount, decimals }
|
Self::BurnChecked { amount, decimals }
|
||||||
}
|
}
|
||||||
|
16 => {
|
||||||
|
let (owner, _rest) = Self::unpack_pubkey(rest)?;
|
||||||
|
Self::InitializeAccount2 { owner }
|
||||||
|
}
|
||||||
|
|
||||||
_ => return Err(TokenError::InvalidInstruction.into()),
|
_ => return Err(TokenError::InvalidInstruction.into()),
|
||||||
})
|
})
|
||||||
|
@ -518,6 +536,10 @@ impl TokenInstruction {
|
||||||
buf.extend_from_slice(&amount.to_le_bytes());
|
buf.extend_from_slice(&amount.to_le_bytes());
|
||||||
buf.push(decimals);
|
buf.push(decimals);
|
||||||
}
|
}
|
||||||
|
&Self::InitializeAccount2 { owner } => {
|
||||||
|
buf.push(16);
|
||||||
|
buf.extend_from_slice(owner.as_ref());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
@ -625,7 +647,7 @@ pub fn initialize_account(
|
||||||
mint_pubkey: &Pubkey,
|
mint_pubkey: &Pubkey,
|
||||||
owner_pubkey: &Pubkey,
|
owner_pubkey: &Pubkey,
|
||||||
) -> Result<Instruction, ProgramError> {
|
) -> Result<Instruction, ProgramError> {
|
||||||
let data = TokenInstruction::InitializeAccount.pack(); // TODO do we need to return result?
|
let data = TokenInstruction::InitializeAccount.pack();
|
||||||
|
|
||||||
let accounts = vec![
|
let accounts = vec![
|
||||||
AccountMeta::new(*account_pubkey, false),
|
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.
|
/// Creates a `InitializeMultisig` instruction.
|
||||||
pub fn initialize_multisig(
|
pub fn initialize_multisig(
|
||||||
token_program_id: &Pubkey,
|
token_program_id: &Pubkey,
|
||||||
|
@ -1214,5 +1261,15 @@ mod test {
|
||||||
assert_eq!(packed, expect);
|
assert_eq!(packed, expect);
|
||||||
let unpacked = TokenInstruction::unpack(&expect).unwrap();
|
let unpacked = TokenInstruction::unpack(&expect).unwrap();
|
||||||
assert_eq!(unpacked, check);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction.
|
fn _process_initialize_account(
|
||||||
pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
|
accounts: &[AccountInfo],
|
||||||
|
owner: Option<&Pubkey>,
|
||||||
|
) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
let new_account_info = next_account_info(account_info_iter)?;
|
let new_account_info = next_account_info(account_info_iter)?;
|
||||||
let mint_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 new_account_info_data_len = new_account_info.data_len();
|
||||||
let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?;
|
let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?;
|
||||||
|
|
||||||
|
@ -76,7 +82,7 @@ impl Processor {
|
||||||
}
|
}
|
||||||
|
|
||||||
account.mint = *mint_info.key;
|
account.mint = *mint_info.key;
|
||||||
account.owner = *owner_info.key;
|
account.owner = *owner;
|
||||||
account.delegate = COption::None;
|
account.delegate = COption::None;
|
||||||
account.delegated_amount = 0;
|
account.delegated_amount = 0;
|
||||||
account.state = AccountState::Initialized;
|
account.state = AccountState::Initialized;
|
||||||
|
@ -97,6 +103,16 @@ impl Processor {
|
||||||
Ok(())
|
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.
|
/// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction.
|
||||||
pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
|
pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
|
@ -641,6 +657,10 @@ impl Processor {
|
||||||
msg!("Instruction: InitializeAccount");
|
msg!("Instruction: InitializeAccount");
|
||||||
Self::process_initialize_account(accounts)
|
Self::process_initialize_account(accounts)
|
||||||
}
|
}
|
||||||
|
TokenInstruction::InitializeAccount2 { owner } => {
|
||||||
|
msg!("Instruction: InitializeAccount2");
|
||||||
|
Self::process_initialize_account2(accounts, owner)
|
||||||
|
}
|
||||||
TokenInstruction::InitializeMultisig { m } => {
|
TokenInstruction::InitializeMultisig { m } => {
|
||||||
msg!("Instruction: InitializeMultisig");
|
msg!("Instruction: InitializeMultisig");
|
||||||
Self::process_initialize_multisig(accounts, m)
|
Self::process_initialize_multisig(accounts, m)
|
||||||
|
@ -5660,4 +5680,52 @@ mod tests {
|
||||||
let account = Account::unpack_unchecked(&account_account.data).unwrap();
|
let account = Account::unpack_unchecked(&account_account.data).unwrap();
|
||||||
assert_eq!(account.state, AccountState::Initialized);
|
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