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
26pub struct ERC20Service {
28 evm: Arc<Evm>,
29}
30
31impl ERC20Service {
32 pub fn new(evm: Arc<Evm>) -> Self {
33 Self { evm }
34 }
35
36 fn erc20(&self, token_address: Address) -> IERC20<Provider<ethers::providers::Http>> {
38 IERC20::new(token_address, self.evm.client.provider.clone())
39 }
40
41 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 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 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 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 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 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 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#[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}