linera_ethereum/
client.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::fmt::Debug;
5
6use alloy::rpc::types::eth::{
7    request::{TransactionInput, TransactionRequest},
8    BlockId, BlockNumberOrTag, Filter, Log,
9};
10use alloy_primitives::{Address, Bytes, U256, U64};
11use async_trait::async_trait;
12use linera_base::ensure;
13use serde::{de::DeserializeOwned, Deserialize, Serialize};
14use serde_json::value::RawValue;
15
16use crate::common::{
17    event_name_from_expanded, parse_log, EthereumEvent, EthereumQueryError, EthereumServiceError,
18};
19
20/// A basic RPC client for making JSON queries
21#[async_trait]
22pub trait JsonRpcClient {
23    type Error: From<serde_json::Error> + From<EthereumQueryError>;
24
25    /// The inner function that has to be implemented and access the client
26    async fn request_inner(&self, payload: Vec<u8>) -> Result<Vec<u8>, Self::Error>;
27
28    /// Gets a new ID for the next message.
29    async fn get_id(&self) -> u64;
30
31    /// The function doing the parsing of the input and output.
32    async fn request<T, R>(&self, method: &str, params: T) -> Result<R, Self::Error>
33    where
34        T: Debug + Serialize + Send + Sync,
35        R: DeserializeOwned + Send,
36    {
37        let id = self.get_id().await;
38        let payload = JsonRpcRequest::new(id, method, params);
39        let payload = serde_json::to_vec(&payload)?;
40        let body = self.request_inner(payload).await?;
41        let result = serde_json::from_slice::<JsonRpcResponse>(&body)?;
42        let raw = result.result;
43        let res = serde_json::from_str(raw.get())?;
44        ensure!(id == result.id, EthereumQueryError::IdIsNotMatching);
45        ensure!(
46            "2.0" == result.jsonrpc,
47            EthereumQueryError::WrongJsonRpcVersion
48        );
49        Ok(res)
50    }
51}
52
53#[derive(Serialize, Deserialize, Debug)]
54struct JsonRpcRequest<'a, T> {
55    id: u64,
56    jsonrpc: &'a str,
57    method: &'a str,
58    params: T,
59}
60
61impl<'a, T> JsonRpcRequest<'a, T> {
62    /// Creates a new JSON RPC request, the id does not matter
63    pub fn new(id: u64, method: &'a str, params: T) -> Self {
64        Self {
65            id,
66            jsonrpc: "2.0",
67            method,
68            params,
69        }
70    }
71}
72
73#[derive(Debug, Deserialize)]
74pub struct JsonRpcResponse {
75    id: u64,
76    jsonrpc: String,
77    result: Box<RawValue>,
78}
79
80/// The basic Ethereum queries that can be used from a smart contract and do not require
81/// gas to be executed.
82#[async_trait]
83pub trait EthereumQueries {
84    type Error;
85
86    /// Lists all the accounts of the Ethereum node.
87    async fn get_accounts(&self) -> Result<Vec<String>, Self::Error>;
88
89    /// Gets the latest block number of the Ethereum node.
90    async fn get_block_number(&self) -> Result<u64, Self::Error>;
91
92    /// Gets the balance of the specified address at the specified block number.
93    /// if no block number is specified then the balance of the latest block is
94    /// returned.
95    async fn get_balance(&self, address: &str, block_number: u64) -> Result<U256, Self::Error>;
96
97    /// Reads the events of the smart contract.
98    ///
99    /// This is done from a specified `contract_address` and `event_name_expanded`.
100    /// That is one should have `MyEvent(type1 indexed,type2)` instead
101    /// of the usual `MyEvent(type1,type2)`
102    ///
103    /// The `from_block` is inclusive.
104    /// The `to_block` is exclusive (contrary to Ethereum where it is inclusive)
105    async fn read_events(
106        &self,
107        contract_address: &str,
108        event_name_expanded: &str,
109        from_block: u64,
110        to_block: u64,
111    ) -> Result<Vec<EthereumEvent>, Self::Error>;
112
113    /// The operation done with `eth_call` on Ethereum returns
114    /// a result but are not committed to the blockchain. This can be useful for example
115    /// for executing function that are const and allow to inspect
116    /// the contract without modifying it.
117    async fn non_executive_call(
118        &self,
119        contract_address: &str,
120        data: Bytes,
121        from: &str,
122        block: u64,
123    ) -> Result<Bytes, Self::Error>;
124}
125
126pub(crate) fn get_block_id(block_number: u64) -> BlockId {
127    let number = BlockNumberOrTag::Number(block_number);
128    BlockId::Number(number)
129}
130
131#[async_trait]
132impl<C> EthereumQueries for C
133where
134    C: JsonRpcClient + Sync,
135    EthereumServiceError: From<<C as JsonRpcClient>::Error>,
136{
137    type Error = EthereumServiceError;
138
139    async fn get_accounts(&self) -> Result<Vec<String>, Self::Error> {
140        let results: Vec<String> = self.request("eth_accounts", ()).await?;
141        Ok(results
142            .into_iter()
143            .map(|x| x.to_lowercase())
144            .collect::<Vec<_>>())
145    }
146
147    async fn get_block_number(&self) -> Result<u64, Self::Error> {
148        let result = self.request::<_, U64>("eth_blockNumber", ()).await?;
149        Ok(result.to::<u64>())
150    }
151
152    async fn get_balance(&self, address: &str, block_number: u64) -> Result<U256, Self::Error> {
153        let address = address.parse::<Address>()?;
154        let tag = get_block_id(block_number);
155        Ok(self.request("eth_getBalance", (address, tag)).await?)
156    }
157
158    async fn read_events(
159        &self,
160        contract_address: &str,
161        event_name_expanded: &str,
162        from_block: u64,
163        to_block: u64,
164    ) -> Result<Vec<EthereumEvent>, Self::Error> {
165        let contract_address = contract_address.parse::<Address>()?;
166        let event_name = event_name_from_expanded(event_name_expanded);
167        let filter = Filter::new()
168            .address(contract_address)
169            .event(&event_name)
170            .from_block(from_block)
171            .to_block(to_block - 1);
172        let events = self
173            .request::<_, Vec<Log>>("eth_getLogs", (filter,))
174            .await?;
175        events
176            .into_iter()
177            .map(|x| parse_log(event_name_expanded, x))
178            .collect::<Result<_, _>>()
179    }
180
181    async fn non_executive_call(
182        &self,
183        contract_address: &str,
184        data: Bytes,
185        from: &str,
186        block: u64,
187    ) -> Result<Bytes, Self::Error> {
188        let contract_address = contract_address.parse::<Address>()?;
189        let from = from.parse::<Address>()?;
190        let input = TransactionInput::new(data);
191        let tx = TransactionRequest::default()
192            .from(from)
193            .to(contract_address)
194            .input(input);
195        let tag = get_block_id(block);
196        Ok(self.request::<_, Bytes>("eth_call", (tx, tag)).await?)
197    }
198}