Skip to main content

hpsvm_token/
transfer_checked.rs

1use hpsvm::{HPSVM, types::FailedTransactionMetadata};
2use smallvec::{SmallVec, smallvec};
3use solana_address::Address;
4use solana_keypair::Keypair;
5use solana_signer::{Signer, signers::Signers};
6
7use super::{
8    TOKEN_ID, get_multisig_signers, get_spl_account,
9    spl_token::{instruction::transfer_checked, state::Mint},
10};
11
12/// ### Description
13/// Builder for the [`transfer_checked`] instruction.
14///
15/// ### Optional fields
16/// - `source`: associated token account of the `owner` by default.
17/// - `owner`: `payer` by default.
18/// - `decimals`: `mint` decimals by default.
19/// - `token_program_id`: [`TOKEN_ID`] by default.
20#[derive(Debug)]
21pub struct TransferChecked<'a> {
22    svm: &'a mut HPSVM,
23    payer: &'a Keypair,
24    mint: &'a Address,
25    source: Option<&'a Address>,
26    destination: &'a Address,
27    token_program_id: Option<&'a Address>,
28    amount: u64,
29    decimals: Option<u8>,
30    signers: SmallVec<[&'a Keypair; 1]>,
31    owner: Option<Address>,
32}
33
34impl<'a> TransferChecked<'a> {
35    /// Creates a new instance of [`transfer_checked`] instruction.
36    pub fn new(
37        svm: &'a mut HPSVM,
38        payer: &'a Keypair,
39        mint: &'a Address,
40        destination: &'a Address,
41        amount: u64,
42    ) -> Self {
43        TransferChecked {
44            svm,
45            payer,
46            mint,
47            source: None,
48            destination,
49            token_program_id: None,
50            amount,
51            decimals: None,
52            owner: None,
53            signers: smallvec![payer],
54        }
55    }
56
57    /// Sets the token program id for the instruction.
58    pub fn token_program_id(mut self, program_id: &'a Address) -> Self {
59        self.token_program_id = Some(program_id);
60        self
61    }
62
63    /// Sets the decimals of the transfer.
64    pub fn decimals(mut self, value: u8) -> Self {
65        self.decimals = Some(value);
66        self
67    }
68
69    /// Sets the token account source.
70    pub fn source(mut self, source: &'a Address) -> Self {
71        self.source = Some(source);
72        self
73    }
74
75    /// Sets the owner of the account with single owner.
76    pub fn owner(mut self, owner: &'a Keypair) -> Self {
77        self.owner = Some(owner.pubkey());
78        self.signers = smallvec![owner];
79        self
80    }
81
82    /// Sets the owner of the account with multisig owner.
83    pub fn multisig(mut self, multisig: &'a Address, signers: &'a [&'a Keypair]) -> Self {
84        self.owner = Some(*multisig);
85        self.signers = SmallVec::from(signers);
86        self
87    }
88
89    /// Sends the transaction.
90    pub fn send(self) -> Result<(), FailedTransactionMetadata> {
91        let payer_pk = self.payer.pubkey();
92        let token_program_id = self.token_program_id.unwrap_or(&TOKEN_ID);
93
94        let authority = self.owner.unwrap_or(payer_pk);
95        let signing_keys = self.signers.pubkeys();
96        let signer_keys = get_multisig_signers(&authority, &signing_keys);
97
98        let source_pk = if let Some(source) = self.source {
99            *source
100        } else {
101            spl_associated_token_account_interface::address::get_associated_token_address_with_program_id(
102                &authority,
103                self.mint,
104                token_program_id,
105            )
106        };
107
108        let mint: Mint = get_spl_account(self.svm, self.mint)?;
109        let ix = transfer_checked(
110            token_program_id,
111            &source_pk,
112            self.mint,
113            self.destination,
114            &authority,
115            &signer_keys,
116            self.amount,
117            self.decimals.unwrap_or(mint.decimals),
118        )?;
119
120        super::sign_and_send(self.svm, self.payer, &self.signers, ix)
121    }
122}