Skip to main content

evm_sdk/erc/
erc20.rs

1use crate::{Evm, EvmClient, EvmError};
2use ethers::{
3    contract::abigen,
4    providers::Provider,
5    types::{Address, H256, U256},
6};
7use std::sync::Arc;
8
9abigen!(
10    IERC20,
11    r#"[
12        function totalSupply() external view returns (uint256)
13        function balanceOf(address account) external view returns (uint256)
14        function transfer(address to, uint256 amount) external returns (bool)
15        function allowance(address owner, address spender) external view returns (uint256)
16        function approve(address spender, uint256 amount) external returns (bool)
17        function transferFrom(address from, address to, uint256 amount) external returns (bool)
18        function decimals() external view returns (uint8)  
19        function name() external view returns (string)    
20        function symbol() external view returns (string)   
21        event Transfer(address indexed from, address indexed to, uint256 value)
22        event Approval(address indexed owner, address indexed spender, uint256 value)
23    ]"#
24);
25
26/// ERC20 Service for interacting with ERC20 tokens
27pub struct ERC20Service {
28    evm: Arc<Evm>,
29}
30
31impl ERC20Service {
32    pub fn new(evm: Arc<Evm>) -> Self {
33        Self { evm }
34    }
35
36    /// Create ERC20 token instance
37    fn erc20(&self, token_address: Address) -> IERC20<Provider<ethers::providers::Http>> {
38        IERC20::new(token_address, self.evm.client.provider.clone())
39    }
40
41    /// Get ERC20 token balance
42    pub async fn get_balance(
43        &self,
44        token_address: Address,
45        owner: Address,
46    ) -> Result<U256, EvmError> {
47        let erc20 = self.erc20(token_address);
48        erc20
49            .balance_of(owner)
50            .call()
51            .await
52            .map_err(|e| EvmError::ContractError(format!("Failed to get ERC20 balance: {}", e)))
53    }
54
55    /// Get ERC20 token total supply
56    pub async fn get_total_supply(&self, token_address: Address) -> Result<U256, EvmError> {
57        let erc20 = self.erc20(token_address);
58        erc20.total_supply().call().await.map_err(|e| {
59            EvmError::ContractError(format!("Failed to get ERC20 total supply: {}", e))
60        })
61    }
62
63    /// Transfer ERC20 tokens
64    pub async fn transfer(
65        &self,
66        token_address: Address,
67        to: Address,
68        amount: U256,
69    ) -> Result<H256, EvmError> {
70        if self.evm.client.wallet.is_none() {
71            return Err(EvmError::WalletError("No wallet configured".to_string()));
72        }
73        let erc20 = self.erc20(token_address);
74        let tx = erc20.transfer(to, amount);
75        let pending_tx = tx
76            .send()
77            .await
78            .map_err(|e| EvmError::TransactionError(format!("Failed to transfer ERC20: {}", e)))?;
79        Ok(pending_tx.tx_hash())
80    }
81
82    /// Get ERC20 token allowance
83    pub async fn get_allowance(
84        &self,
85        token_address: Address,
86        owner: Address,
87        spender: Address,
88    ) -> Result<U256, EvmError> {
89        let erc20 = self.erc20(token_address);
90        erc20
91            .allowance(owner, spender)
92            .call()
93            .await
94            .map_err(|e| EvmError::ContractError(format!("Failed to get allowance: {}", e)))
95    }
96
97    /// Approve spender to spend tokens
98    pub async fn approve(
99        &self,
100        token_address: Address,
101        spender: Address,
102        amount: U256,
103    ) -> Result<H256, EvmError> {
104        if self.evm.client.wallet.is_none() {
105            return Err(EvmError::WalletError("No wallet configured".to_string()));
106        }
107        let erc20 = self.erc20(token_address);
108        let tx = erc20.approve(spender, amount);
109        let pending_tx = tx
110            .send()
111            .await
112            .map_err(|e| EvmError::TransactionError(format!("Failed to approve: {}", e)))?;
113        Ok(pending_tx.tx_hash())
114    }
115
116    /// Transfer from (requires allowance)
117    pub async fn transfer_from(
118        &self,
119        token_address: Address,
120        from: Address,
121        to: Address,
122        amount: U256,
123    ) -> Result<H256, EvmError> {
124        if self.evm.client.wallet.is_none() {
125            return Err(EvmError::WalletError("No wallet configured".to_string()));
126        }
127        let erc20 = self.erc20(token_address);
128        let tx = erc20.transfer_from(from, to, amount);
129        let pending_tx = tx
130            .send()
131            .await
132            .map_err(|e| EvmError::TransactionError(format!("Failed to transfer from: {}", e)))?;
133        Ok(pending_tx.tx_hash())
134    }
135
136    /// Get ERC20 token decimals
137    pub async fn get_decimals(&self, token_address: Address) -> Result<u8, EvmError> {
138        let erc20 = self.erc20(token_address);
139        erc20
140            .decimals()
141            .call()
142            .await
143            .map_err(|e| EvmError::ContractError(format!("Failed to get ERC20 decimals: {}", e)))
144    }
145}
146
147/// ERC20 Token Metadata
148#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
149pub struct ERCTokenMetadata {
150    pub address: Address,
151    pub name: String,
152    pub symbol: String,
153    pub decimals: u8,
154}