linera_ethereum/test_utils/
mod.rs1use alloy::{
5 network::{Ethereum, EthereumWallet},
6 node_bindings::{Anvil, AnvilInstance},
7 providers::{
8 fillers::{
9 BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller,
10 WalletFiller,
11 },
12 ProviderBuilder, RootProvider,
13 },
14 signers::local::PrivateKeySigner,
15 sol,
16};
17use alloy_primitives::{Address, U256};
18use linera_base::port::get_free_port;
19use url::Url;
20
21use crate::{
22 client::EthereumQueries, common::EthereumServiceError, provider::EthereumClientSimplified,
23};
24
25sol!(
26 #[allow(missing_docs)]
27 #[sol(rpc)]
28 SimpleTokenContract,
29 "./contracts/SimpleToken.json"
30);
31
32sol!(
33 #[allow(missing_docs)]
34 #[sol(rpc)]
35 EventNumericsContract,
36 "./contracts/EventNumerics.json"
37);
38
39#[expect(clippy::type_complexity)]
41#[derive(Clone)]
42pub struct EthereumClient {
43 pub provider: FillProvider<
45 JoinFill<
46 JoinFill<
47 alloy::providers::Identity,
48 JoinFill<GasFiller, JoinFill<BlobGasFiller, JoinFill<NonceFiller, ChainIdFiller>>>,
49 >,
50 WalletFiller<EthereumWallet>,
51 >,
52 RootProvider<Ethereum>,
53 >,
54}
55
56impl EthereumClient {
57 pub fn new(url: &str) -> Result<Self, EthereumServiceError> {
60 let rpc_url = Url::parse(url)?;
61 let pk: PrivateKeySigner =
63 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
64 .parse()
65 .unwrap();
66 let wallet = EthereumWallet::from(pk);
67 let provider = ProviderBuilder::new()
68 .wallet(wallet.clone())
69 .connect_http(rpc_url);
70 let endpoint = Self { provider };
71 Ok(endpoint)
72 }
73}
74
75pub struct AnvilTest {
77 pub anvil_instance: AnvilInstance,
79 pub endpoint: String,
81 pub ethereum_client: EthereumClient,
83 pub rpc_url: Url,
85}
86
87pub async fn get_anvil() -> anyhow::Result<AnvilTest> {
89 let port = get_free_port().await?;
90 let anvil_instance = Anvil::new().port(port).try_spawn()?;
91 let endpoint = anvil_instance.endpoint();
92 let ethereum_client = EthereumClient::new(&endpoint)?;
93 let rpc_url = Url::parse(&endpoint)?;
94 Ok(AnvilTest {
95 anvil_instance,
96 endpoint,
97 ethereum_client,
98 rpc_url,
99 })
100}
101
102impl AnvilTest {
103 pub fn get_address(&self, index: usize) -> String {
105 let address = self.anvil_instance.addresses()[index];
106 format!("{address:?}")
107 }
108}
109
110pub struct SimpleTokenContractFunction {
112 pub contract_address: String,
114 pub anvil_test: AnvilTest,
116}
117
118impl SimpleTokenContractFunction {
119 pub async fn new(anvil_test: AnvilTest) -> anyhow::Result<Self> {
121 let initial_supply = U256::from(1000);
123 let simple_token =
124 SimpleTokenContract::deploy(&anvil_test.ethereum_client.provider, initial_supply)
125 .await?;
126 let contract_address = simple_token.address();
127 let contract_address = format!("{contract_address:?}");
128 Ok(Self {
129 contract_address,
130 anvil_test,
131 })
132 }
133
134 pub async fn balance_of(&self, to: &str, block: u64) -> anyhow::Result<U256> {
137 let contract_address = self.contract_address.parse::<Address>()?;
139 let simple_token = SimpleTokenContract::new(
140 contract_address,
141 self.anvil_test.ethereum_client.provider.clone(),
142 );
143 let to_address = to.parse::<Address>()?;
145 let data = simple_token.balanceOf(to_address).calldata().clone();
146 let ethereum_client_simp = EthereumClientSimplified::new(self.anvil_test.endpoint.clone());
148 let answer = ethereum_client_simp
149 .non_executive_call(&self.contract_address, data, to, block)
150 .await?;
151 let mut vec = [0_u8; 32];
153 for (i, val) in vec.iter_mut().enumerate() {
154 *val = answer.0[i];
155 }
156 let balance = U256::from_be_bytes(vec);
157 Ok(balance)
158 }
159
160 pub async fn transfer(&self, from: &str, to: &str, value: U256) -> anyhow::Result<()> {
162 let contract_address = self.contract_address.parse::<Address>()?;
164 let to_address = to.parse::<Address>()?;
165 let from_address = from.parse::<Address>()?;
166 let simple_token = SimpleTokenContract::new(
167 contract_address,
168 self.anvil_test.ethereum_client.provider.clone(),
169 );
170 let builder = simple_token.transfer(to_address, value).from(from_address);
172 let _receipt = builder.send().await?.get_receipt().await?;
173 Ok(())
174 }
175}
176
177pub struct EventNumericsContractFunction {
179 pub contract_address: String,
181 pub anvil_test: AnvilTest,
183}
184
185impl EventNumericsContractFunction {
186 pub async fn new(anvil_test: AnvilTest) -> anyhow::Result<Self> {
188 let initial_supply = U256::from(0);
190 let event_numerics =
191 EventNumericsContract::deploy(&anvil_test.ethereum_client.provider, initial_supply)
192 .await?;
193 let contract_address = event_numerics.address();
195 let contract_address = format!("{contract_address:?}");
196 Ok(Self {
197 contract_address,
198 anvil_test,
199 })
200 }
201}