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#[allow(clippy::type_complexity)]
40#[derive(Clone)]
41pub struct EthereumClient {
42 pub provider: FillProvider<
43 JoinFill<
44 JoinFill<
45 alloy::providers::Identity,
46 JoinFill<GasFiller, JoinFill<BlobGasFiller, JoinFill<NonceFiller, ChainIdFiller>>>,
47 >,
48 WalletFiller<EthereumWallet>,
49 >,
50 RootProvider<Ethereum>,
51 >,
52}
53
54impl EthereumClient {
55 pub fn new(url: String) -> Result<Self, EthereumServiceError> {
58 let rpc_url = Url::parse(&url)?;
59 let pk: PrivateKeySigner =
61 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
62 .parse()
63 .unwrap();
64 let wallet = EthereumWallet::from(pk);
65 let provider = ProviderBuilder::new()
66 .wallet(wallet.clone())
67 .connect_http(rpc_url);
68 let endpoint = Self { provider };
69 Ok(endpoint)
70 }
71}
72
73pub struct AnvilTest {
74 pub anvil_instance: AnvilInstance,
75 pub endpoint: String,
76 pub ethereum_client: EthereumClient,
77 pub rpc_url: Url,
78}
79
80pub async fn get_anvil() -> anyhow::Result<AnvilTest> {
81 let port = get_free_port().await?;
82 let anvil_instance = Anvil::new().port(port).try_spawn()?;
83 let endpoint = anvil_instance.endpoint();
84 let ethereum_client = EthereumClient::new(endpoint.clone())?;
85 let rpc_url = Url::parse(&endpoint)?;
86 Ok(AnvilTest {
87 anvil_instance,
88 endpoint,
89 ethereum_client,
90 rpc_url,
91 })
92}
93
94impl AnvilTest {
95 pub fn get_address(&self, index: usize) -> String {
96 let address = self.anvil_instance.addresses()[index];
97 format!("{:?}", address)
98 }
99}
100
101pub struct SimpleTokenContractFunction {
102 pub contract_address: String,
103 pub anvil_test: AnvilTest,
104}
105
106impl SimpleTokenContractFunction {
107 pub async fn new(anvil_test: AnvilTest) -> anyhow::Result<Self> {
108 let initial_supply = U256::from(1000);
110 let simple_token =
111 SimpleTokenContract::deploy(&anvil_test.ethereum_client.provider, initial_supply)
112 .await?;
113 let contract_address = simple_token.address();
114 let contract_address = format!("{:?}", contract_address);
115 Ok(Self {
116 contract_address,
117 anvil_test,
118 })
119 }
120
121 pub async fn balance_of(&self, to: &str, block: u64) -> anyhow::Result<U256> {
123 let contract_address = self.contract_address.parse::<Address>()?;
125 let simple_token = SimpleTokenContract::new(
126 contract_address,
127 self.anvil_test.ethereum_client.provider.clone(),
128 );
129 let to_address = to.parse::<Address>()?;
131 let data = simple_token.balanceOf(to_address).calldata().clone();
132 let ethereum_client_simp = EthereumClientSimplified::new(self.anvil_test.endpoint.clone());
134 let answer = ethereum_client_simp
135 .non_executive_call(&self.contract_address, data, to, block)
136 .await?;
137 let mut vec = [0_u8; 32];
139 for (i, val) in vec.iter_mut().enumerate() {
140 *val = answer.0[i];
141 }
142 let balance = U256::from_be_bytes(vec);
143 Ok(balance)
144 }
145
146 pub async fn transfer(&self, from: &str, to: &str, value: U256) -> anyhow::Result<()> {
147 let contract_address = self.contract_address.parse::<Address>()?;
149 let to_address = to.parse::<Address>()?;
150 let from_address = from.parse::<Address>()?;
151 let simple_token = SimpleTokenContract::new(
152 contract_address,
153 self.anvil_test.ethereum_client.provider.clone(),
154 );
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.ethereum_client.provider, initial_supply)
173 .await?;
174 let contract_address = event_numerics.address();
176 let contract_address = format!("{:?}", contract_address);
177 Ok(Self {
178 contract_address,
179 anvil_test,
180 })
181 }
182}