linera_ethereum/test_utils/
mod.rs1use linera_alloy::{
6 network::{Ethereum, EthereumSigner},
7 node_bindings::{Anvil, AnvilInstance},
8 primitives::{Address, U256},
9 providers::{
10 fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, SignerFiller},
11 ProviderBuilder, RootProvider,
12 },
13 signers::{wallet::LocalWallet, Signer},
14 sol,
15 transports::http::reqwest::Client,
16};
17use linera_base::port::get_free_port;
18use url::Url;
19
20use crate::{
21 client::EthereumQueries,
22 provider::{EthereumClient, EthereumClientSimplified, HttpProvider},
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)]
40pub struct AnvilTest {
41 pub anvil_instance: AnvilInstance,
42 pub endpoint: String,
43 pub ethereum_client: EthereumClient<HttpProvider>,
44 pub wallet_info: (LocalWallet, String),
45 pub rpc_url: Url,
46 pub provider: FillProvider<
47 JoinFill<
48 JoinFill<
49 JoinFill<JoinFill<linera_alloy::providers::Identity, GasFiller>, NonceFiller>,
50 ChainIdFiller,
51 >,
52 SignerFiller<EthereumSigner>,
53 >,
54 RootProvider<linera_alloy::transports::http::Http<Client>>,
55 linera_alloy::transports::http::Http<Client>,
56 Ethereum,
57 >,
58}
59
60pub async fn get_anvil() -> anyhow::Result<AnvilTest> {
61 let port = get_free_port().await?;
62 let anvil_instance = Anvil::new().port(port).try_spawn()?;
63 let index = 0;
64 let wallet: LocalWallet = anvil_instance.keys()[index].clone().into();
65 let address = format!("{:?}", anvil_instance.addresses()[index]);
66 let wallet_info = (wallet.clone(), address);
67 let endpoint = anvil_instance.endpoint();
68 let ethereum_client = EthereumClient::new(endpoint.clone())?;
69 let rpc_url = Url::parse(&endpoint)?;
70 let provider = ProviderBuilder::new()
71 .with_recommended_fillers()
72 .signer(EthereumSigner::from(wallet))
73 .on_http(rpc_url.clone());
74 Ok(AnvilTest {
75 anvil_instance,
76 endpoint,
77 ethereum_client,
78 wallet_info,
79 rpc_url,
80 provider,
81 })
82}
83
84impl AnvilTest {
85 pub fn get_wallet(&self, index: usize) -> (LocalWallet, String) {
86 let address = self.anvil_instance.addresses()[index];
87 let address = format!("{:?}", address);
88 let wallet: LocalWallet = self.anvil_instance.keys()[index].clone().into();
89 let wallet = wallet.with_chain_id(Some(self.anvil_instance.chain_id()));
90 (wallet, address)
91 }
92
93 pub fn get_address(&self, index: usize) -> String {
94 let address = self.anvil_instance.addresses()[index];
95 format!("{:?}", address)
96 }
97}
98
99pub struct SimpleTokenContractFunction {
100 pub contract_address: String,
101 pub anvil_test: AnvilTest,
102}
103
104impl SimpleTokenContractFunction {
105 pub async fn new(anvil_test: AnvilTest) -> anyhow::Result<Self> {
106 let initial_supply = U256::from(1000);
108 let simple_token =
109 SimpleTokenContract::deploy(&anvil_test.provider, initial_supply).await?;
110 let contract_address = simple_token.address();
111 let contract_address = format!("{:?}", contract_address);
112 Ok(Self {
113 contract_address,
114 anvil_test,
115 })
116 }
117
118 pub async fn balance_of(&self, to: &str, block: u64) -> anyhow::Result<U256> {
120 let contract_address = self.contract_address.parse::<Address>()?;
122 let simple_token =
123 SimpleTokenContract::new(contract_address, self.anvil_test.provider.clone());
124 let to_address = to.parse::<Address>()?;
126 let data = simple_token.balanceOf(to_address).calldata().clone();
127 let answer = self
129 .anvil_test
130 .ethereum_client
131 .non_executive_call(&self.contract_address, data.clone(), to, block)
132 .await?;
133 let ethereum_client_simp = EthereumClientSimplified::new(self.anvil_test.endpoint.clone());
135 let answer_simp = ethereum_client_simp
136 .non_executive_call(&self.contract_address, data, to, block)
137 .await?;
138 assert_eq!(answer_simp, answer);
139 let mut vec = [0_u8; 32];
141 for (i, val) in vec.iter_mut().enumerate() {
142 *val = answer.0[i];
143 }
144 let balance = U256::from_be_bytes(vec);
145 Ok(balance)
146 }
147
148 pub async fn transfer(&self, from: &str, to: &str, value: U256) -> anyhow::Result<()> {
149 let contract_address = self.contract_address.parse::<Address>()?;
151 let to_address = to.parse::<Address>()?;
152 let from_address = from.parse::<Address>()?;
153 let simple_token =
154 SimpleTokenContract::new(contract_address, self.anvil_test.provider.clone());
155 let builder = simple_token.transfer(to_address, value).from(from_address);
157 let _receipt = builder.send().await?.get_receipt().await?;
158 Ok(())
159 }
160}
161
162pub struct EventNumericsContractFunction {
163 pub contract_address: String,
164 pub anvil_test: AnvilTest,
165}
166
167impl EventNumericsContractFunction {
168 pub async fn new(anvil_test: AnvilTest) -> anyhow::Result<Self> {
169 let initial_supply = U256::from(0);
171 let event_numerics =
172 EventNumericsContract::deploy(&anvil_test.provider, initial_supply).await?;
173 let contract_address = event_numerics.address();
175 let contract_address = format!("{:?}", contract_address);
176 Ok(Self {
177 contract_address,
178 anvil_test,
179 })
180 }
181}