diff --git a/Cargo.lock b/Cargo.lock index d40653e..b3d68b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -540,6 +540,7 @@ dependencies = [ "serde_yaml", "serum-common", "serum_dex", + "signal-hook", "simplelog", "slog-scope", "slog-stdlog", @@ -2978,6 +2979,16 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "signal-hook" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index f98ecc3..d826087 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ crossbeam = "0.8.1" crossbeam-channel = "0.5.1" crossbeam-utils = "0.8.5" crossbeam-queue = "0.3.2" +signal-hook = "0.3.9" [profile.release] lto = "fat" codegen-units = 1 diff --git a/src/crank.rs b/src/crank.rs index f951db0..33c2429 100644 --- a/src/crank.rs +++ b/src/crank.rs @@ -2,7 +2,8 @@ use crate::{ config::{Configuration, ParsedMarketKeys}, }; use anyhow::{anyhow, format_err, Result}; -use crossbeam::sync::WaitGroup; +use crossbeam::{select, sync::WaitGroup}; +use crossbeam_channel::Receiver; use crossbeam_queue::ArrayQueue; use log::{debug, error, info, warn}; use safe_transmute::{ @@ -44,7 +45,7 @@ impl Crank { pub fn new(config: Arc) -> Arc { Arc::new(Self { config }) } - pub fn start(self: &Arc) -> Result<()> { + pub fn start(self: &Arc, exit_chan: Receiver) -> Result<()> { let rpc_client = Arc::new(RpcClient::new(self.config.http_rpc_url.clone())); let payer = Arc::new(self.config.payer()); let dex_program = Pubkey::from_str(self.config.crank.dex_program.as_str()).unwrap(); @@ -52,6 +53,13 @@ impl Crank { let slot_height_map: Arc>> = Arc::new(RwLock::new(HashMap::new())); loop { + select! { + recv(exit_chan) -> _msg => { + warn!("caught exit signal"); + return Ok(()); + }, + default => {} + } let work_loop = |market_key: &ParsedMarketKeys| -> Result>> { let event_q_value_and_context = rpc_client.get_account_with_commitment( @@ -173,12 +181,10 @@ impl Crank { account_metas.push(AccountMeta::new(**pubkey, false)); } let instructions = consume_events_ix( - &rpc_client, &dex_program, &payer, account_metas, self.config.crank.events_per_worker, - &market_key.keys.event_q, )?; Ok(Some(instructions)) }; @@ -263,7 +269,7 @@ impl Crank { // so split it up let instructions_chunks = instructions.chunks(instructions.len() / 2); info!("starting chunked crank instruction processing"); - for (idx, chunk) in instructions_chunks.enumerate() { + for (_idx, chunk) in instructions_chunks.enumerate() { let res = run_loop(&chunk.to_vec()); if res.is_err() { error!( @@ -281,7 +287,7 @@ impl Crank { error!("failed to send crank instructions {:#?}", res.err()); } else { info!( - "crank ran {}. processed {} instructions for {} markets: {:#?}", + "crank ran {} processed {} instructions for {} markets: {:#?}", res.unwrap(), instructions.len(), instructions_markets.len(), @@ -320,12 +326,10 @@ impl Crank { } fn consume_events_ix( - client: &Arc, program_id: &Pubkey, payer: &Keypair, account_metas: Vec, to_consume: usize, - event_q: &Pubkey, ) -> Result> { let instruction_data: Vec = MarketInstruction::ConsumeEvents(to_consume as u16).pack(); let instruction = Instruction { @@ -447,42 +451,3 @@ pub struct MarketPubkeys { pub pc_vault: Box, pub vault_signer_key: Box, } - -fn consume_events_once( - client: &Arc, - program_id: &Pubkey, - payer: &Keypair, - account_metas: Vec, - to_consume: usize, - event_q: &Pubkey, -) -> Result { - let _start = std::time::Instant::now(); - let instruction_data: Vec = MarketInstruction::ConsumeEvents(to_consume as u16).pack(); - let instruction = Instruction { - program_id: *program_id, - accounts: account_metas, - data: instruction_data, - }; - let random_instruction = solana_sdk::system_instruction::transfer( - &payer.pubkey(), - &payer.pubkey(), - rand::random::() % 10000 + 1, - ); - let (recent_hash, _fee_calc) = client.get_recent_blockhash()?; - let txn = Transaction::new_signed_with_payer( - &[instruction, random_instruction], - Some(&payer.pubkey()), - &[payer], - recent_hash, - ); - - info!("Consuming events ..."); - let signature = client.send_transaction_with_config( - &txn, - RpcSendTransactionConfig { - skip_preflight: true, - ..RpcSendTransactionConfig::default() - }, - )?; - Ok(signature) -} diff --git a/src/main.rs b/src/main.rs index 076b76f..9cf3f90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,15 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; use std::sync::Arc; - +use crossbeam::sync::WaitGroup; +use crossbeam_channel; use anyhow::{anyhow, Result}; use clap::{App, Arg, SubCommand}; use log::{error, info, warn}; +use signal_hook::{ + consts::{SIGINT, SIGQUIT, SIGTERM}, + iterator::Signals, +}; pub mod config; pub mod crank; @@ -55,8 +60,31 @@ async fn process_matches<'a>( false, )?); cfg.init_log(false)?; - let crank_turner = crank::Crank::new(cfg); - crank_turner.start()?; + let mut signals = + Signals::new(vec![SIGINT, SIGTERM, SIGQUIT]).expect("failed to registers signals"); + let (s, r) = crossbeam_channel::unbounded(); + let wg = WaitGroup::new(); + { + let wg = wg.clone(); + tokio::task::spawn(async move { + let crank_turner = crank::Crank::new(cfg); + let res = crank_turner.start(r); + if res.is_err() { + error!("encountered error while turning crank {:#?}", res.err()); + } + drop(wg); + }); + } + for signal in signals.forever() { + warn!("encountered exit signal {}", signal); + break; + } + let err = s.send(true); + if err.is_err() { + error!("failed to send exit notif {:#?}", err.err()); + return Err(anyhow!("unexpected error during shutdown, failed to send exit notifications").into()); + } + wg.wait() } _ => return Err(anyhow!("failed to match subcommand")), } diff --git a/src/token_instruction.rs b/src/token_instruction.rs deleted file mode 100644 index 9e566ea..0000000 --- a/src/token_instruction.rs +++ /dev/null @@ -1,614 +0,0 @@ -/* -Copyright 2020 Solana Foundation. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#![allow(unused)] -//! Instruction types - -use solana_sdk::{ - instruction::{AccountMeta, Instruction}, - program_error::ProgramError, - pubkey::Pubkey, -}; -use std::mem::size_of; - -/* CUSTOM ERRORS - 0: invalid instruction - 1: owner required if no initial supply -*/ - -/// Minimum number of multisignature signers (min N) -pub const MIN_SIGNERS: usize = 1; -/// Maximum number of multisignature signers (max N) -pub const MAX_SIGNERS: usize = 11; - -/// Instructions supported by the token program. -#[repr(C)] -#[derive(Clone, Debug, PartialEq)] -pub enum TokenInstruction { - /// Initializes a new mint and optionally deposits all the newly minted tokens in an account. - /// - /// The `InitializeMint` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable]` The mint to initialize. - /// 1. - /// * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. - /// * If supply is zero: `[]` The owner/multisignature of the mint. - /// 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if - /// present then further minting is supported. - /// - InitializeMint { - /// Initial amount of tokens to mint. - amount: u64, - /// Number of base 10 digits to the right of the decimal place. - decimals: u8, - }, - /// Initializes a new account to hold tokens. If this account is associated with the native mint - /// then the token balance of the initialized account will be equal to the amount of SOL in the account. - /// - /// The `InitializeAccount` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable]` The account to initialize. - /// 1. `[]` The mint this account will be associated with. - /// 2. `[]` The new account's owner/multisignature. - InitializeAccount, - /// Initializes a multisignature account with N provided signers. - /// - /// Multisignature accounts can used in place of any single owner/delegate accounts in any - /// token instruction that require an owner/delegate to be present. The variant field represents the - /// number of signers (M) required to validate this multisignature account. - /// - /// The `InitializeMultisig` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable]` The multisignature account to initialize. - /// 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. - InitializeMultisig { - /// The number of signers (M) required to validate this multisignature account. - m: u8, - }, - /// Transfers tokens from one account to another either directly or via a delegate. If this - /// account is associated with the native mint then equal amounts of SOL and Tokens will be - /// transferred to the destination account. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner/delegate - /// 0. `[writable]` The source account. - /// 1. `[writable]` The destination account. - /// 2. '[signer]' The source account's owner/delegate. - /// - /// * Multisignature owner/delegate - /// 0. `[writable]` The source account. - /// 1. `[writable]` The destination account. - /// 2. '[]' The source account's multisignature owner/delegate. - /// 3. ..3+M '[signer]' M signer accounts. - Transfer { - /// The amount of tokens to transfer. - amount: u64, - }, - /// Approves a delegate. A delegate is given the authority over - /// tokens on behalf of the source account's owner. - - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The source account. - /// 1. `[]` The delegate. - /// 2. `[signer]` The source account owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The source account. - /// 1. `[]` The delegate. - /// 2. '[]' The source account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts - Approve { - /// The amount of tokens the delegate is approved for. - amount: u64, - }, - /// Revokes the delegate's authority. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The source account. - /// 1. `[signer]` The source account owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The source account. - /// 1. '[]' The source account's multisignature owner. - /// 2. ..2+M '[signer]' M signer accounts - Revoke, - /// Sets a new owner of a mint or account. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The mint or account to change the owner of. - /// 1. `[]` The new owner/delegate/multisignature. - /// 2. `[signer]` The owner of the mint or account. - /// - /// * Multisignature owner - /// 0. `[writable]` The mint or account to change the owner of. - /// 1. `[]` The new owner/delegate/multisignature. - /// 2. `[]` The mint's or account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts - SetOwner, - /// Mints new tokens to an account. The native mint does not support minting. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The mint. - /// 1. `[writable]` The account to mint tokens to. - /// 2. `[signer]` The mint's owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The mint. - /// 1. `[writable]` The account to mint tokens to. - /// 2. `[]` The mint's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts. - MintTo { - /// The amount of new tokens to mint. - amount: u64, - }, - /// Burns tokens by removing them from an account. `Burn` does not support accounts - /// associated with the native mint, use `CloseAccount` instead. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner/delegate - /// 0. `[writable]` The account to burn from. - /// 1. `[signer]` The account's owner/delegate. - /// - /// * Multisignature owner/delegate - /// 0. `[writable]` The account to burn from. - /// 1. `[]` The account's multisignature owner/delegate. - /// 2. ..2+M '[signer]' M signer accounts. - Burn { - /// The amount of tokens to burn. - amount: u64, - }, - /// Close an account by transferring all its SOL to the destination account. - /// Non-native accounts may only be closed if its token amount is zero. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The account to close. - /// 1. '[writable]' The destination account. - /// 2. `[signer]` The account's owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The account to close. - /// 1. '[writable]' The destination account. - /// 2. `[]` The account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts. - CloseAccount, -} -impl TokenInstruction { - /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). - pub fn unpack(input: &[u8]) -> Result { - if input.len() < size_of::() { - return Err(ProgramError::Custom(0)); - } - Ok(match input[0] { - 0 => { - if input.len() < size_of::() + size_of::() + size_of::() { - return Err(ProgramError::Custom(1)); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - let decimals = - unsafe { *(&input[size_of::() + size_of::()] as *const u8) }; - Self::InitializeMint { amount, decimals } - } - 1 => Self::InitializeAccount, - 2 => { - if input.len() < size_of::() + size_of::() { - return Err(ProgramError::Custom(2)); - } - #[allow(clippy::cast_ptr_alignment)] - let m = unsafe { *(&input[1] as *const u8) }; - Self::InitializeMultisig { m } - } - 3 => { - if input.len() < size_of::() + size_of::() { - return Err(ProgramError::Custom(3)); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Transfer { amount } - } - 4 => { - if input.len() < size_of::() + size_of::() { - return Err(ProgramError::Custom(4)); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Approve { amount } - } - 5 => Self::Revoke, - 6 => Self::SetOwner, - 7 => { - if input.len() < size_of::() + size_of::() { - return Err(ProgramError::Custom(5)); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::MintTo { amount } - } - 8 => { - if input.len() < size_of::() + size_of::() { - return Err(ProgramError::Custom(6)); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Burn { amount } - } - 9 => Self::CloseAccount, - _ => return Err(ProgramError::Custom(7)), - }) - } - - /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer. - pub fn pack(self: &Self) -> Result, ProgramError> { - let mut output = vec![0u8; size_of::()]; - match self { - Self::InitializeMint { amount, decimals } => { - output[0] = 0; - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; - *value = *amount; - let value = - unsafe { &mut *(&mut output[size_of::() + size_of::()] as *mut u8) }; - *value = *decimals; - } - Self::InitializeAccount => output[0] = 1, - Self::InitializeMultisig { m } => { - output[0] = 2; - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u8) }; - *value = *m; - } - Self::Transfer { amount } => { - output[0] = 3; - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; - *value = *amount; - } - Self::Approve { amount } => { - output[0] = 4; - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; - *value = *amount; - } - Self::Revoke => output[0] = 5, - Self::SetOwner => output[0] = 6, - Self::MintTo { amount } => { - output[0] = 7; - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; - *value = *amount; - } - Self::Burn { amount } => { - output[0] = 8; - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; - *value = *amount; - } - Self::CloseAccount => output[0] = 9, - } - Ok(output) - } -} - -/// Creates a 'InitializeMint' instruction. -pub fn initialize_mint( - token_program_id: &Pubkey, - mint_pubkey: &Pubkey, - account_pubkey: Option<&Pubkey>, - owner_pubkey: Option<&Pubkey>, - amount: u64, - decimals: u8, -) -> Result { - let data = TokenInstruction::InitializeMint { amount, decimals }.pack()?; - - let mut accounts = vec![AccountMeta::new(*mint_pubkey, false)]; - if amount != 0 { - match account_pubkey { - Some(pubkey) => accounts.push(AccountMeta::new(*pubkey, false)), - None => { - return Err(ProgramError::NotEnoughAccountKeys); - } - } - } - match owner_pubkey { - Some(pubkey) => accounts.push(AccountMeta::new_readonly(*pubkey, false)), - None => { - if amount == 0 { - return Err(ProgramError::Custom(8)); - } - } - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `InitializeAccount` instruction. -pub fn initialize_account( - token_program_id: &Pubkey, - account_pubkey: &Pubkey, - mint_pubkey: &Pubkey, - owner_pubkey: &Pubkey, -) -> Result { - let data = TokenInstruction::InitializeAccount.pack()?; - - let accounts = vec![ - AccountMeta::new(*account_pubkey, false), - AccountMeta::new_readonly(*mint_pubkey, false), - AccountMeta::new_readonly(*owner_pubkey, false), - ]; - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `InitializeMultisig` instruction. -pub fn initialize_multisig( - token_program_id: &Pubkey, - multisig_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - m: u8, -) -> Result { - if !is_valid_signer_index(m as usize) - || !is_valid_signer_index(signer_pubkeys.len()) - || m as usize > signer_pubkeys.len() - { - return Err(ProgramError::MissingRequiredSignature); - } - let data = TokenInstruction::InitializeMultisig { m }.pack()?; - - let mut accounts = Vec::with_capacity(1 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*multisig_pubkey, false)); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new_readonly(**signer_pubkey, false)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `Transfer` instruction. -pub fn transfer( - token_program_id: &Pubkey, - source_pubkey: &Pubkey, - destination_pubkey: &Pubkey, - authority_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::Transfer { amount }.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*source_pubkey, false)); - accounts.push(AccountMeta::new(*destination_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *authority_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates an `Approve` instruction. -pub fn approve( - token_program_id: &Pubkey, - source_pubkey: &Pubkey, - delegate_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::Approve { amount }.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new_readonly(*source_pubkey, false)); - accounts.push(AccountMeta::new(*delegate_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates an `Approve` instruction. -pub fn revoke( - token_program_id: &Pubkey, - source_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], -) -> Result { - let data = TokenInstruction::Revoke.pack()?; - - let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); - accounts.push(AccountMeta::new_readonly(*source_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates an `SetOwner` instruction. -pub fn set_owner( - token_program_id: &Pubkey, - owned_pubkey: &Pubkey, - new_owner_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], -) -> Result { - let data = TokenInstruction::SetOwner.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*owned_pubkey, false)); - accounts.push(AccountMeta::new_readonly(*new_owner_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates an `MintTo` instruction. -pub fn mint_to( - token_program_id: &Pubkey, - mint_pubkey: &Pubkey, - account_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::MintTo { amount }.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*mint_pubkey, false)); - accounts.push(AccountMeta::new(*account_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates an `Burn` instruction. -pub fn burn( - token_program_id: &Pubkey, - account_pubkey: &Pubkey, - authority_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::Burn { amount }.pack()?; - - let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*account_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *authority_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates an `CloseAccount` instruction. -pub fn close_account( - token_program_id: &Pubkey, - account_pubkey: &Pubkey, - dest_pubkey: &Pubkey, - authority_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], -) -> Result { - let data = TokenInstruction::CloseAccount.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*account_pubkey, false)); - accounts.push(AccountMeta::new(*dest_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *authority_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS -pub fn is_valid_signer_index(index: usize) -> bool { - !(index < MIN_SIGNERS || index > MAX_SIGNERS) -}