sal_vault/ethereum/
contract.rs1use ethers::abi::{Abi, Token};
6use ethers::prelude::*;
7use serde::{Deserialize, Serialize};
8use std::str::FromStr;
9use std::sync::Arc;
10
11use super::networks::NetworkConfig;
12use super::wallet::EthereumWallet;
13use crate::error::CryptoError;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Contract {
18 pub address: Address,
20 pub abi: Abi,
22 pub network: NetworkConfig,
24}
25
26impl Contract {
27 pub fn new(address: Address, abi: Abi, network: NetworkConfig) -> Self {
29 Contract {
30 address,
31 abi,
32 network,
33 }
34 }
35
36 pub fn from_address_string(
38 address_str: &str,
39 abi: Abi,
40 network: NetworkConfig,
41 ) -> Result<Self, CryptoError> {
42 let address = Address::from_str(address_str)
43 .map_err(|e| CryptoError::InvalidAddress(format!("Invalid address format: {}", e)))?;
44
45 Ok(Contract::new(address, abi, network))
46 }
47
48 pub fn create_ethers_contract(
50 &self,
51 provider: Provider<Http>,
52 _wallet: Option<&EthereumWallet>,
53 ) -> Result<ethers::contract::Contract<ethers::providers::Provider<Http>>, CryptoError> {
54 let contract =
55 ethers::contract::Contract::new(self.address, self.abi.clone(), Arc::new(provider));
56
57 Ok(contract)
58 }
59}
60
61pub fn load_abi_from_json(json_str: &str) -> Result<Abi, CryptoError> {
63 serde_json::from_str(json_str)
64 .map_err(|e| CryptoError::SerializationError(format!("Failed to parse ABI JSON: {}", e)))
65}
66
67pub async fn call_read_function(
69 contract: &Contract,
70 provider: &Provider<Http>,
71 function_name: &str,
72 args: Vec<Token>,
73) -> Result<Vec<Token>, CryptoError> {
74 let _ethers_contract = contract.create_ethers_contract(provider.clone(), None)?;
76
77 let function = contract
79 .abi
80 .function(function_name)
81 .map_err(|e| CryptoError::ContractError(format!("Function not found in ABI: {}", e)))?;
82
83 let call_data = function.encode_input(&args).map_err(|e| {
85 CryptoError::ContractError(format!("Failed to encode function call: {}", e))
86 })?;
87
88 let tx = TransactionRequest::new()
90 .to(contract.address)
91 .data(call_data);
92
93 let result = provider
94 .call(&tx.into(), None)
95 .await
96 .map_err(|e| CryptoError::ContractError(format!("Contract call failed: {}", e)))?;
97
98 let decoded = function.decode_output(&result).map_err(|e| {
100 CryptoError::ContractError(format!("Failed to decode function output: {}", e))
101 })?;
102
103 Ok(decoded)
104}
105
106pub async fn call_write_function(
108 contract: &Contract,
109 wallet: &EthereumWallet,
110 provider: &Provider<Http>,
111 function_name: &str,
112 args: Vec<Token>,
113) -> Result<H256, CryptoError> {
114 let client = SignerMiddleware::new(provider.clone(), wallet.wallet.clone());
116
117 let function = contract
119 .abi
120 .function(function_name)
121 .map_err(|e| CryptoError::ContractError(format!("Function not found in ABI: {}", e)))?;
122
123 let call_data = function.encode_input(&args).map_err(|e| {
125 CryptoError::ContractError(format!("Failed to encode function call: {}", e))
126 })?;
127
128 let tx = TransactionRequest::new()
130 .to(contract.address)
131 .data(call_data)
132 .gas(U256::from(300000)); log::info!("Sending transaction to contract at {}", contract.address);
136 log::info!("Function: {}, Args: {:?}", function_name, args);
137
138 log::debug!("Sending transaction to contract at {}", contract.address);
140 log::debug!("Function: {}, Args: {:?}", function_name, args);
141 log::debug!("From address: {}", wallet.address);
142 log::debug!("Gas limit: {:?}", tx.gas);
143
144 let pending_tx = match client.send_transaction(tx, None).await {
145 Ok(pending_tx) => {
146 log::debug!("Transaction sent successfully: {:?}", pending_tx.tx_hash());
147 log::info!("Transaction sent successfully: {:?}", pending_tx.tx_hash());
148 pending_tx
149 }
150 Err(e) => {
151 log::error!("Failed to send transaction: {}", e);
153 log::error!("ERROR DETAILS: {:?}", e);
154 return Err(CryptoError::ContractError(format!(
155 "Failed to send transaction: {}",
156 e
157 )));
158 }
159 };
160
161 Ok(pending_tx.tx_hash())
163}
164
165pub async fn estimate_gas(
167 contract: &Contract,
168 wallet: &EthereumWallet,
169 provider: &Provider<Http>,
170 function_name: &str,
171 args: Vec<Token>,
172) -> Result<U256, CryptoError> {
173 let function = contract
175 .abi
176 .function(function_name)
177 .map_err(|e| CryptoError::ContractError(format!("Function not found in ABI: {}", e)))?;
178
179 let call_data = function.encode_input(&args).map_err(|e| {
181 CryptoError::ContractError(format!("Failed to encode function call: {}", e))
182 })?;
183
184 let tx = TransactionRequest::new()
186 .from(wallet.address)
187 .to(contract.address)
188 .data(call_data);
189
190 let gas = provider
192 .estimate_gas(&tx.into(), None)
193 .await
194 .map_err(|e| CryptoError::ContractError(format!("Failed to estimate gas: {}", e)))?;
195
196 Ok(gas)
197}