linera_ethereum/
provider.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use async_lock::Mutex;
5use async_trait::async_trait;
6use linera_alloy::{
7    primitives::{Address, Bytes, U256},
8    providers::{Provider, ProviderBuilder, RootProvider},
9    rpc::types::eth::{
10        request::{TransactionInput, TransactionRequest},
11        Filter,
12    },
13    transports::http::reqwest::{header::CONTENT_TYPE, Client},
14};
15use url::Url;
16
17use crate::client::{EthereumQueries, JsonRpcClient};
18
19pub type HttpProvider = RootProvider<linera_alloy::transports::http::Http<Client>>;
20
21use crate::{
22    client::get_block_id,
23    common::{event_name_from_expanded, parse_log, EthereumEvent, EthereumServiceError},
24};
25
26/// The Ethereum endpoint and its provider used for accessing the Ethereum node.
27pub struct EthereumClientSimplified {
28    pub url: String,
29    pub id: Mutex<u64>,
30}
31
32#[async_trait]
33impl JsonRpcClient for EthereumClientSimplified {
34    type Error = EthereumServiceError;
35
36    async fn get_id(&self) -> u64 {
37        let mut id = self.id.lock().await;
38        *id += 1;
39        *id
40    }
41
42    async fn request_inner(&self, payload: Vec<u8>) -> Result<Vec<u8>, Self::Error> {
43        let res = Client::new()
44            .post(self.url.clone())
45            .body(payload)
46            .header(CONTENT_TYPE, "application/json")
47            .send()
48            .await?;
49        let body = res.bytes().await?;
50        Ok(body.as_ref().to_vec())
51    }
52}
53
54impl EthereumClientSimplified {
55    /// Connects to an existing Ethereum node and creates an `EthereumEndpoint`
56    /// if successful.
57    pub fn new(url: String) -> Self {
58        let id = Mutex::new(1);
59        Self { url, id }
60    }
61}
62
63#[derive(Clone)]
64pub struct EthereumClient<M> {
65    pub provider: M,
66}
67
68#[async_trait]
69impl EthereumQueries for EthereumClient<HttpProvider> {
70    type Error = EthereumServiceError;
71
72    async fn get_accounts(&self) -> Result<Vec<String>, EthereumServiceError> {
73        Ok(self
74            .provider
75            .get_accounts()
76            .await?
77            .into_iter()
78            .map(|x| format!("{:?}", x))
79            .collect::<Vec<_>>())
80    }
81
82    async fn get_block_number(&self) -> Result<u64, EthereumServiceError> {
83        Ok(self.provider.get_block_number().await?)
84    }
85
86    async fn get_balance(
87        &self,
88        address: &str,
89        block_number: u64,
90    ) -> Result<U256, EthereumServiceError> {
91        let address = address.parse::<Address>()?;
92        let block_id = get_block_id(block_number);
93        let request = self.provider.get_balance(address).block_id(block_id);
94        Ok(request.await?)
95    }
96
97    async fn read_events(
98        &self,
99        contract_address: &str,
100        event_name_expanded: &str,
101        from_block: u64,
102        to_block: u64,
103    ) -> Result<Vec<EthereumEvent>, EthereumServiceError> {
104        let contract_address = contract_address.parse::<Address>()?;
105        let event_name = event_name_from_expanded(event_name_expanded);
106        let filter = Filter::new()
107            .address(contract_address)
108            .event(&event_name)
109            .from_block(from_block)
110            .to_block(to_block - 1);
111        let events = self.provider.get_logs(&filter).await?;
112        events
113            .into_iter()
114            .map(|x| parse_log(event_name_expanded, x))
115            .collect::<Result<_, _>>()
116    }
117
118    async fn non_executive_call(
119        &self,
120        contract_address: &str,
121        data: Bytes,
122        from: &str,
123        block: u64,
124    ) -> Result<Bytes, EthereumServiceError> {
125        let contract_address = contract_address.parse::<Address>()?;
126        let from = from.parse::<Address>()?;
127        let input = TransactionInput::new(data);
128        let tx = TransactionRequest::default()
129            .from(from)
130            .to(contract_address)
131            .input(input);
132        let block_id = get_block_id(block);
133        let eth_call = self.provider.call(&tx).block(block_id);
134        Ok(eth_call.await?)
135    }
136}
137
138impl EthereumClient<HttpProvider> {
139    /// Connects to an existing Ethereum node and creates an `EthereumClient`
140    /// if successful.
141    pub fn new(url: String) -> Result<Self, EthereumServiceError> {
142        let rpc_url = Url::parse(&url)?;
143        let provider = ProviderBuilder::new().on_http(rpc_url);
144        let endpoint = Self { provider };
145        Ok(endpoint)
146    }
147}