kora_lib/token/
token.rs

1use super::{
2    interface::{TokenInterface, TokenState},
3    TokenType,
4};
5use async_trait::async_trait;
6use solana_program::{program_pack::Pack, pubkey::Pubkey};
7use solana_sdk::instruction::Instruction;
8use spl_associated_token_account::{
9    get_associated_token_address_with_program_id, instruction::create_associated_token_account,
10};
11use spl_token::{
12    self,
13    state::{Account as TokenAccountState, AccountState, Mint as MintState},
14};
15
16#[derive(Debug)]
17pub struct TokenAccount {
18    pub mint: Pubkey,
19    pub owner: Pubkey,
20    pub amount: u64,
21    pub delegate: Option<Pubkey>,
22    pub state: u8,
23    pub is_native: Option<u64>,
24    pub delegated_amount: u64,
25    pub close_authority: Option<Pubkey>,
26}
27
28impl TokenState for TokenAccount {
29    fn mint(&self) -> Pubkey {
30        self.mint
31    }
32    fn owner(&self) -> Pubkey {
33        self.owner
34    }
35    fn amount(&self) -> u64 {
36        self.amount
37    }
38    fn decimals(&self) -> u8 {
39        0
40    }
41    fn as_any(&self) -> &dyn std::any::Any {
42        self
43    }
44}
45
46pub struct TokenProgram {
47    token_type: TokenType,
48}
49
50impl TokenProgram {
51    pub fn new(token_type: TokenType) -> Self {
52        Self { token_type }
53    }
54
55    fn get_program_id(&self) -> Pubkey {
56        match self.token_type {
57            TokenType::Spl => spl_token::id(),
58            TokenType::Token2022 => spl_token_2022::id(),
59        }
60    }
61}
62
63#[async_trait]
64impl TokenInterface for TokenProgram {
65    fn program_id(&self) -> Pubkey {
66        self.get_program_id()
67    }
68
69    fn unpack_token_account(
70        &self,
71        data: &[u8],
72    ) -> Result<Box<dyn TokenState + Send + Sync>, Box<dyn std::error::Error + Send + Sync>> {
73        let account = TokenAccountState::unpack(data)?;
74
75        Ok(Box::new(TokenAccount {
76            mint: account.mint,
77            owner: account.owner,
78            amount: account.amount,
79            delegate: account.delegate.into(),
80            state: match account.state {
81                AccountState::Uninitialized => 0,
82                AccountState::Initialized => 1,
83                AccountState::Frozen => 2,
84            },
85            is_native: account.is_native.into(),
86            delegated_amount: account.delegated_amount,
87            close_authority: account.close_authority.into(),
88        }))
89    }
90
91    fn create_initialize_account_instruction(
92        &self,
93        account: &Pubkey,
94        mint: &Pubkey,
95        owner: &Pubkey,
96    ) -> Result<Instruction, Box<dyn std::error::Error + Send + Sync>> {
97        Ok(spl_token::instruction::initialize_account(&self.program_id(), account, mint, owner)?)
98    }
99
100    fn create_transfer_instruction(
101        &self,
102        source: &Pubkey,
103        destination: &Pubkey,
104        authority: &Pubkey,
105        amount: u64,
106    ) -> Result<Instruction, Box<dyn std::error::Error + Send + Sync>> {
107        Ok(spl_token::instruction::transfer(
108            &self.program_id(),
109            source,
110            destination,
111            authority,
112            &[],
113            amount,
114        )?)
115    }
116
117    fn create_transfer_checked_instruction(
118        &self,
119        source: &Pubkey,
120        mint: &Pubkey,
121        destination: &Pubkey,
122        authority: &Pubkey,
123        amount: u64,
124        decimals: u8,
125    ) -> Result<Instruction, Box<dyn std::error::Error + Send + Sync>> {
126        Ok(spl_token::instruction::transfer_checked(
127            &self.program_id(),
128            source,
129            mint,
130            destination,
131            authority,
132            &[],
133            amount,
134            decimals,
135        )?)
136    }
137
138    fn get_associated_token_address(&self, wallet: &Pubkey, mint: &Pubkey) -> Pubkey {
139        get_associated_token_address_with_program_id(wallet, mint, &self.program_id())
140    }
141
142    fn create_associated_token_account_instruction(
143        &self,
144        funding_account: &Pubkey,
145        wallet: &Pubkey,
146        mint: &Pubkey,
147    ) -> Instruction {
148        create_associated_token_account(funding_account, wallet, mint, &self.program_id())
149    }
150
151    fn get_mint_decimals(
152        &self,
153        mint_data: &[u8],
154    ) -> Result<u8, Box<dyn std::error::Error + Send + Sync>> {
155        Ok(MintState::unpack(mint_data)?.decimals)
156    }
157
158    fn decode_transfer_instruction(
159        &self,
160        data: &[u8],
161    ) -> Result<u64, Box<dyn std::error::Error + Send + Sync>> {
162        let instruction = spl_token::instruction::TokenInstruction::unpack(data)?;
163        match instruction {
164            spl_token::instruction::TokenInstruction::Transfer { amount } => Ok(amount),
165            spl_token::instruction::TokenInstruction::TransferChecked { amount, .. } => Ok(amount),
166            _ => Err("Not a transfer instruction".into()),
167        }
168    }
169}