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}