Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External Treasury with config as delegate #31

Merged
merged 4 commits into from
Aug 2, 2023
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
37 changes: 12 additions & 25 deletions token-dispenser/programs/token-dispenser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,21 +173,17 @@ pub mod token_dispenser {
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
pub payer: Signer<'info>,
pub payer: Signer<'info>,
#[account(init, payer = payer, space = Config::LEN, seeds = [CONFIG_SEED], bump)]
pub config: Account<'info, Config>,
pub config: Account<'info, Config>,
/// Mint of the treasury
pub mint: Account<'info, Mint>,
#[account(
init,
payer = payer,
associated_token::authority = config,
associated_token::mint = mint,
)]
pub treasury: Account<'info, TokenAccount>,
pub system_program: Program<'info, System>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub mint: Account<'info, Mint>,
/// Treasury token account. This is an externally owned token account and
/// the owner of this account will approve the config as a delegate using the
/// solana CLI command `spl-token approve <treasury_account_address> <approve_amount> <config_address>`
#[account( token::mint = mint )]
pub treasury: Account<'info, TokenAccount>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
Expand Down Expand Up @@ -221,11 +217,7 @@ pub struct Checkout<'info> {
/// Mint of the treasury & claimant_fund token account.
/// Needed if the `claimant_fund` token account needs to be initialized
pub mint: Account<'info, Mint>,
#[account(
mut,
associated_token::authority = config,
associated_token::mint = config.mint,
)]
#[account(mut)]
pub treasury: Account<'info, TokenAccount>,
#[account(mut, seeds = [CART_SEED, claimant.key.as_ref()], bump)]
pub cart: Account<'info, Cart>,
Expand Down Expand Up @@ -544,10 +536,6 @@ pub fn get_cart_pda(claimant: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[CART_SEED, claimant.as_ref()], &crate::id())
}

pub fn get_treasury_ata(config: &Pubkey, mint: &Pubkey) -> Pubkey {
get_associated_token_address(config, mint)
}

