Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ pub enum TokenWrapInstruction {
///
/// Accounts expected by this instruction:
///
/// 0. `[w]` Escrow of unwrapped tokens, must be owned by:
/// `get_wrapped_mint_authority(wrapped_mint_address)`
/// 1. `[w]` Unwrapped token account to wrap
/// `get_wrapped_mint_authority(wrapped_mint_address)`
/// 2. `[w]` Recipient wrapped token account
/// 3. `[w]` Wrapped mint, must be initialized, address must be:
/// 0. `[w]` Recipient wrapped token account
/// 1. `[w]` Wrapped mint, must be initialized, address must be:
/// `get_wrapped_mint_address(unwrapped_mint_address,
/// wrapped_token_program_id)`
/// 4. `[]` Unwrapped token mint
/// 5. `[]` Wrapped mint authority, address must be:
/// 2. `[]` Wrapped mint authority, address must be:
/// `get_wrapped_mint_authority(wrapped_mint)`
/// 6. `[]` SPL Token program for unwrapped mint
/// 7. `[]` SPL Token program for wrapped mint
/// 3. `[]` SPL Token program for unwrapped mint
/// 4. `[]` SPL Token program for wrapped mint
/// 5. `[w]` Unwrapped token account to wrap
/// `get_wrapped_mint_authority(wrapped_mint_address)`
/// 6. `[]` Unwrapped token mint
/// 7. `[w]` Escrow of unwrapped tokens, must be owned by:
/// `get_wrapped_mint_authority(wrapped_mint_address)`
/// 8. `[s]` Transfer authority on unwrapped token account. Not required to
/// be a signer if it's a multisig.
/// 9. `..8+M` `[s]` (Optional) M multisig signers on unwrapped token
Expand Down Expand Up @@ -160,27 +160,27 @@ pub fn create_mint(
#[allow(clippy::too_many_arguments)]
pub fn wrap(
program_id: &Pubkey,
unwrapped_escrow_address: &Pubkey,
unwrapped_token_account_address: &Pubkey,
recipient_wrapped_token_account_address: &Pubkey,
wrapped_mint_address: &Pubkey,
unwrapped_mint_address: &Pubkey,
wrapped_mint_authority_address: &Pubkey,
unwrapped_token_program_id: &Pubkey,
wrapped_token_program_id: &Pubkey,
unwrapped_token_account_address: &Pubkey,
unwrapped_mint_address: &Pubkey,
unwrapped_escrow_address: &Pubkey,
transfer_authority_address: &Pubkey,
multisig_signer_pubkeys: &[&Pubkey],
amount: u64,
) -> Instruction {
let mut accounts = vec![
AccountMeta::new(*unwrapped_escrow_address, false),
AccountMeta::new(*unwrapped_token_account_address, false),
AccountMeta::new(*recipient_wrapped_token_account_address, false),
AccountMeta::new(*wrapped_mint_address, false),
AccountMeta::new_readonly(*unwrapped_mint_address, false),
AccountMeta::new_readonly(*wrapped_mint_authority_address, false),
AccountMeta::new_readonly(*unwrapped_token_program_id, false),
AccountMeta::new_readonly(*wrapped_token_program_id, false),
AccountMeta::new(*unwrapped_token_account_address, false),
AccountMeta::new_readonly(*unwrapped_mint_address, false),
AccountMeta::new(*unwrapped_escrow_address, false),
AccountMeta::new_readonly(
*transfer_authority_address,
multisig_signer_pubkeys.is_empty(),
Expand Down
26 changes: 13 additions & 13 deletions program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,16 +171,16 @@ pub fn process_wrap(_program_id: &Pubkey, accounts: &[AccountInfo], amount: u64)

let account_info_iter = &mut accounts.iter();

let unwrapped_escrow = next_account_info(account_info_iter)?;
let unwrapped_token_account = next_account_info(account_info_iter)?;
let recipient_wrapped_token_account = next_account_info(account_info_iter)?;
let wrapped_mint = next_account_info(account_info_iter)?;
let unwrapped_mint = next_account_info(account_info_iter)?;
let wrapped_mint_authority = next_account_info(account_info_iter)?;
let unwrapped_token_program = next_account_info(account_info_iter)?;
let wrapped_token_program = next_account_info(account_info_iter)?;
let unwrapped_token_account = next_account_info(account_info_iter)?;
let unwrapped_mint = next_account_info(account_info_iter)?;
let unwrapped_escrow = next_account_info(account_info_iter)?;
let transfer_authority = next_account_info(account_info_iter)?;
let _signer_accounts = account_info_iter.as_slice();
let multisig_signer_accounts = account_info_iter.as_slice();

// Validate accounts

Expand All @@ -207,24 +207,24 @@ pub fn process_wrap(_program_id: &Pubkey, accounts: &[AccountInfo], amount: u64)

let unwrapped_mint_data = unwrapped_mint.try_borrow_data()?;
let unwrapped_mint_state = PodStateWithExtensions::<PodMint>::unpack(&unwrapped_mint_data)?;
invoke_signed(

let multisig_signer_pubkeys = multisig_signer_accounts
.iter()
.map(|account| account.key)
.collect::<Vec<_>>();

invoke(
&spl_token_2022::instruction::transfer_checked(
unwrapped_token_program.key,
unwrapped_token_account.key,
unwrapped_mint.key,
unwrapped_escrow.key,
transfer_authority.key,
&[],
&multisig_signer_pubkeys,
amount,
unwrapped_mint_state.base.decimals,
)?,
&[
unwrapped_token_account.clone(),
unwrapped_mint.clone(),
unwrapped_escrow.clone(),
transfer_authority.clone(),
],
&[],
&accounts[5..],
)?;

// Mint wrapped tokens to recipient
Expand Down
46 changes: 45 additions & 1 deletion program/tests/helpers/common.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use {
crate::helpers::create_mint_builder::TokenProgram,
crate::helpers::{
create_mint_builder::{KeyedAccount, TokenProgram},
wrap_builder::TransferAuthority,
},
mollusk_svm::Mollusk,
solana_account::Account,
solana_program_option::COption,
Expand Down Expand Up @@ -89,3 +92,44 @@ pub fn setup_mint(token_program: TokenProgram, rent: &Rent, mint_authority: Pubk
..Default::default()
}
}

pub fn setup_multisig(program: TokenProgram) -> TransferAuthority {
let multisig_key = Pubkey::new_unique();
let signer0_key = Pubkey::new_unique();
let signer1_key = Pubkey::new_unique();
let signer2_key = Pubkey::new_unique();

let mut multisig_account = Account {
lamports: 100_000_000,
owner: program.id(),
data: vec![0; spl_token_2022::state::Multisig::LEN],
..Account::default()
};

let multisig_state = spl_token_2022::state::Multisig {
m: 2,
n: 3,
is_initialized: true,
signers: [
signer0_key,
signer1_key,
signer2_key,
Pubkey::default(),
Pubkey::default(),
Pubkey::default(),
Pubkey::default(),
Pubkey::default(),
Pubkey::default(),
Pubkey::default(),
Pubkey::default(),
],
};
spl_token_2022::state::Multisig::pack(multisig_state, &mut multisig_account.data).unwrap();
TransferAuthority {
keyed_account: KeyedAccount {
key: multisig_key,
account: multisig_account,
},
signers: vec![signer0_key, signer1_key, signer2_key],
}
}
2 changes: 1 addition & 1 deletion program/tests/helpers/create_mint_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct CreateMintResult {
pub wrapped_backpointer: KeyedAccount,
}

#[derive(Debug, Clone)]
#[derive(Default, Debug, Clone)]
pub struct KeyedAccount {
pub key: Pubkey,
pub account: Account,
Expand Down
54 changes: 37 additions & 17 deletions program/tests/helpers/wrap_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ use {
spl_token_wrap::{get_wrapped_mint_address, get_wrapped_mint_authority, instruction::wrap},
};

#[derive(Debug, Clone, Default)]
pub struct TransferAuthority {
pub keyed_account: KeyedAccount,
pub signers: Vec<Pubkey>,
}

pub struct WrapBuilder<'a> {
mollusk: Mollusk,
wrap_amount: Option<u64>,
Expand All @@ -31,6 +37,7 @@ pub struct WrapBuilder<'a> {
unwrapped_escrow_account: Option<Account>,
unwrapped_token_program: Option<TokenProgram>,
wrapped_token_program: Option<TokenProgram>,
transfer_authority: Option<TransferAuthority>,
}

impl Default for WrapBuilder<'_> {
Expand All @@ -49,6 +56,7 @@ impl Default for WrapBuilder<'_> {
unwrapped_escrow_account: None,
unwrapped_token_program: None,
wrapped_token_program: None,
transfer_authority: None,
}
}
}
Expand Down Expand Up @@ -104,6 +112,11 @@ impl<'a> WrapBuilder<'a> {
self
}

pub fn transfer_authority(mut self, auth: TransferAuthority) -> Self {
self.transfer_authority = Some(auth);
self
}

pub fn check(mut self, check: Check<'a>) -> Self {
self.checks.push(check);
self
Expand Down Expand Up @@ -178,7 +191,7 @@ impl<'a> WrapBuilder<'a> {

pub fn execute(mut self) -> WrapResult {
let unwrapped_token_account_address = Pubkey::new_unique();
let unwrapped_token_account_authority = Pubkey::new_unique();
let unwrapped_token_account_authority = self.transfer_authority.clone().unwrap_or_default();

let unwrapped_token_program = self
.unwrapped_token_program
Expand All @@ -204,7 +217,7 @@ impl<'a> WrapBuilder<'a> {

let token = spl_token::state::Account {
mint: unwrapped_mint.key,
owner: unwrapped_token_account_authority,
owner: unwrapped_token_account_authority.keyed_account.key,
amount: self.unwrapped_token_starting_amount.unwrap_or(wrap_amount),
delegate: None.into(),
state: spl_token::state::AccountState::Initialized,
Expand Down Expand Up @@ -258,42 +271,49 @@ impl<'a> WrapBuilder<'a> {

let instruction = wrap(
&spl_token_wrap::id(),
&unwrapped_escrow_address,
&unwrapped_token_account_address,
&recipient.key,
&wrapped_mint.key,
&unwrapped_mint.key,
&wrapped_mint_authority,
&unwrapped_token_program.id(),
&wrapped_token_program.id(),
&unwrapped_token_account_authority,
&[],
&unwrapped_token_account_address,
&unwrapped_mint.key,
&unwrapped_escrow_address,
&unwrapped_token_account_authority.keyed_account.key,
&unwrapped_token_account_authority
.signers
.iter()
.collect::<Vec<_>>(),
wrap_amount,
);

let accounts = &[
(
unwrapped_escrow_address,
self.unwrapped_escrow_account
.unwrap_or(unwrapped_escrow_account),
),
(unwrapped_token_account_address, unwrapped_token_account),
let mut accounts = vec![
recipient.pair(),
wrapped_mint.pair(),
unwrapped_mint.pair(),
(wrapped_mint_authority, Account::default()),
unwrapped_token_program.keyed_account(),
wrapped_token_program.keyed_account(),
(unwrapped_token_account_authority, Account::default()),
(unwrapped_token_account_address, unwrapped_token_account),
unwrapped_mint.pair(),
(
unwrapped_escrow_address,
self.unwrapped_escrow_account
.unwrap_or(unwrapped_escrow_account),
),
unwrapped_token_account_authority.keyed_account.pair(),
];

for signer_key in &unwrapped_token_account_authority.signers {
accounts.push((*signer_key, Account::default()));
}

if self.checks.is_empty() {
self.checks.push(Check::success());
}

let result =
self.mollusk
.process_and_validate_instruction(&instruction, accounts, &self.checks);
.process_and_validate_instruction(&instruction, &accounts, &self.checks);

WrapResult {
unwrapped_token: KeyedAccount {
Expand Down
33 changes: 32 additions & 1 deletion program/tests/test_wrap.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::helpers::{
common::MINT_SUPPLY,
common::{setup_multisig, MINT_SUPPLY},
create_mint_builder::{CreateMintBuilder, KeyedAccount, TokenProgram},
wrap_builder::{WrapBuilder, WrapResult},
},
Expand Down Expand Up @@ -163,3 +163,34 @@ fn test_successful_spl_token_2022_to_token_2022() {

assert_wrap_result(starting_amount, wrap_amount, &wrap_result);
}

#[test]
fn test_wrap_with_spl_token_multisig() {
let starting_amount = 500_000;
let wrap_amount = 8_000;
let multisig = setup_multisig(TokenProgram::SplToken);

let wrap_result = WrapBuilder::default()
.transfer_authority(multisig)
.recipient_starting_amount(starting_amount)
.wrap_amount(wrap_amount)
.execute();

assert_wrap_result(starting_amount, wrap_amount, &wrap_result);
}

#[test]
fn test_wrap_with_token_2022_multisig() {
let starting_amount = 10_000;
let wrap_amount = 252;
let multisig = setup_multisig(TokenProgram::SplToken2022);

let wrap_result = WrapBuilder::default()
.transfer_authority(multisig)
.unwrapped_token_program(TokenProgram::SplToken2022)
.recipient_starting_amount(starting_amount)
.wrap_amount(wrap_amount)
.execute();

assert_wrap_result(starting_amount, wrap_amount, &wrap_result);
}