ic_web3_rs/
lib.rs

1//! Ethereum JSON-RPC client (Web3).
2
3#![allow(
4    clippy::type_complexity,
5    clippy::wrong_self_convention,
6    clippy::single_match,
7    clippy::let_unit_value,
8    clippy::match_wild_err_arm
9)]
10// #![warn(missing_docs)]
11#![allow(non_camel_case_types)]
12#![allow(unused_variables)]
13#![allow(dead_code)]
14#![allow(unused_imports)]
15// select! in WS transport
16#![recursion_limit = "256"]
17
18use ic_cdk::api::management_canister::http_request::TransformContext;
19use jsonrpc_core as rpc;
20
21/// Re-export of the `futures` crate.
22#[macro_use]
23pub extern crate futures;
24pub use futures::executor::{block_on, block_on_stream};
25
26pub use ethabi;
27use transports::ic_http_client::CallOptions;
28
29// it needs to be before other modules
30// otherwise the macro for tests is not available.
31#[macro_use]
32pub mod helpers;
33
34pub mod api;
35pub mod confirm;
36pub mod contract;
37pub mod error;
38pub mod ic;
39pub mod signing;
40pub mod transforms;
41pub mod transports;
42pub mod types;
43// pub mod tx_helpers;
44
45pub use crate::{
46    api::Web3,
47    error::{Error, Result},
48};
49
50/// Assigned RequestId
51pub type RequestId = usize;
52
53// TODO [ToDr] The transport most likely don't need to be thread-safe.
54// (though it has to be Send)
55/// Transport implementation
56pub trait Transport: std::fmt::Debug + Clone {
57    /// The type of future this transport returns when a call is made.
58    type Out: futures::Future<Output = error::Result<rpc::Value>>;
59
60    /// Prepare serializable RPC call for given method with parameters.
61    fn prepare(&self, method: &str, params: Vec<rpc::Value>) -> (RequestId, rpc::Call);
62
63    /// Execute prepared RPC call.
64    fn send(&self, id: RequestId, request: rpc::Call, options: CallOptions) -> Self::Out;
65
66    /// Execute remote method with given parameters.
67    fn execute(&self, method: &str, params: Vec<rpc::Value>, options: CallOptions) -> Self::Out {
68        let (id, request) = self.prepare(method, params);
69        self.send(id, request, options)
70    }
71
72    /// set the max response bytes, do nothing by default
73    fn set_max_response_bytes(&mut self, bytes: u64) {}
74}
75
76/// A transport implementation supporting batch requests.
77pub trait BatchTransport: Transport {
78    /// The type of future this transport returns when a call is made.
79    type Batch: futures::Future<Output = error::Result<Vec<error::Result<rpc::Value>>>>;
80
81    /// Sends a batch of prepared RPC calls.
82    fn send_batch<T>(&self, requests: T) -> Self::Batch
83    where
84        T: IntoIterator<Item = (RequestId, rpc::Call)>;
85}
86
87/// A transport implementation supporting pub sub subscriptions.
88pub trait DuplexTransport: Transport {
89    /// The type of stream this transport returns
90    type NotificationStream: futures::Stream<Item = rpc::Value>;
91
92    /// Add a subscription to this transport
93    fn subscribe(&self, id: api::SubscriptionId) -> error::Result<Self::NotificationStream>;
94
95    /// Remove a subscription from this transport
96    fn unsubscribe(&self, id: api::SubscriptionId) -> error::Result<()>;
97}
98
99impl<X, T> Transport for X
100where
101    T: Transport + ?Sized,
102    X: std::ops::Deref<Target = T>,
103    X: std::fmt::Debug,
104    X: Clone,
105{
106    type Out = T::Out;
107
108    fn prepare(&self, method: &str, params: Vec<rpc::Value>) -> (RequestId, rpc::Call) {
109        (**self).prepare(method, params)
110    }
111
112    fn send(&self, id: RequestId, request: rpc::Call, options: CallOptions) -> Self::Out {
113        (**self).send(id, request, options)
114    }
115}
116
117impl<X, T> BatchTransport for X
118where
119    T: BatchTransport + ?Sized,
120    X: std::ops::Deref<Target = T>,
121    X: std::fmt::Debug,
122    X: Clone,
123{
124    type Batch = T::Batch;
125
126    fn send_batch<I>(&self, requests: I) -> Self::Batch
127    where
128        I: IntoIterator<Item = (RequestId, rpc::Call)>,
129    {
130        (**self).send_batch(requests)
131    }
132}
133
134impl<X, T> DuplexTransport for X
135where
136    T: DuplexTransport + ?Sized,
137    X: std::ops::Deref<Target = T>,
138    X: std::fmt::Debug,
139    X: Clone,
140{
141    type NotificationStream = T::NotificationStream;
142
143    fn subscribe(&self, id: api::SubscriptionId) -> error::Result<Self::NotificationStream> {
144        (**self).subscribe(id)
145    }
146
147    fn unsubscribe(&self, id: api::SubscriptionId) -> error::Result<()> {
148        (**self).unsubscribe(id)
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::{error, rpc, RequestId, Transport};
155
156    use crate::{api::Web3, transports::ic_http_client::CallOptions};
157    use futures::future::BoxFuture;
158    use ic_cdk::api::management_canister::http_request::TransformContext;
159    use std::sync::Arc;
160
161    #[derive(Debug, Clone)]
162    struct FakeTransport;
163
164    impl Transport for FakeTransport {
165        type Out = BoxFuture<'static, error::Result<rpc::Value>>;
166
167        fn prepare(&self, _method: &str, _params: Vec<rpc::Value>) -> (RequestId, rpc::Call) {
168            unimplemented!()
169        }
170
171        fn send(&self, _id: RequestId, _request: rpc::Call, options: CallOptions) -> Self::Out {
172            unimplemented!()
173        }
174    }
175
176    #[test]
177    fn should_allow_to_use_arc_as_transport() {
178        let transport = Arc::new(FakeTransport);
179        let transport2 = transport.clone();
180
181        let _web3_1 = Web3::new(transport);
182        let _web3_2 = Web3::new(transport2);
183    }
184}