diff --git a/prdoc/pr_7482.prdoc b/prdoc/pr_7482.prdoc new file mode 100644 index 0000000000000..0f548582a21a8 --- /dev/null +++ b/prdoc/pr_7482.prdoc @@ -0,0 +1,7 @@ +title: '[pallet-revive] rpc - gas used fixes ' +doc: +- audience: Runtime Dev + description: '#7463 follow up with RPC fixes' +crates: +- name: pallet-revive-eth-rpc + bump: minor diff --git a/substrate/frame/revive/rpc/examples/rust/deploy.rs b/substrate/frame/revive/rpc/examples/rust/deploy.rs index b74d7ea18d41f..12f539bba42ff 100644 --- a/substrate/frame/revive/rpc/examples/rust/deploy.rs +++ b/substrate/frame/revive/rpc/examples/rust/deploy.rs @@ -19,10 +19,8 @@ use pallet_revive::{ create1, evm::{Account, BlockTag, ReceiptInfo, U256}, }; -use pallet_revive_eth_rpc::{ - example::{wait_for_receipt, TransactionBuilder}, - EthRpcClient, -}; +use pallet_revive_eth_rpc::{example::TransactionBuilder, EthRpcClient}; +use std::sync::Arc; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -36,43 +34,49 @@ async fn main() -> anyhow::Result<()> { println!("Account:"); println!("- address: {:?}", account.address()); println!("- substrate: {}", account.substrate_account()); - let client = HttpClientBuilder::default().build("http://localhost:8545")?; + let client = Arc::new(HttpClientBuilder::default().build("http://localhost:8545")?); println!("\n\n=== Deploying contract ===\n\n"); let nonce = client.get_transaction_count(account.address(), BlockTag::Latest.into()).await?; - let hash = TransactionBuilder::default() + let tx = TransactionBuilder::new(&client) .value(5_000_000_000_000u128.into()) .input(input) - .send(&client) + .send() .await?; - println!("Deploy Tx hash: {hash:?}"); + println!("Deploy Tx hash: {:?}", tx.hash()); let ReceiptInfo { block_number, gas_used, contract_address, .. } = - wait_for_receipt(&client, hash).await?; + tx.wait_for_receipt().await?; let contract_address = contract_address.unwrap(); assert_eq!(contract_address, create1(&account.address(), nonce.try_into().unwrap())); println!("Receipt:"); - println!("- Block number: {block_number}"); - println!("- Gas used: {gas_used}"); + println!("- Block number: {block_number}"); + println!("- Gas estimated: {}", tx.gas()); + println!("- Gas used: {gas_used}"); println!("- Contract address: {contract_address:?}"); let balance = client.get_balance(contract_address, BlockTag::Latest.into()).await?; println!("- Contract balance: {balance:?}"); + if std::env::var("SKIP_CALL").is_ok() { + return Ok(()); + } + println!("\n\n=== Calling contract ===\n\n"); - let hash = TransactionBuilder::default() + let tx = TransactionBuilder::new(&client) .value(U256::from(1_000_000u32)) .to(contract_address) - .send(&client) + .send() .await?; - println!("Contract call tx hash: {hash:?}"); - let ReceiptInfo { block_number, gas_used, to, .. } = wait_for_receipt(&client, hash).await?; + println!("Contract call tx hash: {:?}", tx.hash()); + let ReceiptInfo { block_number, gas_used, to, .. } = tx.wait_for_receipt().await?; println!("Receipt:"); - println!("- Block number: {block_number}"); - println!("- Gas used: {gas_used}"); - println!("- To: {to:?}"); + println!("- Block number: {block_number}"); + println!("- Gas used: {gas_used}"); + println!("- Gas estimated: {}", tx.gas()); + println!("- To: {to:?}"); Ok(()) } diff --git a/substrate/frame/revive/rpc/examples/rust/rpc-playground.rs b/substrate/frame/revive/rpc/examples/rust/rpc-playground.rs index 64175ca60b5f4..ea8916b3b993d 100644 --- a/substrate/frame/revive/rpc/examples/rust/rpc-playground.rs +++ b/substrate/frame/revive/rpc/examples/rust/rpc-playground.rs @@ -17,13 +17,14 @@ use jsonrpsee::http_client::HttpClientBuilder; use pallet_revive::evm::{Account, BlockTag}; use pallet_revive_eth_rpc::EthRpcClient; +use std::sync::Arc; #[tokio::main] async fn main() -> anyhow::Result<()> { let account = Account::default(); println!("Account address: {:?}", account.address()); - let client = HttpClientBuilder::default().build("http://localhost:8545")?; + let client = Arc::new(HttpClientBuilder::default().build("http://localhost:8545")?); let block = client.get_block_by_number(BlockTag::Latest.into(), false).await?; println!("Latest block: {block:#?}"); diff --git a/substrate/frame/revive/rpc/examples/rust/transfer.rs b/substrate/frame/revive/rpc/examples/rust/transfer.rs index 1d67a2dba28f9..5f16716760f9a 100644 --- a/substrate/frame/revive/rpc/examples/rust/transfer.rs +++ b/substrate/frame/revive/rpc/examples/rust/transfer.rs @@ -16,14 +16,12 @@ // limitations under the License. use jsonrpsee::http_client::HttpClientBuilder; use pallet_revive::evm::{Account, BlockTag, ReceiptInfo}; -use pallet_revive_eth_rpc::{ - example::{wait_for_receipt, TransactionBuilder}, - EthRpcClient, -}; +use pallet_revive_eth_rpc::{example::TransactionBuilder, EthRpcClient}; +use std::sync::Arc; #[tokio::main] async fn main() -> anyhow::Result<()> { - let client = HttpClientBuilder::default().build("http://localhost:8545")?; + let client = Arc::new(HttpClientBuilder::default().build("http://localhost:8545")?); let alith = Account::default(); let alith_address = alith.address(); @@ -41,16 +39,15 @@ async fn main() -> anyhow::Result<()> { print_balance().await?; println!("\n\n=== Transferring ===\n\n"); - let hash = TransactionBuilder::default() + let tx = TransactionBuilder::new(&client) .signer(alith) .value(value) .to(ethan.address()) - .send(&client) + .send() .await?; - println!("Transaction hash: {hash:?}"); + println!("Transaction hash: {:?}", tx.hash()); - let ReceiptInfo { block_number, gas_used, status, .. } = - wait_for_receipt(&client, hash).await?; + let ReceiptInfo { block_number, gas_used, status, .. } = tx.wait_for_receipt().await?; println!("Receipt: "); println!("- Block number: {block_number}"); println!("- Gas used: {gas_used}"); diff --git a/substrate/frame/revive/rpc/revive_chain.metadata b/substrate/frame/revive/rpc/revive_chain.metadata index 29d91486afe00..89476924cf007 100644 Binary files a/substrate/frame/revive/rpc/revive_chain.metadata and b/substrate/frame/revive/rpc/revive_chain.metadata differ diff --git a/substrate/frame/revive/rpc/src/cli.rs b/substrate/frame/revive/rpc/src/cli.rs index b6c57d2c3b0bf..7ebf53e7fbfe0 100644 --- a/substrate/frame/revive/rpc/src/cli.rs +++ b/substrate/frame/revive/rpc/src/cli.rs @@ -16,9 +16,9 @@ // limitations under the License. //! The Ethereum JSON-RPC server. use crate::{ - client::{connect, Client}, + client::{connect, native_to_eth_ratio, Client}, BlockInfoProvider, BlockInfoProviderImpl, CacheReceiptProvider, DBReceiptProvider, - EthRpcServer, EthRpcServerImpl, ReceiptProvider, SystemHealthRpcServer, + EthRpcServer, EthRpcServerImpl, ReceiptExtractor, ReceiptProvider, SystemHealthRpcServer, SystemHealthRpcServerImpl, LOG_TARGET, }; use clap::Parser; @@ -146,6 +146,8 @@ pub fn run(cmd: CliCommand) -> anyhow::Result<()> { let (api, rpc_client, rpc) = connect(&node_rpc_url).await?; let block_provider: Arc = Arc::new(BlockInfoProviderImpl::new(cache_size, api.clone(), rpc.clone())); + + let receipt_extractor = ReceiptExtractor::new(native_to_eth_ratio(&api).await?); let receipt_provider: Arc = if let Some(database_url) = database_url.as_ref() { log::info!(target: LOG_TARGET, "🔗 Connecting to provided database"); @@ -155,6 +157,7 @@ pub fn run(cmd: CliCommand) -> anyhow::Result<()> { database_url, database_read_only, block_provider.clone(), + receipt_extractor.clone(), ) .await?, )) @@ -163,8 +166,15 @@ pub fn run(cmd: CliCommand) -> anyhow::Result<()> { Arc::new(CacheReceiptProvider::default()) }; - let client = - Client::new(api, rpc_client, rpc, block_provider, receipt_provider).await?; + let client = Client::new( + api, + rpc_client, + rpc, + block_provider, + receipt_provider, + receipt_extractor, + ) + .await?; client.subscribe_and_cache_blocks(&essential_spawn_handle); Ok::<_, crate::ClientError>(client) } diff --git a/substrate/frame/revive/rpc/src/client.rs b/substrate/frame/revive/rpc/src/client.rs index 7f45a27a05a35..e312114e90e30 100644 --- a/substrate/frame/revive/rpc/src/client.rs +++ b/substrate/frame/revive/rpc/src/client.rs @@ -17,11 +17,10 @@ //! The client connects to the source substrate chain //! and is used by the rpc server to query and send transactions to the substrate chain. use crate::{ - extract_receipts_from_block, subxt_client::{ revive::calls::types::EthTransact, runtime_types::pallet_revive::storage::ContractInfo, }, - BlockInfoProvider, ReceiptProvider, TransactionInfo, LOG_TARGET, + BlockInfoProvider, ReceiptExtractor, ReceiptProvider, TransactionInfo, LOG_TARGET, }; use jsonrpsee::types::{error::CALL_EXECUTION_FAILED_CODE, ErrorObjectOwned}; use pallet_revive::{ @@ -170,6 +169,7 @@ pub struct Client { rpc: LegacyRpcMethods, receipt_provider: Arc, block_provider: Arc, + receipt_extractor: ReceiptExtractor, chain_id: u64, max_block_weight: Weight, } @@ -180,6 +180,12 @@ async fn chain_id(api: &OnlineClient) -> Result) -> Result { + let query = subxt_client::constants().revive().native_to_eth_ratio(); + api.constants().at(&query).map_err(|err| err.into()) +} + /// Fetch the max block weight from the substrate chain. async fn max_block_weight(api: &OnlineClient) -> Result { let query = subxt_client::constants().system().block_weights(); @@ -226,6 +232,7 @@ impl Client { rpc: LegacyRpcMethods, block_provider: Arc, receipt_provider: Arc, + receipt_extractor: ReceiptExtractor, ) -> Result { let (chain_id, max_block_weight) = tokio::try_join!(chain_id(&api), max_block_weight(&api))?; @@ -236,6 +243,7 @@ impl Client { rpc, receipt_provider, block_provider, + receipt_extractor, chain_id, max_block_weight, }) @@ -320,7 +328,7 @@ impl Client { }, }; - log::debug!(target: LOG_TARGET, "Pushing block: {}", block.number()); + log::trace!(target: LOG_TARGET, "Pushing block: {}", block.number()); if let Err(err) = callback(block).await { log::error!(target: LOG_TARGET, "Failed to process block: {err:?}"); } @@ -336,7 +344,7 @@ impl Client { spawn_handle.spawn("subscribe-blocks", None, async move { let res = client .subscribe_new_blocks(SubscriptionType::BestBlocks, |block| async { - let receipts = extract_receipts_from_block(&block).await?; + let receipts = client.receipt_extractor.extract_from_block(&block).await?; client.receipt_provider.insert(&block.hash(), &receipts).await; if let Some(pruned) = client.block_provider.cache_block(block).await { @@ -360,9 +368,10 @@ impl Client { ) -> Result<(), ClientError> { let new_blocks_fut = self.subscribe_new_blocks(SubscriptionType::FinalizedBlocks, |block| async move { - let receipts = extract_receipts_from_block(&block).await.inspect_err(|err| { - log::error!(target: LOG_TARGET, "Failed to extract receipts from block: {err:?}"); - })?; + let receipts = + self.receipt_extractor.extract_from_block(&block).await.inspect_err(|err| { + log::error!(target: LOG_TARGET, "Failed to extract receipts from block: {err:?}"); + })?; self.receipt_provider.insert(&block.hash(), &receipts).await; Ok(()) }); @@ -370,7 +379,7 @@ impl Client { let Some(oldest_block) = oldest_block else { return new_blocks_fut.await }; let old_blocks_fut = self.subscribe_past_blocks(|block| async move { - let receipts = extract_receipts_from_block(&block).await?; + let receipts = self.receipt_extractor.extract_from_block(&block).await?; self.receipt_provider.insert(&block.hash(), &receipts).await; if block.number() == oldest_block { Ok(ControlFlow::Break(())) @@ -481,7 +490,7 @@ impl Client { pub async fn receipt_by_hash_and_index( &self, block_hash: &H256, - transaction_index: &U256, + transaction_index: usize, ) -> Option { self.receipt_provider .receipt_by_block_hash_and_index(block_hash, transaction_index) @@ -665,7 +674,7 @@ impl Client { let state_root = header.state_root.0.into(); let extrinsics_root = header.extrinsics_root.0.into(); - let receipts = extract_receipts_from_block(&block).await.unwrap_or_default(); + let receipts = self.receipt_extractor.extract_from_block(&block).await.unwrap_or_default(); let gas_used = receipts.iter().fold(U256::zero(), |acc, (_, receipt)| acc + receipt.gas_used); let transactions = if hydrated_transactions { diff --git a/substrate/frame/revive/rpc/src/eth-indexer.rs b/substrate/frame/revive/rpc/src/eth-indexer.rs index 894143be0a525..6e3dc0b4bc5cf 100644 --- a/substrate/frame/revive/rpc/src/eth-indexer.rs +++ b/substrate/frame/revive/rpc/src/eth-indexer.rs @@ -17,8 +17,8 @@ //! The Ethereum JSON-RPC server. use clap::Parser; use pallet_revive_eth_rpc::{ - client::{connect, Client, SubstrateBlockNumber}, - BlockInfoProvider, BlockInfoProviderImpl, DBReceiptProvider, ReceiptProvider, + client::{connect, native_to_eth_ratio, Client, SubstrateBlockNumber}, + BlockInfoProvider, BlockInfoProviderImpl, DBReceiptProvider, ReceiptExtractor, ReceiptProvider, }; use sc_cli::SharedParams; use std::sync::Arc; @@ -78,10 +78,20 @@ pub async fn main() -> anyhow::Result<()> { let (api, rpc_client, rpc) = connect(&node_rpc_url).await?; let block_provider: Arc = Arc::new(BlockInfoProviderImpl::new(0, api.clone(), rpc.clone())); - let receipt_provider: Arc = - Arc::new(DBReceiptProvider::new(&database_url, false, block_provider.clone()).await?); + let receipt_extractor = ReceiptExtractor::new(native_to_eth_ratio(&api).await?); + let receipt_provider: Arc = Arc::new( + DBReceiptProvider::new( + &database_url, + false, + block_provider.clone(), + receipt_extractor.clone(), + ) + .await?, + ); - let client = Client::new(api, rpc_client, rpc, block_provider, receipt_provider).await?; + let client = + Client::new(api, rpc_client, rpc, block_provider, receipt_provider, receipt_extractor) + .await?; client.subscribe_and_cache_receipts(oldest_block).await?; Ok(()) diff --git a/substrate/frame/revive/rpc/src/eth-rpc-tester.rs b/substrate/frame/revive/rpc/src/eth-rpc-tester.rs index 0ddad6874dfd5..7cea1a303e383 100644 --- a/substrate/frame/revive/rpc/src/eth-rpc-tester.rs +++ b/substrate/frame/revive/rpc/src/eth-rpc-tester.rs @@ -17,10 +17,8 @@ use clap::Parser; use jsonrpsee::http_client::HttpClientBuilder; use pallet_revive::evm::{Account, BlockTag, ReceiptInfo}; -use pallet_revive_eth_rpc::{ - example::{wait_for_receipt, TransactionBuilder}, - EthRpcClient, -}; +use pallet_revive_eth_rpc::{example::TransactionBuilder, EthRpcClient}; +use std::sync::Arc; use tokio::{ io::{AsyncBufReadExt, BufReader}, process::{Child, ChildStderr, Command}, @@ -119,7 +117,7 @@ async fn test_eth_rpc(stderr: ChildStderr) -> anyhow::Result<()> { println!("Account:"); println!("- address: {:?}", account.address()); - let client = HttpClientBuilder::default().build("http://localhost:8545")?; + let client = Arc::new(HttpClientBuilder::default().build("http://localhost:8545")?); let nonce = client.get_transaction_count(account.address(), BlockTag::Latest.into()).await?; let balance = client.get_balance(account.address(), BlockTag::Latest.into()).await?; @@ -127,29 +125,29 @@ async fn test_eth_rpc(stderr: ChildStderr) -> anyhow::Result<()> { println!("- balance: {balance:?}"); println!("\n\n=== Deploying dummy contract ===\n\n"); - let hash = TransactionBuilder::default().input(input).send(&client).await?; + let tx = TransactionBuilder::new(&client).input(input).send().await?; - println!("Hash: {hash:?}"); + println!("Hash: {:?}", tx.hash()); println!("Waiting for receipt..."); let ReceiptInfo { block_number, gas_used, contract_address, .. } = - wait_for_receipt(&client, hash).await?; + tx.wait_for_receipt().await?; let contract_address = contract_address.unwrap(); println!("\nReceipt:"); - println!("Block explorer: https://westend-asset-hub-eth-explorer.parity.io/{hash:?}"); + println!("Block explorer: https://westend-asset-hub-eth-explorer.parity.io/{:?}", tx.hash()); println!("- Block number: {block_number}"); println!("- Gas used: {gas_used}"); println!("- Address: {contract_address:?}"); println!("\n\n=== Calling dummy contract ===\n\n"); - let hash = TransactionBuilder::default().to(contract_address).send(&client).await?; + let tx = TransactionBuilder::new(&client).to(contract_address).send().await?; - println!("Hash: {hash:?}"); + println!("Hash: {:?}", tx.hash()); println!("Waiting for receipt..."); - let ReceiptInfo { block_number, gas_used, to, .. } = wait_for_receipt(&client, hash).await?; + let ReceiptInfo { block_number, gas_used, to, .. } = tx.wait_for_receipt().await?; println!("\nReceipt:"); - println!("Block explorer: https://westend-asset-hub-eth-explorer.parity.io/{hash:?}"); + println!("Block explorer: https://westend-asset-hub-eth-explorer.parity.io/{:?}", tx.hash()); println!("- Block number: {block_number}"); println!("- Gas used: {gas_used}"); println!("- To: {to:?}"); diff --git a/substrate/frame/revive/rpc/src/example.rs b/substrate/frame/revive/rpc/src/example.rs index aad5b4fbc344d..c8c633a4e982c 100644 --- a/substrate/frame/revive/rpc/src/example.rs +++ b/substrate/frame/revive/rpc/src/example.rs @@ -20,38 +20,11 @@ use anyhow::Context; use pallet_revive::evm::{ Account, BlockTag, Bytes, GenericTransaction, TransactionLegacyUnsigned, H160, H256, U256, }; - -/// Wait for a transaction receipt. -pub async fn wait_for_receipt( - client: &(impl EthRpcClient + Send + Sync), - hash: H256, -) -> anyhow::Result { - for _ in 0..30 { - tokio::time::sleep(std::time::Duration::from_secs(2)).await; - let receipt = client.get_transaction_receipt(hash).await?; - if let Some(receipt) = receipt { - return Ok(receipt) - } - } - - anyhow::bail!("Failed to get receipt") -} - -/// Wait for a successful transaction receipt. -pub async fn wait_for_successful_receipt( - client: &(impl EthRpcClient + Send + Sync), - hash: H256, -) -> anyhow::Result { - let receipt = wait_for_receipt(client, hash).await?; - if receipt.is_success() { - Ok(receipt) - } else { - anyhow::bail!("Transaction failed") - } -} +use std::sync::Arc; /// Transaction builder. -pub struct TransactionBuilder { +pub struct TransactionBuilder { + client: Arc, signer: Account, value: U256, input: Bytes, @@ -59,9 +32,51 @@ pub struct TransactionBuilder { mutate: Box, } -impl Default for TransactionBuilder { - fn default() -> Self { +#[derive(Debug)] +pub struct SubmittedTransaction { + tx: GenericTransaction, + hash: H256, + client: Arc, +} + +impl SubmittedTransaction { + /// Get the hash of the transaction. + pub fn hash(&self) -> H256 { + self.hash + } + + /// The gas sent with the transaction. + pub fn gas(&self) -> U256 { + self.tx.gas.unwrap() + } + + /// Wait for the receipt of the transaction. + pub async fn wait_for_receipt(&self) -> anyhow::Result { + let hash = self.hash(); + for _ in 0..30 { + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + let receipt = self.client.get_transaction_receipt(hash).await?; + if let Some(receipt) = receipt { + if receipt.is_success() { + assert!( + self.gas() > receipt.gas_used, + "Gas used should be less than gas estimated." + ); + return Ok(receipt) + } else { + anyhow::bail!("Transaction failed") + } + } + } + + anyhow::bail!("Timeout, failed to get receipt") + } +} + +impl TransactionBuilder { + pub fn new(client: &Arc) -> Self { Self { + client: Arc::clone(client), signer: Account::default(), value: U256::zero(), input: Bytes::default(), @@ -69,9 +84,6 @@ impl Default for TransactionBuilder { mutate: Box::new(|_| {}), } } -} - -impl TransactionBuilder { /// Set the signer. pub fn signer(mut self, signer: Account) -> Self { self.signer = signer; @@ -103,11 +115,8 @@ impl TransactionBuilder { } /// Call eth_call to get the result of a view function - pub async fn eth_call( - self, - client: &(impl EthRpcClient + Send + Sync), - ) -> anyhow::Result> { - let TransactionBuilder { signer, value, input, to, .. } = self; + pub async fn eth_call(self) -> anyhow::Result> { + let TransactionBuilder { client, signer, value, input, to, .. } = self; let from = signer.address(); let result = client @@ -127,8 +136,8 @@ impl TransactionBuilder { } /// Send the transaction. - pub async fn send(self, client: &(impl EthRpcClient + Send + Sync)) -> anyhow::Result { - let TransactionBuilder { signer, value, input, to, mutate } = self; + pub async fn send(self) -> anyhow::Result> { + let TransactionBuilder { client, signer, value, input, to, mutate } = self; let from = signer.address(); let chain_id = Some(client.chain_id().await?); @@ -153,6 +162,7 @@ impl TransactionBuilder { .await .with_context(|| "Failed to fetch gas estimate")?; + println!("Gas estimate: {gas:?}"); let mut unsigned_tx = TransactionLegacyUnsigned { gas, nonce, @@ -166,23 +176,18 @@ impl TransactionBuilder { mutate(&mut unsigned_tx); - let tx = signer.sign_transaction(unsigned_tx.into()); - let bytes = tx.signed_payload(); + let signed_tx = signer.sign_transaction(unsigned_tx.into()); + let bytes = signed_tx.signed_payload(); let hash = client .send_raw_transaction(bytes.into()) .await .with_context(|| "transaction failed")?; - Ok(hash) - } - - /// Send the transaction and wait for the receipt. - pub async fn send_and_wait_for_receipt( - self, - client: &(impl EthRpcClient + Send + Sync), - ) -> anyhow::Result { - let hash = self.send(client).await?; - wait_for_successful_receipt(client, hash).await + Ok(SubmittedTransaction { + tx: GenericTransaction::from_signed(signed_tx, Some(from)), + hash, + client, + }) } } diff --git a/substrate/frame/revive/rpc/src/lib.rs b/substrate/frame/revive/rpc/src/lib.rs index f2567db8a330d..3599083dcd431 100644 --- a/substrate/frame/revive/rpc/src/lib.rs +++ b/substrate/frame/revive/rpc/src/lib.rs @@ -41,6 +41,9 @@ pub use block_info_provider::*; mod receipt_provider; pub use receipt_provider::*; +mod receipt_extractor; +pub use receipt_extractor::*; + mod rpc_health; pub use rpc_health::*; @@ -299,8 +302,10 @@ impl EthRpcServer for EthRpcServerImpl { block_hash: H256, transaction_index: U256, ) -> RpcResult> { - let Some(receipt) = - self.client.receipt_by_hash_and_index(&block_hash, &transaction_index).await + let Some(receipt) = self + .client + .receipt_by_hash_and_index(&block_hash, transaction_index.as_usize()) + .await else { return Ok(None); }; diff --git a/substrate/frame/revive/rpc/src/receipt_extractor.rs b/substrate/frame/revive/rpc/src/receipt_extractor.rs new file mode 100644 index 0000000000000..e53f98639671c --- /dev/null +++ b/substrate/frame/revive/rpc/src/receipt_extractor.rs @@ -0,0 +1,178 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + client::SubstrateBlock, + subxt_client::{ + revive::{calls::types::EthTransact, events::ContractEmitted}, + system::events::ExtrinsicSuccess, + transaction_payment::events::TransactionFeePaid, + SrcChainConfig, + }, + ClientError, LOG_TARGET, +}; +use futures::{stream, StreamExt}; +use pallet_revive::{ + create1, + evm::{GenericTransaction, Log, ReceiptInfo, TransactionSigned, H256, U256}, +}; +use sp_core::keccak_256; + +/// Utility to extract receipts from extrinsics. +#[derive(Clone, Debug)] +pub struct ReceiptExtractor { + /// The native to eth decimal ratio, used to calculated gas from native fees. + native_to_eth_ratio: u32, +} + +impl ReceiptExtractor { + /// Create a new `ReceiptExtractor` with the given native to eth ratio. + pub fn new(native_to_eth_ratio: u32) -> Self { + Self { native_to_eth_ratio } + } + + /// Extract a [`TransactionSigned`] and a [`ReceiptInfo`] and from an extrinsic. + pub async fn extract_from_extrinsic( + &self, + block: &SubstrateBlock, + ext: subxt::blocks::ExtrinsicDetails>, + call: EthTransact, + ) -> Result<(TransactionSigned, ReceiptInfo), ClientError> { + let transaction_index = ext.index(); + let block_number = U256::from(block.number()); + let block_hash = block.hash(); + let events = ext.events().await?; + + let success = events.has::().inspect_err(|err| { + log::debug!(target: LOG_TARGET, "Failed to lookup for ExtrinsicSuccess event in block {block_number}: {err:?}") + })?; + let tx_fees = events + .find_first::()? + .ok_or(ClientError::TxFeeNotFound) + .inspect_err( + |err| log::debug!(target: LOG_TARGET, "TransactionFeePaid not found in events for block {block_number}\n{err:?}") + )?; + let transaction_hash = H256(keccak_256(&call.payload)); + + let signed_tx = + TransactionSigned::decode(&call.payload).map_err(|_| ClientError::TxDecodingFailed)?; + let from = signed_tx.recover_eth_address().map_err(|_| { + log::error!(target: LOG_TARGET, "Failed to recover eth address from signed tx"); + ClientError::RecoverEthAddressFailed + })?; + + let tx_info = GenericTransaction::from_signed(signed_tx.clone(), Some(from)); + let gas_price = tx_info.gas_price.unwrap_or_default(); + let gas_used = U256::from(tx_fees.tip.saturating_add(tx_fees.actual_fee)) + .saturating_mul(self.native_to_eth_ratio.into()) + .checked_div(gas_price) + .unwrap_or_default(); + + // get logs from ContractEmitted event + let logs = events + .iter() + .filter_map(|event_details| { + let event_details = event_details.ok()?; + let event = event_details.as_event::().ok()??; + + Some(Log { + address: event.contract, + topics: event.topics, + data: Some(event.data.into()), + block_number: Some(block_number), + transaction_hash, + transaction_index: Some(transaction_index.into()), + block_hash: Some(block_hash), + log_index: Some(event_details.index().into()), + ..Default::default() + }) + }) + .collect(); + + let contract_address = if tx_info.to.is_none() { + Some(create1( + &from, + tx_info + .nonce + .unwrap_or_default() + .try_into() + .map_err(|_| ClientError::ConversionFailed)?, + )) + } else { + None + }; + + log::debug!(target: LOG_TARGET, "Adding receipt for tx hash: {transaction_hash:?} - block: {block_number:?}"); + let receipt = ReceiptInfo::new( + block_hash, + block_number, + contract_address, + from, + logs, + tx_info.to, + gas_price, + gas_used, + success, + transaction_hash, + transaction_index.into(), + tx_info.r#type.unwrap_or_default(), + ); + Ok((signed_tx, receipt)) + } + + /// Extract receipts from block. + pub async fn extract_from_block( + &self, + block: &SubstrateBlock, + ) -> Result, ClientError> { + // Filter extrinsics from pallet_revive + let extrinsics = block.extrinsics().await.inspect_err(|err| { + log::debug!(target: LOG_TARGET, "Error fetching for #{:?} extrinsics: {err:?}", block.number()); + })?; + + let extrinsics = extrinsics.iter().flat_map(|ext| { + let call = ext.as_extrinsic::().ok()??; + Some((ext, call)) + }); + + stream::iter(extrinsics) + .map(|(ext, call)| async move { self.extract_from_extrinsic(block, ext, call).await }) + .buffer_unordered(10) + .collect::>>() + .await + .into_iter() + .collect::, _>>() + } + + /// Extract receipt from transaction + pub async fn extract_from_transaction( + &self, + block: &SubstrateBlock, + transaction_index: usize, + ) -> Result<(TransactionSigned, ReceiptInfo), ClientError> { + let extrinsics = block.extrinsics().await?; + let ext = extrinsics + .iter() + .nth(transaction_index) + .ok_or(ClientError::EthExtrinsicNotFound)?; + + let call = ext + .as_extrinsic::()? + .ok_or_else(|| ClientError::EthExtrinsicNotFound)?; + self.extract_from_extrinsic(block, ext, call).await + } +} diff --git a/substrate/frame/revive/rpc/src/receipt_provider.rs b/substrate/frame/revive/rpc/src/receipt_provider.rs index 5c102b3d3d41a..7c5e33cf01e39 100644 --- a/substrate/frame/revive/rpc/src/receipt_provider.rs +++ b/substrate/frame/revive/rpc/src/receipt_provider.rs @@ -15,23 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - client::SubstrateBlock, - subxt_client::{ - revive::{calls::types::EthTransact, events::ContractEmitted}, - system::events::ExtrinsicSuccess, - transaction_payment::events::TransactionFeePaid, - SrcChainConfig, - }, - ClientError, LOG_TARGET, -}; -use futures::{stream, StreamExt}; use jsonrpsee::core::async_trait; -use pallet_revive::{ - create1, - evm::{GenericTransaction, Log, ReceiptInfo, TransactionSigned, H256, U256}, -}; -use sp_core::keccak_256; +use pallet_revive::evm::{ReceiptInfo, TransactionSigned, H256}; use tokio::join; mod cache; @@ -53,7 +38,7 @@ pub trait ReceiptProvider: Send + Sync { async fn receipt_by_block_hash_and_index( &self, block_hash: &H256, - transaction_index: &U256, + transaction_index: usize, ) -> Option; /// Get the number of receipts per block. @@ -79,7 +64,7 @@ impl ReceiptProvider for (Main async fn receipt_by_block_hash_and_index( &self, block_hash: &H256, - transaction_index: &U256, + transaction_index: usize, ) -> Option { if let Some(receipt) = self.0.receipt_by_block_hash_and_index(block_hash, transaction_index).await @@ -111,130 +96,3 @@ impl ReceiptProvider for (Main self.1.signed_tx_by_hash(hash).await } } - -/// Extract a [`TransactionSigned`] and a [`ReceiptInfo`] and from an extrinsic. -pub async fn extract_receipt_from_extrinsic( - block: &SubstrateBlock, - ext: subxt::blocks::ExtrinsicDetails>, - call: EthTransact, -) -> Result<(TransactionSigned, ReceiptInfo), ClientError> { - let transaction_index = ext.index(); - let block_number = U256::from(block.number()); - let block_hash = block.hash(); - let events = ext.events().await?; - - let success = events.has::().inspect_err(|err| { - log::debug!(target: LOG_TARGET, "Failed to lookup for ExtrinsicSuccess event in block {block_number}: {err:?}") - })?; - let tx_fees = events - .find_first::()? - .ok_or(ClientError::TxFeeNotFound) - .inspect_err( - |err| log::debug!(target: LOG_TARGET, "TransactionFeePaid not found in events for block {block_number}\n{err:?}") - )?; - let transaction_hash = H256(keccak_256(&call.payload)); - - let signed_tx = - TransactionSigned::decode(&call.payload).map_err(|_| ClientError::TxDecodingFailed)?; - let from = signed_tx.recover_eth_address().map_err(|_| { - log::error!(target: LOG_TARGET, "Failed to recover eth address from signed tx"); - ClientError::RecoverEthAddressFailed - })?; - - let tx_info = GenericTransaction::from_signed(signed_tx.clone(), Some(from)); - let gas_price = tx_info.gas_price.unwrap_or_default(); - let gas_used = (tx_fees.tip.saturating_add(tx_fees.actual_fee)) - .checked_div(gas_price.as_u128()) - .unwrap_or_default(); - - // get logs from ContractEmitted event - let logs = events - .iter() - .filter_map(|event_details| { - let event_details = event_details.ok()?; - let event = event_details.as_event::().ok()??; - - Some(Log { - address: event.contract, - topics: event.topics, - data: Some(event.data.into()), - block_number: Some(block_number), - transaction_hash, - transaction_index: Some(transaction_index.into()), - block_hash: Some(block_hash), - log_index: Some(event_details.index().into()), - ..Default::default() - }) - }) - .collect(); - - let contract_address = if tx_info.to.is_none() { - Some(create1( - &from, - tx_info - .nonce - .unwrap_or_default() - .try_into() - .map_err(|_| ClientError::ConversionFailed)?, - )) - } else { - None - }; - - log::debug!(target: LOG_TARGET, "Adding receipt for tx hash: {transaction_hash:?} - block: {block_number:?}"); - let receipt = ReceiptInfo::new( - block_hash, - block_number, - contract_address, - from, - logs, - tx_info.to, - gas_price, - gas_used.into(), - success, - transaction_hash, - transaction_index.into(), - tx_info.r#type.unwrap_or_default(), - ); - Ok((signed_tx, receipt)) -} - -/// Extract receipts from block. -pub async fn extract_receipts_from_block( - block: &SubstrateBlock, -) -> Result, ClientError> { - // Filter extrinsics from pallet_revive - let extrinsics = block.extrinsics().await.inspect_err(|err| { - log::debug!(target: LOG_TARGET, "Error fetching for #{:?} extrinsics: {err:?}", block.number()); - })?; - - let extrinsics = extrinsics.iter().flat_map(|ext| { - let call = ext.as_extrinsic::().ok()??; - Some((ext, call)) - }); - - stream::iter(extrinsics) - .map(|(ext, call)| async move { extract_receipt_from_extrinsic(block, ext, call).await }) - .buffer_unordered(10) - .collect::>>() - .await - .into_iter() - .collect::, _>>() -} - -/// Extract receipt from transaction -pub async fn extract_receipts_from_transaction( - block: &SubstrateBlock, - transaction_index: usize, -) -> Result<(TransactionSigned, ReceiptInfo), ClientError> { - let extrinsics = block.extrinsics().await?; - let ext = extrinsics - .iter() - .nth(transaction_index) - .ok_or(ClientError::EthExtrinsicNotFound)?; - - let call = ext - .as_extrinsic::()? - .ok_or_else(|| ClientError::EthExtrinsicNotFound)?; - extract_receipt_from_extrinsic(block, ext, call).await -} diff --git a/substrate/frame/revive/rpc/src/receipt_provider/cache.rs b/substrate/frame/revive/rpc/src/receipt_provider/cache.rs index 39124929ec07d..a4741d18a3b34 100644 --- a/substrate/frame/revive/rpc/src/receipt_provider/cache.rs +++ b/substrate/frame/revive/rpc/src/receipt_provider/cache.rs @@ -16,7 +16,7 @@ // limitations under the License. use super::ReceiptProvider; use jsonrpsee::core::async_trait; -use pallet_revive::evm::{ReceiptInfo, TransactionSigned, H256, U256}; +use pallet_revive::evm::{ReceiptInfo, TransactionSigned, H256}; use std::{collections::HashMap, sync::Arc}; use tokio::sync::RwLock; @@ -48,13 +48,13 @@ impl ReceiptProvider for CacheReceiptProvider { async fn receipt_by_block_hash_and_index( &self, block_hash: &H256, - transaction_index: &U256, + transaction_index: usize, ) -> Option { let cache = self.cache().await; let receipt_hash = cache .transaction_hashes_by_block_and_index .get(block_hash)? - .get(transaction_index)?; + .get(&transaction_index)?; let receipt = cache.receipts_by_hash.get(receipt_hash)?; Some(receipt.clone()) } @@ -84,7 +84,7 @@ struct ReceiptCache { signed_tx_by_hash: HashMap, /// A map of receipt hashes by block hash. - transaction_hashes_by_block_and_index: HashMap>, + transaction_hashes_by_block_and_index: HashMap>, } impl ReceiptCache { @@ -93,7 +93,9 @@ impl ReceiptCache { if !receipts.is_empty() { let values = receipts .iter() - .map(|(_, receipt)| (receipt.transaction_index, receipt.transaction_hash)) + .map(|(_, receipt)| { + (receipt.transaction_index.as_usize(), receipt.transaction_hash) + }) .collect::>(); self.transaction_hashes_by_block_and_index.insert(*block_hash, values); diff --git a/substrate/frame/revive/rpc/src/receipt_provider/db.rs b/substrate/frame/revive/rpc/src/receipt_provider/db.rs index 63917d6193ea7..15f4119b4aefd 100644 --- a/substrate/frame/revive/rpc/src/receipt_provider/db.rs +++ b/substrate/frame/revive/rpc/src/receipt_provider/db.rs @@ -16,10 +16,10 @@ // limitations under the License. use super::*; -use crate::BlockInfoProvider; +use crate::{BlockInfoProvider, ReceiptExtractor}; use jsonrpsee::core::async_trait; use pallet_revive::evm::{ReceiptInfo, TransactionSigned}; -use sp_core::{H256, U256}; +use sp_core::H256; use sqlx::{query, SqlitePool}; use std::sync::Arc; @@ -30,6 +30,8 @@ pub struct DBReceiptProvider { pool: SqlitePool, /// The block provider used to fetch blocks, and reconstruct receipts. block_provider: Arc, + /// A means to extract receipts from extrinsics. + receipt_extractor: ReceiptExtractor, /// weather or not we should write to the DB. read_only: bool, } @@ -40,9 +42,10 @@ impl DBReceiptProvider { database_url: &str, read_only: bool, block_provider: Arc, + receipt_extractor: ReceiptExtractor, ) -> Result { let pool = SqlitePool::connect(database_url).await?; - Ok(Self { pool, block_provider, read_only }) + Ok(Self { pool, block_provider, read_only, receipt_extractor }) } async fn fetch_row(&self, transaction_hash: &H256) -> Option<(H256, usize)> { @@ -125,12 +128,14 @@ impl ReceiptProvider for DBReceiptProvider { async fn receipt_by_block_hash_and_index( &self, block_hash: &H256, - transaction_index: &U256, + transaction_index: usize, ) -> Option { let block = self.block_provider.block_by_hash(block_hash).await.ok()??; - let transaction_index: usize = transaction_index.as_usize(); // TODO: check for overflow - let (_, receipt) = - extract_receipts_from_transaction(&block, transaction_index).await.ok()?; + let (_, receipt) = self + .receipt_extractor + .extract_from_transaction(&block, transaction_index) + .await + .ok()?; Some(receipt) } @@ -138,8 +143,11 @@ impl ReceiptProvider for DBReceiptProvider { let (block_hash, transaction_index) = self.fetch_row(transaction_hash).await?; let block = self.block_provider.block_by_hash(&block_hash).await.ok()??; - let (_, receipt) = - extract_receipts_from_transaction(&block, transaction_index).await.ok()?; + let (_, receipt) = self + .receipt_extractor + .extract_from_transaction(&block, transaction_index) + .await + .ok()?; Some(receipt) } @@ -161,8 +169,11 @@ impl ReceiptProvider for DBReceiptProvider { let transaction_index = result.transaction_index.try_into().ok()?; let block = self.block_provider.block_by_hash(&block_hash).await.ok()??; - let (signed_tx, _) = - extract_receipts_from_transaction(&block, transaction_index).await.ok()?; + let (signed_tx, _) = self + .receipt_extractor + .extract_from_transaction(&block, transaction_index) + .await + .ok()?; Some(signed_tx) } } @@ -179,6 +190,7 @@ mod tests { DBReceiptProvider { pool, block_provider: Arc::new(MockBlockInfoProvider {}), + receipt_extractor: ReceiptExtractor::new(1_000_000), read_only: false, } } diff --git a/substrate/frame/revive/rpc/src/tests.rs b/substrate/frame/revive/rpc/src/tests.rs index e64e16d45b2ae..e1ac274d32ea8 100644 --- a/substrate/frame/revive/rpc/src/tests.rs +++ b/substrate/frame/revive/rpc/src/tests.rs @@ -18,7 +18,7 @@ use crate::{ cli::{self, CliCommand}, - example::{wait_for_successful_receipt, TransactionBuilder}, + example::TransactionBuilder, EthRpcClient, }; use clap::Parser; @@ -29,7 +29,7 @@ use pallet_revive::{ evm::{Account, BlockTag, U256}, }; use static_init::dynamic; -use std::thread; +use std::{sync::Arc, thread}; use substrate_cli_test_utils::*; /// Create a websocket client with a 120s timeout. @@ -117,19 +117,15 @@ macro_rules! unwrap_call_err( #[tokio::test] async fn transfer() -> anyhow::Result<()> { let _lock = SHARED_RESOURCES.write(); - let client = SharedResources::client().await; + let client = Arc::new(SharedResources::client().await); let ethan = Account::from(subxt_signer::eth::dev::ethan()); let initial_balance = client.get_balance(ethan.address(), BlockTag::Latest.into()).await?; let value = 1_000_000_000_000_000_000_000u128.into(); - let hash = TransactionBuilder::default() - .value(value) - .to(ethan.address()) - .send(&client) - .await?; + let tx = TransactionBuilder::new(&client).value(value).to(ethan.address()).send().await?; - let receipt = wait_for_successful_receipt(&client, hash).await?; + let receipt = tx.wait_for_receipt().await?; assert_eq!( Some(ethan.address()), receipt.to, @@ -145,7 +141,7 @@ async fn transfer() -> anyhow::Result<()> { #[tokio::test] async fn deploy_and_call() -> anyhow::Result<()> { let _lock = SHARED_RESOURCES.write(); - let client = SharedResources::client().await; + let client = std::sync::Arc::new(SharedResources::client().await); let account = Account::default(); // Balance transfer @@ -153,13 +149,9 @@ async fn deploy_and_call() -> anyhow::Result<()> { let initial_balance = client.get_balance(ethan.address(), BlockTag::Latest.into()).await?; let value = 1_000_000_000_000_000_000_000u128.into(); - let hash = TransactionBuilder::default() - .value(value) - .to(ethan.address()) - .send(&client) - .await?; + let tx = TransactionBuilder::new(&client).value(value).to(ethan.address()).send().await?; - let receipt = wait_for_successful_receipt(&client, hash).await?; + let receipt = tx.wait_for_receipt().await?; assert_eq!( Some(ethan.address()), receipt.to, @@ -175,8 +167,8 @@ async fn deploy_and_call() -> anyhow::Result<()> { let (bytes, _) = pallet_revive_fixtures::compile_module("dummy")?; let input = bytes.into_iter().chain(data.clone()).collect::>(); let nonce = client.get_transaction_count(account.address(), BlockTag::Latest.into()).await?; - let hash = TransactionBuilder::default().value(value).input(input).send(&client).await?; - let receipt = wait_for_successful_receipt(&client, hash).await?; + let tx = TransactionBuilder::new(&client).value(value).input(input).send().await?; + let receipt = tx.wait_for_receipt().await?; let contract_address = create1(&account.address(), nonce.try_into().unwrap()); assert_eq!( Some(contract_address), @@ -188,12 +180,12 @@ async fn deploy_and_call() -> anyhow::Result<()> { assert_eq!(value, balance, "Contract balance should be the same as the value sent."); // Call contract - let hash = TransactionBuilder::default() + let tx = TransactionBuilder::new(&client) .value(value) .to(contract_address) - .send(&client) + .send() .await?; - let receipt = wait_for_successful_receipt(&client, hash).await?; + let receipt = tx.wait_for_receipt().await?; assert_eq!( Some(contract_address), @@ -206,13 +198,13 @@ async fn deploy_and_call() -> anyhow::Result<()> { // Balance transfer to contract let balance = client.get_balance(contract_address, BlockTag::Latest.into()).await?; - let hash = TransactionBuilder::default() + let tx = TransactionBuilder::new(&client) .value(value) .to(contract_address) - .send(&client) + .send() .await?; - wait_for_successful_receipt(&client, hash).await?; + tx.wait_for_receipt().await?; let increase = client.get_balance(contract_address, BlockTag::Latest.into()).await? - balance; assert_eq!(value, increase, "contract's balance should have increased by the value sent."); Ok(()) @@ -221,17 +213,19 @@ async fn deploy_and_call() -> anyhow::Result<()> { #[tokio::test] async fn revert_call() -> anyhow::Result<()> { let _lock = SHARED_RESOURCES.write(); - let client = SharedResources::client().await; + let client = Arc::new(SharedResources::client().await); let (bytecode, contract) = get_contract("Errors")?; - let receipt = TransactionBuilder::default() + let receipt = TransactionBuilder::new(&client) .input(bytecode) - .send_and_wait_for_receipt(&client) + .send() + .await? + .wait_for_receipt() .await?; - let err = TransactionBuilder::default() + let err = TransactionBuilder::new(&client) .to(receipt.contract_address.unwrap()) .input(contract.function("triggerRequireError")?.encode_input(&[])?.to_vec()) - .send(&client) + .send() .await .unwrap_err(); @@ -244,17 +238,21 @@ async fn revert_call() -> anyhow::Result<()> { #[tokio::test] async fn event_logs() -> anyhow::Result<()> { let _lock = SHARED_RESOURCES.write(); - let client = SharedResources::client().await; + let client = Arc::new(SharedResources::client().await); let (bytecode, contract) = get_contract("EventExample")?; - let receipt = TransactionBuilder::default() + let receipt = TransactionBuilder::new(&client) .input(bytecode) - .send_and_wait_for_receipt(&client) + .send() + .await? + .wait_for_receipt() .await?; - let receipt = TransactionBuilder::default() + let receipt = TransactionBuilder::new(&client) .to(receipt.contract_address.unwrap()) .input(contract.function("triggerEvent")?.encode_input(&[])?.to_vec()) - .send_and_wait_for_receipt(&client) + .send() + .await? + .wait_for_receipt() .await?; assert_eq!(receipt.logs.len(), 1, "There should be one log."); Ok(()) @@ -263,14 +261,14 @@ async fn event_logs() -> anyhow::Result<()> { #[tokio::test] async fn invalid_transaction() -> anyhow::Result<()> { let _lock = SHARED_RESOURCES.write(); - let client = SharedResources::client().await; + let client = Arc::new(SharedResources::client().await); let ethan = Account::from(subxt_signer::eth::dev::ethan()); - let err = TransactionBuilder::default() + let err = TransactionBuilder::new(&client) .value(U256::from(1_000_000_000_000u128)) .to(ethan.address()) .mutate(|tx| tx.chain_id = Some(42u32.into())) - .send(&client) + .send() .await .unwrap_err(); @@ -283,28 +281,32 @@ async fn invalid_transaction() -> anyhow::Result<()> { #[tokio::test] async fn native_evm_ratio_works() -> anyhow::Result<()> { let _lock = SHARED_RESOURCES.write(); - let client = SharedResources::client().await; + let client = Arc::new(SharedResources::client().await); let (bytecode, contract) = get_contract("PiggyBank")?; - let contract_address = TransactionBuilder::default() + let contract_address = TransactionBuilder::new(&client) .input(bytecode) - .send_and_wait_for_receipt(&client) + .send() + .await? + .wait_for_receipt() .await? .contract_address .unwrap(); let value = 10_000_000_000_000_000_000u128; // 10 eth - TransactionBuilder::default() + TransactionBuilder::new(&client) .to(contract_address) .input(contract.function("deposit")?.encode_input(&[])?.to_vec()) .value(value.into()) - .send_and_wait_for_receipt(&client) + .send() + .await? + .wait_for_receipt() .await?; let contract_value = client.get_balance(contract_address, BlockTag::Latest.into()).await?; assert_eq!(contract_value, value.into()); let withdraw_value = 1_000_000_000_000_000_000u128; // 1 eth - TransactionBuilder::default() + TransactionBuilder::new(&client) .to(contract_address) .input( contract @@ -312,7 +314,9 @@ async fn native_evm_ratio_works() -> anyhow::Result<()> { .encode_input(&[Token::Uint(withdraw_value.into())])? .to_vec(), ) - .send_and_wait_for_receipt(&client) + .send() + .await? + .wait_for_receipt() .await?; let contract_value = client.get_balance(contract_address, BlockTag::Latest.into()).await?;