solidity_bindgen/
web3_provider.rs

1use crate::context::Web3Context;
2use crate::providers::{CallProvider, SendProvider};
3use async_trait::async_trait;
4use std::marker::Unpin;
5use web3::contract::tokens::{Detokenize, Tokenize};
6use web3::contract::Contract;
7use web3::contract::Options;
8use web3::transports::Http;
9use web3::types::{Address, TransactionReceipt};
10
11/// Mostly exists to map to the new futures.
12/// This is the "untyped" API which the generated types will use.
13pub struct Web3Provider {
14    contract: Contract<Http>,
15    context: Web3Context,
16}
17
18#[async_trait]
19impl CallProvider for Web3Provider {
20    async fn call<O: Detokenize + Unpin + Send, Params: Tokenize + Send>(
21        &self,
22        name: &'static str,
23        params: Params,
24    ) -> Result<O, web3::Error> {
25        match self
26            .contract
27            .query(
28                name,
29                params,
30                Some(self.context.from()),
31                Default::default(),
32                None,
33            )
34            .await
35        {
36            Ok(v) => Ok(v),
37            Err(e) => match e {
38                web3::contract::Error::Api(e) => Err(e),
39                // The other variants InvalidOutputType and Abi should be
40                // prevented by the code gen. It is useful to convert the error
41                // type to be restricted to the web3::Error type for a few
42                // reasons. First, the web3::Error type (unlike the
43                // web3::contract::Error type) implements Send. This makes it
44                // usable in async methods. Also for consistency it's easier to
45                // mix methods using both call and send to use the ? operator if
46                // they have the same error type. It is the opinion of this
47                // library that ABI sorts of errors are irrecoverable and should
48                // panic anyway.
49                e => panic!("The ABI is out of date. Name: {}. Inner: {}", name, e),
50            },
51        }
52    }
53}
54
55#[async_trait]
56impl SendProvider for Web3Provider {
57    type Out = TransactionReceipt;
58    async fn send<Params: Tokenize + Send>(
59        &self,
60        func: &'static str,
61        params: Params,
62        options: Option<Options>,
63        confirmations: Option<usize>,
64    ) -> Result<Self::Out, web3::Error> {
65        self.contract
66            .signed_call_with_confirmations(
67                func,
68                params,
69                match options {
70                    None => Default::default(),
71                    Some(options) => options,
72                },
73                match confirmations {
74                    // Num confirmations. From a library standpoint, this should be
75                    // a parameter of the function. Choosing a correct value is very
76                    // difficult, even for a consumer of the library as it would
77                    // require assessing the value of the transaction, security
78                    // margins, and a number of other factors for which data may not
79                    // be available. So just picking a pretty high security margin
80                    // for now.
81                    None => 24,
82                    Some(confirmations) => confirmations,
83                },
84                self.context.secret_key(),
85            )
86            .await
87    }
88}
89
90impl Web3Provider {
91    pub fn new(contract_address: Address, context: &Web3Context, json_abi: &[u8]) -> Self {
92        let context = context.clone();
93
94        // All of the ABIs are verified at compile time, so we can just unwrap here.
95        // See also 4cd1038f-56f2-4cf2-8dbe-672da9006083
96        let contract = Contract::from_json(context.eth(), contract_address, json_abi).unwrap();
97
98        Self { contract, context }
99    }
100}