nautilus_blockchain/contracts/
erc20.rs1use std::sync::Arc;
17
18use alloy::{sol, sol_types::SolCall};
19
20use crate::rpc::{error::BlockchainRpcClientError, http::BlockchainHttpRpcClient};
21
22sol! {
23 #[sol(rpc)]
24 contract ERC20 {
25 function name() external view returns (string);
26 function symbol() external view returns (string);
27 function decimals() external view returns (uint8);
28 }
29}
30
31#[derive(Debug, Clone)]
33pub struct TokenInfo {
34 pub name: String,
36 pub symbol: String,
38 pub decimals: u8,
40}
41
42#[derive(Debug)]
47pub struct Erc20Contract {
48 client: Arc<BlockchainHttpRpcClient>,
50}
51
52fn decode_hex_response(encoded_response: &str) -> Result<Vec<u8>, BlockchainRpcClientError> {
58 let encoded_str = encoded_response
60 .strip_prefix("0x")
61 .unwrap_or(encoded_response);
62 hex::decode(encoded_str).map_err(|e| {
63 BlockchainRpcClientError::AbiDecodingError(format!("Error decoding hex response: {e}"))
64 })
65}
66
67impl Erc20Contract {
68 #[must_use]
70 pub const fn new(client: Arc<BlockchainHttpRpcClient>) -> Self {
71 Self { client }
72 }
73
74 pub async fn fetch_token_info(
82 &self,
83 token_address: &str,
84 ) -> Result<TokenInfo, BlockchainRpcClientError> {
85 let token_name = self.fetch_name(token_address).await?;
86 let token_symbol = self.fetch_symbol(token_address).await?;
87 let token_decimals = self.fetch_decimals(token_address).await?;
88
89 Ok(TokenInfo {
90 name: token_name,
91 symbol: token_symbol,
92 decimals: token_decimals,
93 })
94 }
95
96 async fn fetch_name(&self, token_address: &str) -> Result<String, BlockchainRpcClientError> {
98 let name_call = ERC20::nameCall.abi_encode();
99 let rpc_request = self
100 .client
101 .construct_eth_call(token_address, name_call.as_slice());
102 let encoded_name = self
103 .client
104 .execute_eth_call::<String>(rpc_request)
105 .await
106 .map_err(|e| {
107 BlockchainRpcClientError::ClientError(format!("Error fetching name: {e}"))
108 })?;
109 let bytes = decode_hex_response(&encoded_name)?;
110 ERC20::nameCall::abi_decode_returns(&bytes).map_err(|e| {
111 BlockchainRpcClientError::AbiDecodingError(format!(
112 "Error decoding ERC20 contract name with error {e}"
113 ))
114 })
115 }
116
117 async fn fetch_symbol(&self, token_address: &str) -> Result<String, BlockchainRpcClientError> {
119 let symbol_call = ERC20::symbolCall.abi_encode();
120 let rpc_request = self
121 .client
122 .construct_eth_call(token_address, symbol_call.as_slice());
123 let encoded_symbol = self
124 .client
125 .execute_eth_call::<String>(rpc_request)
126 .await
127 .map_err(|e| {
128 BlockchainRpcClientError::ClientError(format!("Error fetching symbol: {e}"))
129 })?;
130 let bytes = decode_hex_response(&encoded_symbol)?;
131 ERC20::symbolCall::abi_decode_returns(&bytes).map_err(|e| {
132 BlockchainRpcClientError::AbiDecodingError(format!(
133 "Error decoding ERC20 contract symbol with error {e}"
134 ))
135 })
136 }
137
138 async fn fetch_decimals(&self, token_address: &str) -> Result<u8, BlockchainRpcClientError> {
140 let decimals_call = ERC20::decimalsCall.abi_encode();
141 let rpc_request = self
142 .client
143 .construct_eth_call(token_address, decimals_call.as_slice());
144 let encoded_decimals = self
145 .client
146 .execute_eth_call::<String>(rpc_request)
147 .await
148 .map_err(|e| {
149 BlockchainRpcClientError::ClientError(format!("Error fetching decimals: {e}"))
150 })?;
151 let bytes = decode_hex_response(&encoded_decimals)?;
152 ERC20::decimalsCall::abi_decode_returns(&bytes).map_err(|e| {
153 BlockchainRpcClientError::AbiDecodingError(format!(
154 "Error decoding ERC20 contract decimals with error {e}"
155 ))
156 })
157 }
158}