nautilus_blockchain/contracts/
erc20.rs1use std::sync::Arc;
17
18use alloy::{primitives::Address, 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: &Address,
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(
98 &self,
99 token_address: &Address,
100 ) -> Result<String, BlockchainRpcClientError> {
101 let name_call = ERC20::nameCall.abi_encode();
102 let rpc_request = self
103 .client
104 .construct_eth_call(&token_address.to_string(), name_call.as_slice());
105 let encoded_name = self
106 .client
107 .execute_eth_call::<String>(rpc_request)
108 .await
109 .map_err(|e| {
110 BlockchainRpcClientError::ClientError(format!("Error fetching name: {e}"))
111 })?;
112 let bytes = decode_hex_response(&encoded_name)?;
113 ERC20::nameCall::abi_decode_returns(&bytes).map_err(|e| {
114 BlockchainRpcClientError::AbiDecodingError(format!(
115 "Error decoding ERC20 contract name with error {e}"
116 ))
117 })
118 }
119
120 async fn fetch_symbol(
122 &self,
123 token_address: &Address,
124 ) -> Result<String, BlockchainRpcClientError> {
125 let symbol_call = ERC20::symbolCall.abi_encode();
126 let rpc_request = self
127 .client
128 .construct_eth_call(&token_address.to_string(), symbol_call.as_slice());
129 let encoded_symbol = self
130 .client
131 .execute_eth_call::<String>(rpc_request)
132 .await
133 .map_err(|e| {
134 BlockchainRpcClientError::ClientError(format!("Error fetching symbol: {e}"))
135 })?;
136 let bytes = decode_hex_response(&encoded_symbol)?;
137 ERC20::symbolCall::abi_decode_returns(&bytes).map_err(|e| {
138 BlockchainRpcClientError::AbiDecodingError(format!(
139 "Error decoding ERC20 contract symbol with error {e}"
140 ))
141 })
142 }
143
144 async fn fetch_decimals(
146 &self,
147 token_address: &Address,
148 ) -> Result<u8, BlockchainRpcClientError> {
149 let decimals_call = ERC20::decimalsCall.abi_encode();
150 let rpc_request = self
151 .client
152 .construct_eth_call(&token_address.to_string(), decimals_call.as_slice());
153 let encoded_decimals = self
154 .client
155 .execute_eth_call::<String>(rpc_request)
156 .await
157 .map_err(|e| {
158 BlockchainRpcClientError::ClientError(format!("Error fetching decimals: {e}"))
159 })?;
160 let bytes = decode_hex_response(&encoded_decimals)?;
161 ERC20::decimalsCall::abi_decode_returns(&bytes).map_err(|e| {
162 BlockchainRpcClientError::AbiDecodingError(format!(
163 "Error decoding ERC20 contract decimals with error {e}"
164 ))
165 })
166 }
167}