use steel::*;
use crate::{
instruction::*,
state::{
authority_transfer_pda, config_pda, fee_shard_pda, fee_shard_sol_storage_pda,
split_vault_pda, vault_sol_storage_pda,
},
};
pub fn create_vault(payer: Pubkey, seller: Pubkey) -> Instruction {
let (vault_pda, _) = split_vault_pda(&seller);
let (vault_sol_storage, _) = vault_sol_storage_pda(vault_pda);
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new(vault_pda, false),
AccountMeta::new(vault_sol_storage, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
],
data: CreateVault { seller }.to_bytes(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn sweep(
payer: Pubkey,
vault: Pubkey,
seller: Pubkey,
fee_destination: Pubkey,
token_mint: Pubkey,
amount: u64,
is_sol: bool,
vault_tokens: Option<Pubkey>,
seller_tokens: Option<Pubkey>,
fee_dest_tokens: Option<Pubkey>,
token_program: Option<Pubkey>,
) -> Instruction {
let (config_pda, _) = config_pda();
let mut accounts = vec![
AccountMeta::new(payer, true),
AccountMeta::new(vault, false),
AccountMeta::new_readonly(config_pda, false),
];
if is_sol {
let (vault_sol_storage, _) = vault_sol_storage_pda(vault);
accounts.push(AccountMeta::new(vault_sol_storage, false));
accounts.push(AccountMeta::new(seller, false));
accounts.push(AccountMeta::new(fee_destination, false));
accounts.push(AccountMeta::new_readonly(
solana_program::system_program::ID,
false,
));
} else {
accounts.push(AccountMeta::new(
vault_tokens.expect("vault_tokens is required for SPL sweep"),
false,
));
accounts.push(AccountMeta::new(
seller_tokens.expect("seller_tokens is required for SPL sweep"),
false,
));
accounts.push(AccountMeta::new(
fee_dest_tokens.expect("fee_dest_tokens is required for SPL sweep"),
false,
));
accounts.push(AccountMeta::new_readonly(token_mint, false));
accounts.push(AccountMeta::new_readonly(
token_program.unwrap_or(spl_token::ID),
false,
));
}
Instruction {
program_id: crate::ID,
accounts,
data: Sweep {
token_mint,
amount: amount.to_le_bytes(),
is_sol: if is_sol { [1] } else { [0] },
_padding: [0; 7],
}
.to_bytes(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn initialize(
authority: Pubkey,
fee_destination: Pubkey,
fee_bps: Option<u16>,
min_fee_amount: Option<u64>,
min_fee_amount_sol: Option<u64>,
provisioning_fee_sol: Option<u64>,
provisioning_fee_spl: Option<u64>,
discounted_fee_bps: Option<u16>,
use_fee_shard: Option<u8>,
shard_count: Option<u8>,
) -> Instruction {
let (config_pda, _) = config_pda();
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new(config_pda, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
AccountMeta::new_readonly(solana_program::sysvar::rent::ID, false),
],
data: Initialize {
fee_destination,
min_fee_amount: (min_fee_amount.unwrap_or(10_000)).to_le_bytes(),
min_fee_amount_sol: (min_fee_amount_sol.unwrap_or(200_000)).to_le_bytes(),
provisioning_fee_sol: (provisioning_fee_sol.unwrap_or(10_000_000)).to_le_bytes(),
provisioning_fee_spl: (provisioning_fee_spl.unwrap_or(1_000_000)).to_le_bytes(),
fee_bps: (fee_bps.unwrap_or(100)).to_le_bytes(),
discounted_fee_bps: (discounted_fee_bps.unwrap_or(95)).to_le_bytes(),
use_fee_shard: use_fee_shard.unwrap_or(0),
shard_count: shard_count.unwrap_or(8),
_padding: [0; 2],
}
.to_bytes(),
}
}
pub fn update_authority(current_authority: Pubkey, new_authority: Pubkey) -> Instruction {
let (config_pda, _) = config_pda();
let (transfer_pda, _) = authority_transfer_pda(config_pda);
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(current_authority, true),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(transfer_pda, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
],
data: UpdateAuthority { new_authority }.to_bytes(),
}
}
pub fn accept_authority(new_authority: Pubkey) -> Instruction {
let (config_pda, _) = config_pda();
let (transfer_pda, _) = authority_transfer_pda(config_pda);
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(new_authority, true), AccountMeta::new(config_pda, false), AccountMeta::new(transfer_pda, false), ],
data: AcceptAuthority {}.to_bytes(),
}
}
pub fn cancel_authority_proposal(current_authority: Pubkey) -> Instruction {
let (config_pda, _) = config_pda();
let (transfer_pda, _) = authority_transfer_pda(config_pda);
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(current_authority, true), AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(transfer_pda, false), ],
data: CancelAuthorityProposal {}.to_bytes(),
}
}
pub fn update_fee_rate(authority: Pubkey, new_fee_bps: u16) -> Instruction {
let (config_pda, _) = config_pda();
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new(config_pda, false),
],
data: UpdateFeeRate {
new_fee_bps: new_fee_bps.to_le_bytes(),
_padding: [0; 6],
}
.to_bytes(),
}
}
pub fn update_fee_destination(authority: Pubkey, new_fee_destination: Pubkey) -> Instruction {
let (config_pda, _) = config_pda();
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new(config_pda, false),
],
data: UpdateFeeDestination {
new_fee_destination,
}
.to_bytes(),
}
}
pub fn update_min_fee_amount(authority: Pubkey, new_min_fee_amount: u64) -> Instruction {
let (config_pda, _) = config_pda();
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new(config_pda, false),
],
data: UpdateMinFeeAmount {
new_min_fee_amount: new_min_fee_amount.to_le_bytes(),
}
.to_bytes(),
}
}
pub fn update_provisioning_fee(
authority: Pubkey,
new_provisioning_fee_sol: u64,
new_provisioning_fee_spl: u64,
) -> Instruction {
let (config_pda, _) = config_pda();
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new(config_pda, false),
],
data: UpdateProvisioningFee {
new_provisioning_fee_sol: new_provisioning_fee_sol.to_le_bytes(),
new_provisioning_fee_spl: new_provisioning_fee_spl.to_le_bytes(),
}
.to_bytes(),
}
}
pub fn update_discounted_fee_rate(authority: Pubkey, new_discounted_fee_bps: u16) -> Instruction {
let (config_pda, _) = config_pda();
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new(config_pda, false),
],
data: UpdateDiscountedFeeRate {
new_discounted_fee_bps: new_discounted_fee_bps.to_le_bytes(),
_padding: [0; 6],
}
.to_bytes(),
}
}
pub fn update_min_fee_amount_sol(authority: Pubkey, new_min_fee_amount_sol: u64) -> Instruction {
let (config_pda, _) = config_pda();
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new(config_pda, false),
],
data: UpdateMinFeeAmountSol {
new_min_fee_amount_sol: new_min_fee_amount_sol.to_le_bytes(),
}
.to_bytes(),
}
}
pub fn init_shard(payer: Pubkey, authority: Pubkey, index: u64) -> Instruction {
let (config_pda, _) = config_pda();
let (shard_pda, _) = fee_shard_pda(index);
let (shard_sol_storage, _) = fee_shard_sol_storage_pda(shard_pda);
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new_readonly(authority, true),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(shard_pda, false),
AccountMeta::new(shard_sol_storage, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
],
data: InitShard {
index: index.to_le_bytes(),
}
.to_bytes(),
}
}
pub fn collect_from_shard(
authority: Pubkey,
index: u64,
treasury: Pubkey,
mint: Option<Pubkey>,
amount: u64,
token_program: Option<Pubkey>,
) -> Instruction {
let (config_pda, _) = config_pda();
let (shard_pda, _) = fee_shard_pda(index);
let is_sol = mint.is_none() || mint == Some(Pubkey::default());
let mut accounts = vec![
AccountMeta::new_readonly(authority, true),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new_readonly(shard_pda, false),
];
if is_sol {
let (shard_sol_storage, _) = fee_shard_sol_storage_pda(shard_pda);
accounts.push(AccountMeta::new(shard_sol_storage, false));
accounts.push(AccountMeta::new(treasury, false));
accounts.push(AccountMeta::new_readonly(
solana_program::system_program::ID,
false,
));
} else {
let mint = mint.unwrap();
let shard_tokens =
spl_associated_token_account::get_associated_token_address(&shard_pda, &mint);
let treasury_tokens =
spl_associated_token_account::get_associated_token_address(&treasury, &mint);
accounts.push(AccountMeta::new(shard_tokens, false));
accounts.push(AccountMeta::new(treasury_tokens, false));
accounts.push(AccountMeta::new_readonly(
token_program.unwrap_or(spl_token::ID),
false,
));
}
Instruction {
program_id: crate::ID,
accounts,
data: CollectFromShard {
index: index.to_le_bytes(),
amount: amount.to_le_bytes(),
is_sol: [if is_sol { 1 } else { 0 }],
_padding: [0; 7],
}
.to_bytes(),
}
}
pub fn update_shard_config(authority: Pubkey, use_fee_shard: u8, shard_count: u8) -> Instruction {
let (config_pda, _) = config_pda();
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new_readonly(authority, true),
AccountMeta::new(config_pda, false),
],
data: UpdateShardConfig {
use_fee_shard,
shard_count,
_padding: [0; 6],
}
.to_bytes(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn spl_sweep_uses_token_mint_as_mint_account() {
let payer = Pubkey::new_unique();
let vault = Pubkey::new_unique();
let seller = Pubkey::new_unique();
let fee_destination = Pubkey::new_unique();
let token_mint = Pubkey::new_unique();
let vault_tokens = Pubkey::new_unique();
let seller_tokens = Pubkey::new_unique();
let fee_dest_tokens = Pubkey::new_unique();
let ix = sweep(
payer,
vault,
seller,
fee_destination,
token_mint,
0,
false,
Some(vault_tokens),
Some(seller_tokens),
Some(fee_dest_tokens),
Some(spl_token::ID),
);
assert_eq!(ix.accounts[6].pubkey, token_mint);
assert_eq!(ix.accounts[7].pubkey, spl_token::ID);
}
}