impl crate::accounts::Initialize {
pub fn populate(payer: Pubkey, mint: Pubkey, treasury: Pubkey) -> Self {
crate::accounts::Initialize {
Expand All @@ -556,8 +544,6 @@ impl crate::accounts::Initialize {
mint,
treasury,
system_program: system_program::System::id(),
token_program: anchor_spl::token::Token::id(),
associated_token_program: AssociatedToken::id(),
}
}
}
Expand All @@ -579,6 +565,7 @@ impl crate::accounts::Checkout {
pub fn populate(
claimant: Pubkey,
mint: Pubkey,
treasury: Pubkey,
cart_override: Option<Pubkey>,
claimant_fund_override: Option<Pubkey>,
) -> Self {
Expand All @@ -587,7 +574,7 @@ impl crate::accounts::Checkout {
claimant,
config,
mint,
treasury: get_treasury_ata(&config, &mint),
treasury,
cart: cart_override.unwrap_or_else(|| get_cart_pda(&claimant).0),
claimant_fund: claimant_fund_override
.unwrap_or_else(|| get_associated_token_address(&claimant, &mint)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ use {
super::test_happy_path::TestClaimCertificate,
crate::{
accounts,
get_cart_pda,
get_config_pda,
get_receipt_pda,
get_treasury_ata,
instruction,
tests::merkleize,
ClaimInfo,
Expand All @@ -22,7 +19,11 @@ use {
solana_program::{
hash,
instruction::Instruction,
system_instruction::create_account,
program_option::COption,
system_instruction::{
self,
create_account,
},
},
system_program,
AccountDeserialize,
Expand All @@ -31,22 +32,19 @@ use {
InstructionData,
ToAccountMetas,
},
anchor_spl::{
associated_token::{
get_associated_token_address,
get_associated_token_address_with_program_id,
AssociatedToken,
},
token::{
spl_token::instruction::{
anchor_spl::token::{
spl_token,
spl_token::{
error::TokenError,
instruction::{
initialize_account3,
initialize_mint,
initialize_mint2,
mint_to,
},
Mint,
Token,
TokenAccount,
},
Mint,
Token,
TokenAccount,
},
pythnet_sdk::accumulators::merkle::{
MerkleRoot,
Expand Down Expand Up @@ -74,31 +72,49 @@ use {
};

pub struct DispenserSimulator {
banks_client: BanksClient,
pub genesis_keypair: Keypair,
recent_blockhash: hash::Hash,
pub mint_keypair: Keypair,
banks_client: BanksClient,
pub genesis_keypair: Keypair,
recent_blockhash: hash::Hash,
pub mint_keypair: Keypair,
/// also the owner/authority of `pyth_treasury`
pub pyth_mint_authority: Keypair,
pub pyth_treasury: Pubkey,
}

impl DispenserSimulator {
pub async fn new() -> Self {
let program_test = ProgramTest::new("token_dispenser", crate::id(), None);
let (banks_client, genesis_keypair, recent_blockhash) = program_test.start().await;
let mint_keypair = Keypair::new();
let pyth_mint_authority = Keypair::new();
let pyth_treasury = Keypair::new();
let mut simulator = DispenserSimulator {
banks_client,
genesis_keypair,
recent_blockhash,
mint_keypair,
pyth_mint_authority,
pyth_treasury: pyth_treasury.pubkey(),
};

simulator
.create_mint(
&copy_keypair(&simulator.mint_keypair),
&copy_keypair(&simulator.genesis_keypair),
&simulator.pyth_mint_authority.pubkey(),
6,
)
.await
.unwrap();

simulator
.create_token_account(
simulator.mint_keypair.pubkey(),
&copy_keypair(&simulator.pyth_mint_authority),
&pyth_treasury,
)
.await
.unwrap();

simulator
}

Expand All @@ -115,10 +131,16 @@ impl DispenserSimulator {
self.banks_client.get_rent().await.unwrap()
}

pub async fn airdrop(&mut self, target: Pubkey, amount: u64) -> Result<(), BanksClientError> {
let airdrop_ix =
system_instruction::transfer(&self.genesis_keypair.pubkey(), &target, amount);
self.process_ix(&[airdrop_ix], &vec![]).await
}

pub async fn create_mint(
&mut self,
mint_keypair: &Keypair,
mint_authority: &Keypair,
mint_authority: &Pubkey,
decimals: u8,
) -> Result<(), BanksClientError> {
let space = Mint::LEN;
Expand All @@ -131,32 +153,29 @@ impl DispenserSimulator {
space as u64,
&Token::id(),
),
initialize_mint(
initialize_mint2(
&Token::id(),
&mint_keypair.pubkey(),
&mint_authority.pubkey(),
&mint_authority,
None,
decimals,
)
.unwrap(),
];
self.process_ix(init_mint_ixs, &vec![mint_keypair, mint_authority])
.await
self.process_ix(init_mint_ixs, &vec![mint_keypair]).await
}

pub async fn mint_to_treasury(&mut self, mint_amount: u64) -> Result<(), BanksClientError> {
let config = get_config_pda().0;
let treasury = get_treasury_ata(&config, &self.mint_keypair.pubkey());
let mint_to_ix = &[mint_to(
&Token::id(),
&self.mint_keypair.pubkey(),
&treasury,
&self.genesis_keypair.pubkey(),
&self.pyth_treasury,
&self.pyth_mint_authority.pubkey(),
&[],
mint_amount,
)
.unwrap()];
self.process_ix(mint_to_ix, &vec![&copy_keypair(&self.genesis_keypair)])
self.process_ix(mint_to_ix, &vec![&copy_keypair(&self.pyth_mint_authority)])
.await
}

Expand Down Expand Up @@ -187,15 +206,10 @@ impl DispenserSimulator {
mint_pubkey_override: Option<Pubkey>,
treasury_pubkey_override: Option<Pubkey>,
) -> Result<(), BanksClientError> {
let treasury = get_associated_token_address_with_program_id(
&(get_config_pda().0),
&self.mint_keypair.pubkey(),
&Token::id(),
);
let accounts = accounts::Initialize::populate(
self.genesis_keypair.pubkey(),
mint_pubkey_override.unwrap_or(self.mint_keypair.pubkey()),
treasury_pubkey_override.unwrap_or(treasury),
treasury_pubkey_override.unwrap_or(self.pyth_treasury),
)
.to_account_metas(None);
let instruction_data = instruction::Initialize {
Expand Down Expand Up @@ -247,6 +261,44 @@ impl DispenserSimulator {
Ok((merkle_tree, mock_offchain_certificates_and_claimants))
}

pub async fn approve_treasury_delegate(
&mut self,
delegate: Pubkey,
amount: u64,
) -> Result<(), BanksClientError> {
let approve_ix = spl_token::instruction::approve(
&Token::id(),
&self.pyth_treasury,
&delegate,
&self.pyth_mint_authority.pubkey(),
&[],
amount,
)
.unwrap();

self.process_ix(
&[approve_ix],
&vec![&copy_keypair(&self.pyth_mint_authority)],
)
.await
}

pub async fn revoke_treasury_delegate(&mut self) -> Result<(), BanksClientError> {
let revoke_ix = spl_token::instruction::revoke(
&Token::id(),
&self.pyth_treasury,
&self.pyth_mint_authority.pubkey(),
&[],
)
.unwrap();

self.process_ix(
&[revoke_ix],
&vec![&copy_keypair(&self.pyth_mint_authority)],
)
.await
}


pub async fn claim(
&mut self,
Expand Down Expand Up @@ -307,6 +359,7 @@ impl DispenserSimulator {
let accounts = accounts::Checkout::populate(
claimant.pubkey(),
mint,
self.pyth_treasury,
cart_override,
claimant_fund_override,
)
Expand Down Expand Up @@ -358,8 +411,28 @@ impl DispenserSimulator {
)
.unwrap(),
];
self.process_ix(init_token_account_ixs, &vec![&owner, &token_account])
self.process_ix(init_token_account_ixs, &vec![&token_account])
.await
}

pub async fn verify_token_account_data(
&mut self,
token_account: Pubkey,
expected_amount: u64,
expected_delegate: COption<Pubkey>,
expected_delegated_amount: u64,
) -> Result<(), BanksClientError> {
let token_account_data = self
.get_account_data::<TokenAccount>(token_account)
.await
.unwrap();
assert_eq!(token_account_data.amount, expected_amount);
assert_eq!(token_account_data.delegate, expected_delegate);
assert_eq!(
token_account_data.delegated_amount,
expected_delegated_amount
);
Ok(())
}
}

Expand All @@ -386,6 +459,15 @@ impl IntoTransactionError for ErrorCode {
)
}
}

impl IntoTransactionError for TokenError {
fn into_transaction_error(self) -> TransactionError {
TransactionError::InstructionError(
0,
InstructionError::try_from(u64::from(ProgramError::from(self))).unwrap(),
)
}
}
impl IntoTransactionError for InstructionError {
fn into_transaction_error(self) -> TransactionError {
TransactionError::InstructionError(0, self)
Expand Down
Loading
Loading