ethers_rs/
runtime.rs

1use std::fmt::Display;
2use std::str::FromStr;
3
4pub use ethers_eip2718::*;
5pub use ethers_macros::*;
6pub use ethers_primitives::*;
7pub use ethers_provider::*;
8pub use ethers_signer::signer::Signer;
9pub use serde::{Deserialize, Serialize};
10pub use serde_ethabi::from_abi;
11pub use serde_ethabi::to_abi;
12
13pub use anyhow::Error;
14use serde_json::json;
15
16/// Contract client errors
17#[derive(Debug, thiserror::Error)]
18pub enum ClientError {
19    /// Tx failed
20    #[error("TxFailure: {0}")]
21    TxFailure(H256),
22
23    /// Address field is none of deploy contract tx receipt.
24    #[error("DeployContract: contract_address field of receipt is null,{0}")]
25    DeployContract(H256),
26    /// Expect signer to execute send_raw_transaction
27    #[error("SignerExpect: method {0} expect signer")]
28    SignerExpect(String),
29    /// Expect signer to execute send_raw_transaction
30    #[error("Accounts: signer return empty accounts list")]
31    Accounts,
32}
33
34#[derive(Clone, Debug, Serialize, Deserialize, Default)]
35pub struct TxOptions {
36    /// Mannul set gas price.
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub gas_price: Option<U256>,
39    /// Transferring ether values.
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub value: Option<U256>,
42}
43
44impl<'a> TryFrom<&'a str> for TxOptions {
45    type Error = anyhow::Error;
46    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
47        Ok(serde_json::from_str(value)?)
48    }
49}
50
51impl TryFrom<String> for TxOptions {
52    type Error = anyhow::Error;
53    fn try_from(value: String) -> Result<Self, Self::Error> {
54        Ok(serde_json::from_str(&value)?)
55    }
56}
57
58impl TryFrom<serde_json::Value> for TxOptions {
59    type Error = anyhow::Error;
60    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
61        Ok(serde_json::from_value(value)?)
62    }
63}
64
65pub trait ToTxOptions {
66    fn to_tx_options(self) -> TxOptions;
67}
68
69impl<T: EthereumUnit> ToTxOptions for T {
70    fn to_tx_options(self) -> TxOptions {
71        TxOptions {
72            gas_price: None,
73            value: Some(self.to_u256()),
74        }
75    }
76}
77
78/// Client to communicate with ethereum contract.
79#[derive(Clone)]
80pub struct Client {
81    /// rpc client provider for ethereum rpc node.
82    pub provider: Provider,
83    /// tx signer.
84    pub signer: Option<Signer>,
85}
86
87impl From<(Provider, Signer)> for Client {
88    fn from((provider, signer): (Provider, Signer)) -> Self {
89        Self {
90            provider,
91            signer: Some(signer),
92        }
93    }
94}
95
96impl Client {
97    pub async fn deploy_contract(
98        &self,
99        constract_name: &str,
100        mut call_data: Vec<u8>,
101        deploy_data: &str,
102        ops: TxOptions,
103    ) -> anyhow::Result<Address> {
104        let mut buff = Vec::<u8>::from_eth_hex(deploy_data)?;
105
106        buff.append(&mut call_data);
107
108        let tx_hash = self
109            ._send_raw_transaction(constract_name, None, buff, ops, false)
110            .await?;
111
112        let receipt = self
113            .provider
114            .register_transaction_listener(tx_hash.clone())?
115            .wait()
116            .await?;
117
118        let status = receipt
119            .status
120            .ok_or(ClientError::TxFailure(tx_hash.clone()))?;
121
122        match status {
123            Status::Success => {
124                if let Some(contract_address) = receipt.contract_address {
125                    return Ok(contract_address);
126                } else {
127                    return Err(ClientError::TxFailure(tx_hash).into());
128                }
129            }
130            Status::Failure => return Err(ClientError::TxFailure(tx_hash).into()),
131        }
132    }
133
134    /// Invoke contract pure/view method without send transaction.
135    pub async fn eth_call(
136        &self,
137        method_name: &str,
138        to: &Address,
139        mut call_data: Vec<u8>,
140    ) -> anyhow::Result<Vec<u8>> {
141        log::debug!("eth_call {}", method_name);
142
143        let mut provider = self.provider.clone();
144
145        let mut selector_name = keccak256(method_name.as_bytes())[0..4].to_vec();
146
147        selector_name.append(&mut call_data);
148
149        let call_data: Bytes = selector_name.into();
150
151        let tx: LegacyTransactionRequest = json!({
152            "to": to,
153            "data":call_data,
154        })
155        .try_into()?;
156
157        let result = provider.eth_call(tx, None::<BlockNumberOrTag>).await?;
158
159        Ok(result.0)
160    }
161
162    /// Send raw transaction contract `to`.
163    ///
164    /// # Parameters
165    ///
166    /// - `to` Contract address
167    /// - `call_data` Transaction [`data`](LegacyTransactionRequest::data) field
168    pub async fn send_raw_transaction(
169        &self,
170        method_name: &str,
171        to: &Address,
172        call_data: Vec<u8>,
173        ops: TxOptions,
174    ) -> anyhow::Result<DefaultTransactionReceipter> {
175        let tx_hash = self
176            ._send_raw_transaction(method_name, Some(to), call_data, ops, true)
177            .await?;
178
179        self.provider.register_transaction_listener(tx_hash.clone())
180    }
181
182    async fn _send_raw_transaction(
183        &self,
184        method_name: &str,
185        to: Option<&Address>,
186        mut call_data: Vec<u8>,
187        ops: TxOptions,
188        selector: bool,
189    ) -> anyhow::Result<H256> {
190        let mut provider = self.provider.clone();
191
192        let mut signer = self
193            .signer
194            .clone()
195            .ok_or(ClientError::SignerExpect(method_name.to_owned()))?;
196
197        let address = {
198            let mut accounts = signer.accounts().await?;
199
200            if accounts.is_empty() {
201                return Err(ClientError::Accounts.into());
202            }
203
204            accounts.remove(0)
205        };
206
207        // Get nonce first.
208        let nonce = provider.eth_get_transaction_count(address).await?;
209
210        log::debug!(
211            target: method_name,
212            "Fetch account {} nonce, {}",
213            address.to_checksum_string(),
214            nonce
215        );
216
217        // Get chain id
218        let chain_id = provider.eth_chain_id().await?;
219
220        log::debug!(target: method_name, "Fetch chain_id, {}", chain_id);
221
222        let call_data = if selector {
223            let mut selector_name = keccak256(method_name.as_bytes())[0..4].to_vec();
224
225            selector_name.append(&mut call_data);
226
227            selector_name
228        } else {
229            call_data
230        };
231
232        let mut tx = LegacyTransactionRequest {
233            chain_id: Some(chain_id),
234            nonce: Some(nonce),
235            to: to.map(|c| c.clone()),
236            data: Some(call_data.into()),
237            value: ops.value,
238            ..Default::default()
239        };
240
241        // estimate gas
242        let gas = provider
243            .eth_estimate_gas(tx.clone(), None::<BlockNumberOrTag>)
244            .await?;
245
246        log::debug!(target: method_name, "Fetch estimate gas, {}", gas);
247
248        tx.gas = Some(gas);
249
250        // Get gas price
251
252        let gas_price = if let Some(gas_price) = ops.gas_price {
253            gas_price
254        } else {
255            provider.eth_gas_price().await?
256        };
257
258        log::debug!(target: method_name, "Fetch gas price, {}", gas_price);
259
260        tx.gas_price = Some(gas_price);
261
262        log::debug!(
263            target: method_name,
264            "Try sign transaction, {}",
265            serde_json::to_string(&tx)?,
266        );
267
268        let signed_tx = signer.sign_eth_transaction(tx).await?;
269
270        log::debug!(
271            target: method_name,
272            "Signed transaction, {}",
273            signed_tx.to_string()
274        );
275
276        let hash = provider.eth_send_raw_transaction(signed_tx).await?;
277
278        log::debug!(target: method_name, "Send transaction success, {}", hash);
279
280        Ok(hash)
281    }
282
283    /// Get balance of client bound signer.
284    ///
285    /// If client signer is [`None`], returns error [`ClientError::SignerExpect`].
286    pub async fn balance(&self) -> anyhow::Result<U256> {
287        let mut signer = self
288            .signer
289            .clone()
290            .ok_or(ClientError::SignerExpect("balance".to_owned()))?;
291
292        let address = signer.address().await?;
293
294        Ok(self.provider.clone().eth_get_balance(address).await?)
295    }
296}
297
298#[derive(Debug)]
299pub struct AbiEvent(pub ethbind::json::Event);
300
301impl FromStr for AbiEvent {
302    type Err = serde_json::Error;
303    fn from_str(s: &str) -> Result<Self, Self::Err> {
304        serde_json::from_str(s).map(|v| Self(v))
305    }
306}
307
308impl Display for AbiEvent {
309    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310        write!(f, "{}", serde_json::to_string(&self.0).unwrap())
311    }
312}