1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
use solana_sdk::{
    program_pack::Pack,
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    system_instruction,
    transaction::Transaction,
};
use spl_token::state::{Account as TokenAccount, Mint};

use super::client::{Client, ClientResult};

pub trait SplToken {
    fn create_token_mint(&self, owner: &Pubkey, decimals: u8) -> ClientResult<Keypair>;
    fn create_token_account(&self, owner: &Pubkey, token_mint: &Pubkey) -> ClientResult<Keypair>;
    fn create_token_account_with_lamports(
        &self,
        owner: &Pubkey,
        token_mint: &Pubkey,
        lamports: u64,
    ) -> ClientResult<Keypair>;
    fn mint_to(
        &self,
        owner: &Keypair,
        token_mint: &Pubkey,
        account: &Pubkey,
        amount: u64,
        decimals: u8,
    ) -> ClientResult<()>;
    fn transfer_to(
        &self,
        owner: &Keypair,
        token_mint: &Pubkey,
        source: &Pubkey,
        destination: &Pubkey,
        amount: u64,
        decimals: u8,
    ) -> ClientResult<()>;
    fn get_associated_token_address(wallet_address: &Pubkey, token_mint: &Pubkey) -> Pubkey;
    fn create_associated_token_account(
        &self,
        funder: &Keypair,
        recipient: &Pubkey,
        token_mint: &Pubkey,
    ) -> ClientResult<Pubkey>;
    fn create_associated_token_account_by_payer(&self, recipient: &Pubkey, token_mint: &Pubkey)
        -> ClientResult<Pubkey>;
    fn close_token_account(&self, owner: &Keypair, account: &Pubkey, destination: &Pubkey) -> ClientResult<()>;
}

impl SplToken for Client {
    fn create_token_mint(&self, owner: &Pubkey, decimals: u8) -> ClientResult<Keypair> {
        let token_mint = Keypair::new();

        let mut transaction = Transaction::new_with_payer(
            &[
                system_instruction::create_account(
                    &self.payer_pubkey(),
                    &token_mint.pubkey(),
                    self.get_minimum_balance_for_rent_exemption(Mint::LEN)?,
                    Mint::LEN as u64,
                    &spl_token::id(),
                ),
                spl_token::instruction::initialize_mint(&spl_token::id(), &token_mint.pubkey(), owner, None, decimals)?,
            ],
            Some(&self.payer_pubkey()),
        );
        transaction.sign(&[self.payer(), &token_mint], self.latest_blockhash()?);
        self.process_transaction(&transaction)?;

        Ok(token_mint)
    }

    fn create_token_account(&self, owner: &Pubkey, token_mint: &Pubkey) -> ClientResult<Keypair> {
        self.create_token_account_with_lamports(
            owner,
            token_mint,
            self.get_minimum_balance_for_rent_exemption(TokenAccount::LEN)?,
        )
    }

    fn create_token_account_with_lamports(
        &self,
        owner: &Pubkey,
        token_mint: &Pubkey,
        lamports: u64,
    ) -> ClientResult<Keypair> {
        let token_account = Keypair::new();

        let mut transaction = Transaction::new_with_payer(
            &[
                system_instruction::create_account(
                    &self.payer_pubkey(),
                    &token_account.pubkey(),
                    lamports,
                    TokenAccount::LEN as u64,
                    &spl_token::id(),
                ),
                spl_token::instruction::initialize_account(
                    &spl_token::id(),
                    &token_account.pubkey(),
                    token_mint,
                    owner,
                )?,
            ],
            Some(&self.payer_pubkey()),
        );
        transaction.sign(&[self.payer(), &token_account], self.latest_blockhash()?);
        self.process_transaction(&transaction)?;

        Ok(token_account)
    }

    fn mint_to(
        &self,
        owner: &Keypair,
        token_mint: &Pubkey,
        account: &Pubkey,
        amount: u64,
        decimals: u8,
    ) -> ClientResult<()> {
        let mut transaction = Transaction::new_with_payer(
            &[spl_token::instruction::mint_to_checked(
                &spl_token::id(),
                token_mint,
                account,
                &owner.pubkey(),
                &[],
                amount,
                decimals,
            )?],
            Some(&self.payer_pubkey()),
        );
        transaction.sign(&[self.payer(), owner], self.latest_blockhash()?);
        self.process_transaction(&transaction)
    }

    fn transfer_to(
        &self,
        authority: &Keypair,
        token_mint: &Pubkey,
        source: &Pubkey,
        destination: &Pubkey,
        amount: u64,
        decimals: u8,
    ) -> ClientResult<()> {
        let mut transaction = Transaction::new_with_payer(
            &[spl_token::instruction::transfer_checked(
                &spl_token::id(),
                source,
                token_mint,
                destination,
                &authority.pubkey(),
                &[],
                amount,
                decimals,
            )?],
            Some(&self.payer_pubkey()),
        );
        transaction.sign(&[self.payer(), authority], self.latest_blockhash()?);
        self.process_transaction(&transaction)
    }

    fn get_associated_token_address(wallet_address: &Pubkey, token_mint: &Pubkey) -> Pubkey {
        spl_associated_token_account::get_associated_token_address(wallet_address, token_mint)
    }

    fn create_associated_token_account(
        &self,
        funder: &Keypair,
        recipient: &Pubkey,
        token_mint: &Pubkey,
    ) -> ClientResult<Pubkey> {
        let mut transaction = Transaction::new_with_payer(
            &[spl_associated_token_account::create_associated_token_account(
                &funder.pubkey(),
                recipient,
                token_mint,
            )],
            Some(&self.payer_pubkey()),
        );
        if funder.pubkey() == self.payer_pubkey() {
            transaction.sign(&[self.payer()], self.latest_blockhash()?);
        } else {
            transaction.sign(&[self.payer(), funder], self.latest_blockhash()?);
        };
        self.process_transaction(&transaction)?;

        Ok(Self::get_associated_token_address(recipient, token_mint))
    }

    fn create_associated_token_account_by_payer(
        &self,
        recipient: &Pubkey,
        token_mint: &Pubkey,
    ) -> ClientResult<Pubkey> {
        self.create_associated_token_account(self.payer(), recipient, token_mint)
    }

    fn close_token_account(&self, owner: &Keypair, account: &Pubkey, destination: &Pubkey) -> ClientResult<()> {
        let mut transaction = Transaction::new_with_payer(
            &[spl_token::instruction::close_account(
                &spl_token::id(),
                account,
                destination,
                &owner.pubkey(),
                &[],
            )?],
            Some(&self.payer_pubkey()),
        );
        transaction.sign(&[self.payer(), owner], self.latest_blockhash()?);
        self.process_transaction(&transaction)
    }
}