From f265d92d35e005720f686caa3a35af12d23deded Mon Sep 17 00:00:00 2001 From: Haardik Date: Mon, 8 Dec 2025 15:08:19 +0530 Subject: [PATCH 1/6] move contracts into test-utils --- .gitmodules | 8 ++++---- contracts/lib/forge-std | 1 - {contracts => crates/test-utils/contracts}/.gitignore | 0 {contracts => crates/test-utils/contracts}/README.md | 0 {contracts => crates/test-utils/contracts}/foundry.lock | 0 {contracts => crates/test-utils/contracts}/foundry.toml | 0 crates/test-utils/contracts/lib/forge-std | 1 + {contracts => crates/test-utils/contracts}/lib/solmate | 0 .../test-utils/contracts}/script/Counter.s.sol | 0 .../test-utils/contracts}/src/Counter.sol | 0 .../test-utils/contracts}/test/Counter.t.sol | 0 11 files changed, 5 insertions(+), 5 deletions(-) delete mode 160000 contracts/lib/forge-std rename {contracts => crates/test-utils/contracts}/.gitignore (100%) rename {contracts => crates/test-utils/contracts}/README.md (100%) rename {contracts => crates/test-utils/contracts}/foundry.lock (100%) rename {contracts => crates/test-utils/contracts}/foundry.toml (100%) create mode 160000 crates/test-utils/contracts/lib/forge-std rename {contracts => crates/test-utils/contracts}/lib/solmate (100%) rename {contracts => crates/test-utils/contracts}/script/Counter.s.sol (100%) rename {contracts => crates/test-utils/contracts}/src/Counter.sol (100%) rename {contracts => crates/test-utils/contracts}/test/Counter.t.sol (100%) diff --git a/.gitmodules b/.gitmodules index 29187eb2..3ad82350 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "contracts/lib/forge-std"] - path = contracts/lib/forge-std +[submodule "crates/test-utils/contracts/lib/forge-std"] + path = crates/test-utils/contracts/lib/forge-std url = https://github.com/foundry-rs/forge-std -[submodule "contracts/lib/solmate"] - path = contracts/lib/solmate +[submodule "crates/test-utils/contracts/lib/solmate"] + path = crates/test-utils/contracts/lib/solmate url = https://github.com/transmissions11/solmate diff --git a/contracts/lib/forge-std b/contracts/lib/forge-std deleted file mode 160000 index 8e40513d..00000000 --- a/contracts/lib/forge-std +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e40513d678f392f398620b3ef2b418648b33e89 diff --git a/contracts/.gitignore b/crates/test-utils/contracts/.gitignore similarity index 100% rename from contracts/.gitignore rename to crates/test-utils/contracts/.gitignore diff --git a/contracts/README.md b/crates/test-utils/contracts/README.md similarity index 100% rename from contracts/README.md rename to crates/test-utils/contracts/README.md diff --git a/contracts/foundry.lock b/crates/test-utils/contracts/foundry.lock similarity index 100% rename from contracts/foundry.lock rename to crates/test-utils/contracts/foundry.lock diff --git a/contracts/foundry.toml b/crates/test-utils/contracts/foundry.toml similarity index 100% rename from contracts/foundry.toml rename to crates/test-utils/contracts/foundry.toml diff --git a/crates/test-utils/contracts/lib/forge-std b/crates/test-utils/contracts/lib/forge-std new file mode 160000 index 00000000..7117c90c --- /dev/null +++ b/crates/test-utils/contracts/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 7117c90c8cf6c68e5acce4f09a6b24715cea4de6 diff --git a/contracts/lib/solmate b/crates/test-utils/contracts/lib/solmate similarity index 100% rename from contracts/lib/solmate rename to crates/test-utils/contracts/lib/solmate diff --git a/contracts/script/Counter.s.sol b/crates/test-utils/contracts/script/Counter.s.sol similarity index 100% rename from contracts/script/Counter.s.sol rename to crates/test-utils/contracts/script/Counter.s.sol diff --git a/contracts/src/Counter.sol b/crates/test-utils/contracts/src/Counter.sol similarity index 100% rename from contracts/src/Counter.sol rename to crates/test-utils/contracts/src/Counter.sol diff --git a/contracts/test/Counter.t.sol b/crates/test-utils/contracts/test/Counter.t.sol similarity index 100% rename from contracts/test/Counter.t.sol rename to crates/test-utils/contracts/test/Counter.t.sol From 1fb5fe45d12f8caadf9ba02e4755410f73769d14 Mon Sep 17 00:00:00 2001 From: Haardik Date: Mon, 8 Dec 2025 18:15:49 +0530 Subject: [PATCH 2/6] refactor existing rpc tests to use foundry contracts --- Cargo.lock | 28 + Cargo.toml | 3 + Justfile | 4 + crates/rpc/Cargo.toml | 3 + crates/rpc/tests/flashblocks_rpc.rs | 592 ++++++++++-------- .../test-utils/contracts/script/Counter.s.sol | 19 - .../script/DeployDoubleCounter.s.sol | 15 + crates/test-utils/contracts/src/Counter.sol | 14 - .../contracts/src/DoubleCounter.sol | 15 + .../test-utils/contracts/test/Counter.t.sol | 24 - crates/test-utils/src/accounts.rs | 83 ++- 11 files changed, 451 insertions(+), 349 deletions(-) delete mode 100644 crates/test-utils/contracts/script/Counter.s.sol create mode 100644 crates/test-utils/contracts/script/DeployDoubleCounter.s.sol delete mode 100644 crates/test-utils/contracts/src/Counter.sol create mode 100644 crates/test-utils/contracts/src/DoubleCounter.sol delete mode 100644 crates/test-utils/contracts/test/Counter.t.sol diff --git a/Cargo.lock b/Cargo.lock index 85ee3bc7..81851777 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,6 +151,28 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-contract" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69af404f1d00ddb42f2419788fa87746a4cd13bab271916d7726fda6c792d94" +dependencies = [ + "alloy-consensus", + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "futures", + "futures-util", + "serde_json", + "thiserror 2.0.17", +] + [[package]] name = "alloy-dyn-abi" version = "1.4.1" @@ -759,6 +781,7 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" dependencies = [ + "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck", @@ -777,12 +800,14 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" dependencies = [ + "alloy-json-abi", "const-hex", "dunce", "heck", "macro-string", "proc-macro2", "quote", + "serde_json", "syn 2.0.111", "syn-solidity", ] @@ -1613,6 +1638,7 @@ name = "base-reth-rpc" version = "0.2.1" dependencies = [ "alloy-consensus", + "alloy-contract", "alloy-eips", "alloy-genesis", "alloy-primitives", @@ -1621,6 +1647,8 @@ dependencies = [ "alloy-rpc-types", "alloy-rpc-types-engine", "alloy-rpc-types-eth", + "alloy-sol-macro", + "alloy-sol-types", "base-reth-flashblocks", "base-reth-test-utils", "eyre", diff --git a/Cargo.toml b/Cargo.toml index b4694d27..63226771 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,6 +107,9 @@ alloy-provider = { version = "1.0.41" } alloy-hardforks = "0.4.4" alloy-rpc-client = { version = "1.0.41" } alloy-serde = { version = "1.0.41" } +alloy-sol-macro = { version = "1.4.1", features = ["json"] } +alloy-sol-types = { version = "1.4.1" } +alloy-contract = { version = "1.0.41" } # op-alloy op-alloy-rpc-types = { version = "0.22.0", default-features = false } diff --git a/Justfile b/Justfile index b07ec1ab..e08b102c 100644 --- a/Justfile +++ b/Justfile @@ -80,6 +80,10 @@ build-maxperf: build-node: cargo build --bin base-reth-node +# Build the contracts used for tests +build-contracts: + cd crates/test-utils/contracts && forge build + # Cleans the workspace clean: cargo clean diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index 8850898c..a29f4b44 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -76,6 +76,9 @@ futures-util.workspace = true alloy-rpc-types-eth.workspace = true alloy-rpc-types-engine.workspace = true alloy-provider.workspace = true +alloy-sol-macro.workspace = true +alloy-sol-types.workspace = true +alloy-contract.workspace = true [package.metadata.cargo-udeps.ignore] normal = ["reth-optimism-cli"] diff --git a/crates/rpc/tests/flashblocks_rpc.rs b/crates/rpc/tests/flashblocks_rpc.rs index 89bd37c7..17514ab8 100644 --- a/crates/rpc/tests/flashblocks_rpc.rs +++ b/crates/rpc/tests/flashblocks_rpc.rs @@ -4,7 +4,7 @@ mod common; use std::str::FromStr; -use alloy_consensus::Receipt; +use alloy_consensus::{Receipt, Transaction}; use alloy_eips::BlockNumberOrTag; use alloy_primitives::{ Address, B256, Bytes, LogData, TxHash, U256, address, b256, bytes, map::HashMap, @@ -14,6 +14,7 @@ use alloy_rpc_client::RpcClient; use alloy_rpc_types::simulate::{SimBlock, SimulatePayload}; use alloy_rpc_types_engine::PayloadId; use alloy_rpc_types_eth::{TransactionInput, error::EthRpcErrorCode}; +use alloy_sol_macro::sol; use base_reth_flashblocks::{Flashblock, Metadata}; use base_reth_test_utils::flashblocks_harness::FlashblocksHarness; use common::{BLOCK_INFO_TXN, BLOCK_INFO_TXN_HASH}; @@ -22,20 +23,246 @@ use futures_util::{SinkExt, StreamExt}; use op_alloy_consensus::OpDepositReceipt; use op_alloy_network::{Optimism, ReceiptResponse, TransactionResponse}; use op_alloy_rpc_types::OpTransactionRequest; +use reth::revm::context::TransactionType; use reth_optimism_primitives::OpReceipt; use reth_rpc_eth_api::RpcReceipt; use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; use serde_json::json; use tokio_tungstenite::{connect_async, tungstenite::Message}; +use crate::DoubleCounterContract::DoubleCounterContractInstance; + +sol!( + #[sol(rpc)] + DoubleCounterContract, + concat!( + env!("CARGO_MANIFEST_DIR"), + "/../test-utils/contracts/out/DoubleCounter.sol/DoubleCounter.json" + ) +); + struct TestSetup { harness: FlashblocksHarness, + txn_details: TransactionDetails, +} + +struct TransactionDetails { + counter_deployment_tx: Bytes, + counter_deployment_hash: TxHash, + counter_address: Address, + + counter_increment_tx: Bytes, + counter_increment_hash: TxHash, + + counter_increment2_tx: Bytes, + counter_increment2_hash: TxHash, + + alice_eth_transfer_tx: Bytes, + alice_eth_transfer_hash: TxHash, } impl TestSetup { async fn new() -> Result { let harness = FlashblocksHarness::new().await?; - Ok(Self { harness }) + + let provider = harness.provider(); + let deployer = &harness.accounts().deployer; + let alice = &harness.accounts().alice; + let bob = &harness.accounts().bob; + + let (counter_deployment_tx, counter_address, counter_deployment_hash) = deployer + .create_deployment_tx(DoubleCounterContract::BYTECODE.clone(), 0) + .expect("should be able to sign DoubleCounter deployment txn"); + let counter = DoubleCounterContractInstance::new(counter_address.clone(), provider); + let (increment1_tx, increment1_tx_hash) = deployer + .sign_txn_request(counter.increment().into_transaction_request().nonce(1)) + .expect("should be able to sign increment() txn"); + let (increment2_tx, increment2_tx_hash) = deployer + .sign_txn_request(counter.increment2().into_transaction_request().nonce(2)) + .expect("should be able to sign increment2() txn"); + let (eth_transfer_tx, eth_transfer_hash) = alice + .sign_txn_request( + OpTransactionRequest::default() + .from(alice.address) + .transaction_type(TransactionType::Eip1559.into()) + .gas_limit(100_000) + .nonce(0) + .to(bob.address) + .value(U256::from_str("999999999000000000000000").unwrap()) + .into(), + ) + .expect("should be able to sign eth transfer txn"); + + let txn_details = TransactionDetails { + counter_deployment_tx, + counter_deployment_hash, + counter_address, + counter_increment_tx: increment1_tx, + counter_increment_hash: increment1_tx_hash, + counter_increment2_tx: increment2_tx, + counter_increment2_hash: increment2_tx_hash, + alice_eth_transfer_tx: eth_transfer_tx, + alice_eth_transfer_hash: eth_transfer_hash, + }; + + Ok(Self { harness, txn_details }) + } + + fn create_first_payload(&self) -> Flashblock { + Flashblock { + payload_id: PayloadId::new([0; 8]), + index: 0, + base: Some(ExecutionPayloadBaseV1 { + parent_beacon_block_root: B256::default(), + parent_hash: B256::default(), + fee_recipient: Address::ZERO, + prev_randao: B256::default(), + block_number: 1, + gas_limit: 30_000_000, + timestamp: 0, + extra_data: Bytes::new(), + base_fee_per_gas: U256::ZERO, + }), + diff: ExecutionPayloadFlashblockDeltaV1 { + blob_gas_used: Some(0), + transactions: vec![BLOCK_INFO_TXN], + ..Default::default() + }, + metadata: Metadata { + block_number: 1, + receipts: { + let mut receipts = HashMap::default(); + receipts.insert( + BLOCK_INFO_TXN_HASH, + OpReceipt::Deposit(OpDepositReceipt { + inner: Receipt { + status: true.into(), + cumulative_gas_used: 10000, + logs: vec![], + }, + deposit_nonce: Some(4012991u64), + deposit_receipt_version: None, + }), + ); + receipts + }, + new_account_balances: HashMap::default(), + }, + } + } + + fn create_second_payload(&self) -> Flashblock { + Flashblock { + payload_id: PayloadId::new([0; 8]), + index: 1, + base: None, + diff: ExecutionPayloadFlashblockDeltaV1 { + state_root: B256::default(), + receipts_root: B256::default(), + gas_used: 0, + block_hash: B256::default(), + blob_gas_used: Some(0), + transactions: vec![ + DEPOSIT_TX, + self.txn_details.alice_eth_transfer_tx.clone(), + self.txn_details.counter_deployment_tx.clone(), + self.txn_details.counter_increment_tx.clone(), + self.txn_details.counter_increment2_tx.clone(), + ], + withdrawals: Vec::new(), + logs_bloom: Default::default(), + withdrawals_root: Default::default(), + }, + metadata: Metadata { + block_number: 1, + receipts: { + let mut receipts = HashMap::default(); + receipts.insert( + DEPOSIT_TX_HASH, + OpReceipt::Deposit(OpDepositReceipt { + inner: Receipt { + status: true.into(), + cumulative_gas_used: 31000, + logs: vec![], + }, + deposit_nonce: Some(4012992u64), + deposit_receipt_version: None, + }), + ); + receipts.insert( + self.txn_details.alice_eth_transfer_hash, + OpReceipt::Legacy(Receipt { + status: true.into(), + cumulative_gas_used: 55000, + logs: vec![], + }), + ); + receipts.insert( + self.txn_details.counter_deployment_hash, + OpReceipt::Legacy(Receipt { + status: true.into(), + cumulative_gas_used: 272279, + logs: vec![], + }), + ); + receipts.insert( + self.txn_details.counter_increment_hash, + OpReceipt::Legacy(Receipt { + status: true.into(), + cumulative_gas_used: 272279 + 44000, + logs: vec![ + alloy_primitives::Log { + address: self.txn_details.counter_address, + data: LogData::new( + vec![TEST_LOG_TOPIC_0, TEST_LOG_TOPIC_1, TEST_LOG_TOPIC_2], + bytes!("0x0000000000000000000000000000000000000000000000000de0b6b3a7640000").into(), // 1 ETH in wei + ) + .unwrap(), + }, + alloy_primitives::Log { + address: TEST_ADDRESS, + data: LogData::new( + vec![TEST_LOG_TOPIC_0], + bytes!("0x0000000000000000000000000000000000000000000000000000000000000001").into(), // Value: 1 + ) + .unwrap(), + }, + ] + }), + ); + receipts.insert( + self.txn_details.counter_increment2_hash, + OpReceipt::Legacy(Receipt { + status: true.into(), + cumulative_gas_used: 272279 + 44000 + 44000, + logs: vec![], + }), + ); + receipts + }, + new_account_balances: { + let mut map = HashMap::default(); + map.insert(TEST_ADDRESS, U256::from(PENDING_BALANCE)); + map + }, + }, + } + } + + fn count1(&self) -> OpTransactionRequest { + let counter = DoubleCounterContractInstance::new( + self.txn_details.counter_address, + self.harness.provider(), + ); + counter.count1().into_transaction_request() + } + + fn count2(&self) -> OpTransactionRequest { + let counter = DoubleCounterContractInstance::new( + self.txn_details.counter_address, + self.harness.provider(), + ); + counter.count2().into_transaction_request() } async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { @@ -43,10 +270,10 @@ impl TestSetup { } async fn send_test_payloads(&self) -> Result<()> { - let base_payload = create_first_payload(); + let base_payload = self.create_first_payload(); self.send_flashblock(base_payload).await?; - let second_payload = create_second_payload(); + let second_payload = self.create_second_payload(); self.send_flashblock(second_payload).await?; Ok(()) @@ -73,22 +300,11 @@ const TEST_ADDRESS: Address = address!("0x12345678901234567890123456789012345678 const PENDING_BALANCE: u64 = 4660; const DEPOSIT_SENDER: Address = address!("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001"); -const TX_SENDER: Address = address!("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"); - +const DEPOSIT_TX: Bytes = bytes!( + "0x7ef8f8a042a8ae5ec231af3d0f90f68543ec8bca1da4f7edd712d5b51b490688355a6db794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e200000044d000a118b00000000000000040000000067cb7cb0000000000077dbd4000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000014edd27304108914dd6503b19b9eeb9956982ef197febbeeed8a9eac3dbaaabdf000000000000000000000000fc56e7272eebbba5bc6c544e159483c4a38f8ba3" +); const DEPOSIT_TX_HASH: TxHash = b256!("0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548"); -const TRANSFER_ETH_HASH: TxHash = - b256!("0xbb079fbde7d12fd01664483cd810e91014113e405247479e5615974ebca93e4a"); - -const DEPLOYMENT_HASH: TxHash = - b256!("0x2b14d58c13406f25a78cfb802fb711c0d2c27bf9eccaec2d1847dc4392918f63"); - -const INCREMENT_HASH: TxHash = - b256!("0x993ad6a332752f6748636ce899b3791e4a33f7eece82c0db4556c7339c1b2929"); -const INCREMENT2_HASH: TxHash = - b256!("0x617a3673399647d12bb82ec8eba2ca3fc468e99894bcf1c67eb50ef38ee615cb"); - -const COUNTER_ADDRESS: Address = address!("0xe7f1725e7734ce288f8367e1bb143e90bb3f0512"); // Test log topics - these represent common events const TEST_LOG_TOPIC_0: B256 = @@ -98,169 +314,6 @@ const TEST_LOG_TOPIC_1: B256 = const TEST_LOG_TOPIC_2: B256 = b256!("0x0000000000000000000000001234567890123456789012345678901234567890"); // To address -// Transaction bytes -const DEPOSIT_TX: Bytes = bytes!( - "0x7ef8f8a042a8ae5ec231af3d0f90f68543ec8bca1da4f7edd712d5b51b490688355a6db794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e200000044d000a118b00000000000000040000000067cb7cb0000000000077dbd4000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000014edd27304108914dd6503b19b9eeb9956982ef197febbeeed8a9eac3dbaaabdf000000000000000000000000fc56e7272eebbba5bc6c544e159483c4a38f8ba3" -); -const TRANSFER_ETH_TX: Bytes = bytes!( - "0x02f87383014a3480808449504f80830186a094deaddeaddeaddeaddeaddeaddeaddeaddead00018ad3c21bcb3f6efc39800080c0019f5a6fe2065583f4f3730e82e5725f651cbbaf11dc1f82c8d29ba1f3f99e5383a061e0bf5dfff4a9bc521ad426eee593d3653c5c330ae8a65fad3175d30f291d31" -); -const DEPLOYMENT_TX: Bytes = bytes!( - "0x02f9029483014a3401808449504f80830493e08080b9023c608060405260015f55600180553480156016575f80fd5b50610218806100245f395ff3fe608060405234801561000f575f80fd5b5060043610610060575f3560e01c80631d63e24d146100645780637477f70014610082578063a87d942c146100a0578063ab57b128146100be578063d09de08a146100c8578063d631c639146100d2575b5f80fd5b61006c6100f0565b6040516100799190610155565b60405180910390f35b61008a6100f6565b6040516100979190610155565b60405180910390f35b6100a86100fb565b6040516100b59190610155565b60405180910390f35b6100c6610103565b005b6100d061011c565b005b6100da610134565b6040516100e79190610155565b60405180910390f35b60015481565b5f5481565b5f8054905090565b60015f8154809291906101159061019b565b9190505550565b5f8081548092919061012d9061019b565b9190505550565b5f600154905090565b5f819050919050565b61014f8161013d565b82525050565b5f6020820190506101685f830184610146565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6101a58261013d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036101d7576101d661016e565b5b60018201905091905056fea264697066735822122025c7e02ddf460dece9c1e52a3f9ff042055b58005168e7825d7f6c426288c27164736f6c63430008190033c001a02f196658032e0b003bcd234349d63081f5d6c2785264c6fec6b25ad877ae326aa0290c9f96f4501439b07a7b5e8e938f15fc30a9c15db3fc5e654d44e1f522060c" -); -const INCREMENT_TX: Bytes = bytes!( - "0x02f86d83014a3402808449504f8082abe094e7f1725e7734ce288f8367e1bb143e90bb3f05128084d09de08ac080a0a9c1a565668084d4052bbd9bc3abce8555a06aed6651c82c2756ac8a83a79fa2a03427f440ce4910a5227ea0cedb60b06cf0bea2dbbac93bd37efa91a474c29d89" -); -const INCREMENT2_TX: Bytes = bytes!( - "0x02f86d83014a3403808449504f8082abe094e7f1725e7734ce288f8367e1bb143e90bb3f05128084ab57b128c001a03a155b8c81165fc8193aa739522c2a9e432e274adea7f0b90ef2b5078737f153a0288d7fad4a3b0d1e7eaf7fab63b298393a5020bf11d91ff8df13b235410799e2" -); - -fn create_test_logs() -> Vec { - vec![ - alloy_primitives::Log { - address: COUNTER_ADDRESS, - data: LogData::new( - vec![TEST_LOG_TOPIC_0, TEST_LOG_TOPIC_1, TEST_LOG_TOPIC_2], - bytes!("0x0000000000000000000000000000000000000000000000000de0b6b3a7640000").into(), // 1 ETH in wei - ) - .unwrap(), - }, - alloy_primitives::Log { - address: TEST_ADDRESS, - data: LogData::new( - vec![TEST_LOG_TOPIC_0], - bytes!("0x0000000000000000000000000000000000000000000000000000000000000001").into(), // Value: 1 - ) - .unwrap(), - }, - ] -} - -fn create_first_payload() -> Flashblock { - Flashblock { - payload_id: PayloadId::new([0; 8]), - index: 0, - base: Some(ExecutionPayloadBaseV1 { - parent_beacon_block_root: B256::default(), - parent_hash: B256::default(), - fee_recipient: Address::ZERO, - prev_randao: B256::default(), - block_number: 1, - gas_limit: 30_000_000, - timestamp: 0, - extra_data: Bytes::new(), - base_fee_per_gas: U256::ZERO, - }), - diff: ExecutionPayloadFlashblockDeltaV1 { - blob_gas_used: Some(0), - transactions: vec![BLOCK_INFO_TXN], - ..Default::default() - }, - metadata: Metadata { - block_number: 1, - receipts: { - let mut receipts = HashMap::default(); - receipts.insert( - BLOCK_INFO_TXN_HASH, - OpReceipt::Deposit(OpDepositReceipt { - inner: Receipt { - status: true.into(), - cumulative_gas_used: 10000, - logs: vec![], - }, - deposit_nonce: Some(4012991u64), - deposit_receipt_version: None, - }), - ); - receipts - }, - new_account_balances: HashMap::default(), - }, - } -} - -fn create_second_payload() -> Flashblock { - Flashblock { - payload_id: PayloadId::new([0; 8]), - index: 1, - base: None, - diff: ExecutionPayloadFlashblockDeltaV1 { - state_root: B256::default(), - receipts_root: B256::default(), - gas_used: 0, - block_hash: B256::default(), - blob_gas_used: Some(0), - transactions: vec![ - DEPOSIT_TX, - TRANSFER_ETH_TX, - DEPLOYMENT_TX, - INCREMENT_TX, - INCREMENT2_TX, - ], - withdrawals: Vec::new(), - logs_bloom: Default::default(), - withdrawals_root: Default::default(), - }, - metadata: Metadata { - block_number: 1, - receipts: { - let mut receipts = HashMap::default(); - receipts.insert( - DEPOSIT_TX_HASH, - OpReceipt::Deposit(OpDepositReceipt { - inner: Receipt { - status: true.into(), - cumulative_gas_used: 31000, - logs: vec![], - }, - deposit_nonce: Some(4012992u64), - deposit_receipt_version: None, - }), - ); - receipts.insert( - TRANSFER_ETH_HASH, - OpReceipt::Legacy(Receipt { - status: true.into(), - cumulative_gas_used: 55000, - logs: vec![], - }), - ); - receipts.insert( - DEPLOYMENT_HASH, - OpReceipt::Legacy(Receipt { - status: true.into(), - cumulative_gas_used: 272279, - logs: vec![], - }), - ); - receipts.insert( - INCREMENT_HASH, - OpReceipt::Legacy(Receipt { - status: true.into(), - cumulative_gas_used: 272279 + 44000, - logs: create_test_logs(), - }), - ); - receipts.insert( - INCREMENT2_HASH, - OpReceipt::Legacy(Receipt { - status: true.into(), - cumulative_gas_used: 272279 + 44000 + 44000, - logs: vec![], - }), - ); - receipts - }, - new_account_balances: { - let mut map = HashMap::default(); - map.insert(TEST_ADDRESS, U256::from(PENDING_BALANCE)); - map.insert(COUNTER_ADDRESS, U256::from(0)); - map - }, - }, - } -} - #[tokio::test] async fn test_get_pending_block() -> Result<()> { let setup = TestSetup::new().await?; @@ -281,7 +334,7 @@ async fn test_get_pending_block() -> Result<()> { assert_eq!(pending_block.number(), latest_block.number()); assert_eq!(pending_block.hash(), latest_block.hash()); - let base_payload = create_first_payload(); + let base_payload = setup.create_first_payload(); setup.send_flashblock(base_payload).await?; // Query pending block after sending the base payload with an empty delta @@ -293,7 +346,7 @@ async fn test_get_pending_block() -> Result<()> { assert_eq!(pending_block.number(), 1); assert_eq!(pending_block.transactions.hashes().len(), 1); // L1Info transaction - let second_payload = create_second_payload(); + let second_payload = setup.create_second_payload(); setup.send_flashblock(second_payload).await?; // Query pending block after sending the second payload with two transactions @@ -329,7 +382,12 @@ async fn test_get_transaction_by_hash_pending() -> Result<()> { let provider = setup.harness.provider(); assert!(provider.get_transaction_by_hash(DEPOSIT_TX_HASH).await?.is_none()); - assert!(provider.get_transaction_by_hash(TRANSFER_ETH_HASH).await?.is_none()); + assert!( + provider + .get_transaction_by_hash(setup.txn_details.alice_eth_transfer_hash) + .await? + .is_none() + ); setup.send_test_payloads().await?; @@ -337,9 +395,16 @@ async fn test_get_transaction_by_hash_pending() -> Result<()> { assert_eq!(tx1.tx_hash(), DEPOSIT_TX_HASH); assert_eq!(tx1.from(), DEPOSIT_SENDER); - let tx2 = provider.get_transaction_by_hash(TRANSFER_ETH_HASH).await?.expect("tx2 expected"); - assert_eq!(tx2.tx_hash(), TRANSFER_ETH_HASH); - assert_eq!(tx2.from(), TX_SENDER); + let tx2 = provider + .get_transaction_by_hash(setup.txn_details.alice_eth_transfer_hash) + .await? + .expect("tx2 expected"); + assert_eq!(tx2.tx_hash(), setup.txn_details.alice_eth_transfer_hash); + assert_eq!(tx2.from(), setup.harness.accounts().alice.address); + assert_eq!( + tx2.inner.inner.as_eip1559().unwrap().to().unwrap(), + setup.harness.accounts().bob.address + ); Ok(()) } @@ -358,8 +423,10 @@ async fn test_get_transaction_receipt_pending() -> Result<()> { provider.get_transaction_receipt(DEPOSIT_TX_HASH).await?.expect("receipt expected"); assert_eq!(receipt.gas_used(), 21000); - let receipt = - provider.get_transaction_receipt(TRANSFER_ETH_HASH).await?.expect("receipt expected"); + let receipt = provider + .get_transaction_receipt(setup.txn_details.alice_eth_transfer_hash) + .await? + .expect("receipt expected"); assert_eq!(receipt.gas_used(), 24000); // 45000 - 21000 Ok(()) @@ -370,13 +437,18 @@ async fn test_get_transaction_count() -> Result<()> { let setup = TestSetup::new().await?; let provider = setup.harness.provider(); - assert_eq!(provider.get_transaction_count(DEPOSIT_SENDER).await?, 0); - assert_eq!(provider.get_transaction_count(TX_SENDER).pending().await?, 0); + let deployer_addr = setup.harness.accounts().deployer.address; + let alice_addr = setup.harness.accounts().alice.address; + + assert_eq!(provider.get_transaction_count(DEPOSIT_SENDER).pending().await?, 0); + assert_eq!(provider.get_transaction_count(deployer_addr).pending().await?, 0); + assert_eq!(provider.get_transaction_count(alice_addr).pending().await?, 0); setup.send_test_payloads().await?; - assert_eq!(provider.get_transaction_count(DEPOSIT_SENDER).await?, 0); - assert_eq!(provider.get_transaction_count(TX_SENDER).pending().await?, 4); + assert_eq!(provider.get_transaction_count(DEPOSIT_SENDER).pending().await?, 2); + assert_eq!(provider.get_transaction_count(deployer_addr).pending().await?, 3); + assert_eq!(provider.get_transaction_count(alice_addr).pending().await?, 1); Ok(()) } @@ -386,54 +458,38 @@ async fn test_eth_call() -> Result<()> { let setup = TestSetup::new().await?; let provider = setup.harness.provider(); - // We ensure that eth_call will succeed because we are on plain state - let send_eth_call = OpTransactionRequest::default() - .from(TX_SENDER) + let accounts = setup.harness.accounts(); + + // Initially, the big spend will succeed because we haven't sent the test payloads yet + let big_spend = OpTransactionRequest::default() + .from(accounts.alice.address) .transaction_type(0) .gas_limit(200000) - .nonce(1) - .to(address!("0xf39635f2adf40608255779ff742afe13de31f577")) - .value(U256::from(9999999999849942300000u128)) - .input(TransactionInput::new(bytes!("0x"))); - - let res = provider.call(send_eth_call.clone()).block(BlockNumberOrTag::Pending.into()).await; + .nonce(0) + .to(setup.harness.accounts().bob.address) + .value(U256::from(9999999999849942300000u128)); + let res = provider.call(big_spend.clone()).block(BlockNumberOrTag::Pending.into()).await; assert!(res.is_ok()); setup.send_test_payloads().await?; - // We included a heavy spending transaction and now don't have enough funds for this request, so - // this eth_call with fail - let res = provider.call(send_eth_call.nonce(4)).block(BlockNumberOrTag::Pending.into()).await; - + // We included a big spending transaction in the payloads + // and now don't have enough funds for this request, so this eth_call with fail + let res = + provider.call(big_spend.clone().nonce(3)).block(BlockNumberOrTag::Pending.into()).await; assert!(res.is_err()); assert!( res.unwrap_err().as_error_resp().unwrap().message.contains("insufficient funds for gas") ); // read count1 from counter contract - let eth_call_count1 = OpTransactionRequest::default() - .from(TX_SENDER) - .transaction_type(0) - .gas_limit(20000000) - .nonce(5) - .to(COUNTER_ADDRESS) - .value(U256::ZERO) - .input(TransactionInput::new(bytes!("0xa87d942c"))); - let res_count1 = provider.call(eth_call_count1).await; + let res_count1 = provider.call(setup.count1()).await; assert!(res_count1.is_ok()); assert_eq!(U256::from_str(res_count1.unwrap().to_string().as_str()).unwrap(), U256::from(2)); // read count2 from counter contract - let eth_call_count2 = OpTransactionRequest::default() - .from(TX_SENDER) - .transaction_type(0) - .gas_limit(20000000) - .nonce(6) - .to(COUNTER_ADDRESS) - .value(U256::ZERO) - .input(TransactionInput::new(bytes!("0xd631c639"))); - let res_count2 = provider.call(eth_call_count2).await; + let res_count2 = provider.call(setup.count2()).await; assert!(res_count2.is_ok()); assert_eq!(U256::from_str(res_count2.unwrap().to_string().as_str()).unwrap(), U256::from(2)); @@ -447,11 +503,11 @@ async fn test_eth_estimate_gas() -> Result<()> { // We ensure that eth_estimate_gas will succeed because we are on plain state let send_estimate_gas = OpTransactionRequest::default() - .from(TX_SENDER) + .from(setup.harness.accounts().alice.address) .transaction_type(0) .gas_limit(200000) - .nonce(1) - .to(address!("0xf39635f2adf40608255779ff742afe13de31f577")) + .nonce(0) + .to(setup.harness.accounts().bob.address) .value(U256::from(9999999999849942300000u128)) .input(TransactionInput::new(bytes!("0x"))); @@ -488,32 +544,18 @@ async fn test_eth_simulate_v1() -> Result<()> { let simulate_call = SimulatePayload { block_state_calls: vec![SimBlock { calls: vec![ - // read number from counter contract - OpTransactionRequest::default() - .from(address!("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266")) - .transaction_type(0) - .gas_limit(200000) - .to(address!("0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512")) - .value(U256::ZERO) - .input(TransactionInput::new(bytes!("0xa87d942c"))) - .into(), + // read count1() from counter contract + setup.count1().into(), // increment() value in contract OpTransactionRequest::default() .from(address!("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266")) .transaction_type(0) .gas_limit(200000) - .to(address!("0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512")) + .to(setup.txn_details.counter_address) .input(TransactionInput::new(bytes!("0xd09de08a"))) .into(), - // read number from counter contract - OpTransactionRequest::default() - .from(address!("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266")) - .transaction_type(0) - .gas_limit(200000) - .to(address!("0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512")) - .value(U256::ZERO) - .input(TransactionInput::new(bytes!("0xa87d942c"))) - .into(), + // read count1() from counter contract + setup.count1().into(), ], block_overrides: None, state_overrides: None, @@ -545,19 +587,22 @@ async fn test_eth_simulate_v1() -> Result<()> { async fn test_send_raw_transaction_sync() -> Result<()> { let setup = TestSetup::new().await?; - setup.send_flashblock(create_first_payload()).await?; + setup.send_flashblock(setup.create_first_payload()).await?; // run the Tx sync and, in parallel, deliver the payload that contains the Tx - let (receipt_result, payload_result) = - tokio::join!(setup.send_raw_transaction_sync(TRANSFER_ETH_TX, None), async { + let second_payload = setup.create_second_payload(); + let (receipt_result, payload_result) = tokio::join!( + setup.send_raw_transaction_sync(setup.txn_details.alice_eth_transfer_tx.clone(), None), + async { tokio::time::sleep(std::time::Duration::from_millis(100)).await; - setup.send_flashblock(create_second_payload()).await - }); + setup.send_flashblock(second_payload).await + } + ); payload_result?; let receipt = receipt_result?; - assert_eq!(receipt.transaction_hash(), TRANSFER_ETH_HASH); + assert_eq!(receipt.transaction_hash(), setup.txn_details.alice_eth_transfer_hash); Ok(()) } @@ -566,7 +611,9 @@ async fn test_send_raw_transaction_sync_timeout() { let setup = TestSetup::new().await.unwrap(); // fail request immediately by passing a timeout of 0 ms - let receipt_result = setup.send_raw_transaction_sync(TRANSFER_ETH_TX, Some(0)).await; + let receipt_result = setup + .send_raw_transaction_sync(setup.txn_details.alice_eth_transfer_tx.clone(), Some(0)) + .await; let error_code = EthRpcErrorCode::TransactionConfirmationTimeout.code(); assert!(receipt_result.err().unwrap().to_string().contains(format!("{}", error_code).as_str())); @@ -601,14 +648,14 @@ async fn test_get_logs_pending() -> Result<()> { assert_eq!(logs.len(), 2); // Verify the first log is from COUNTER_ADDRESS - assert_eq!(logs[0].address(), COUNTER_ADDRESS); + assert_eq!(logs[0].address(), setup.txn_details.counter_address); assert_eq!(logs[0].topics()[0], TEST_LOG_TOPIC_0); - assert_eq!(logs[0].transaction_hash, Some(INCREMENT_HASH)); + assert_eq!(logs[0].transaction_hash, Some(setup.txn_details.counter_increment_hash)); // Verify the second log is from TEST_ADDRESS assert_eq!(logs[1].address(), TEST_ADDRESS); assert_eq!(logs[1].topics()[0], TEST_LOG_TOPIC_0); - assert_eq!(logs[1].transaction_hash, Some(INCREMENT_HASH)); + assert_eq!(logs[1].transaction_hash, Some(setup.txn_details.counter_increment_hash)); Ok(()) } @@ -624,7 +671,7 @@ async fn test_get_logs_filter_by_address() -> Result<()> { let logs = provider .get_logs( &alloy_rpc_types_eth::Filter::default() - .address(COUNTER_ADDRESS) + .address(setup.txn_details.counter_address) .from_block(alloy_eips::BlockNumberOrTag::Pending) .to_block(alloy_eips::BlockNumberOrTag::Pending), ) @@ -632,8 +679,8 @@ async fn test_get_logs_filter_by_address() -> Result<()> { // Should get only 1 log from COUNTER_ADDRESS assert_eq!(logs.len(), 1); - assert_eq!(logs[0].address(), COUNTER_ADDRESS); - assert_eq!(logs[0].transaction_hash, Some(INCREMENT_HASH)); + assert_eq!(logs[0].address(), setup.txn_details.counter_address); + assert_eq!(logs[0].transaction_hash, Some(setup.txn_details.counter_increment_hash)); // Test filtering by TEST_ADDRESS let logs = provider @@ -648,7 +695,7 @@ async fn test_get_logs_filter_by_address() -> Result<()> { // Should get only 1 log from TEST_ADDRESS assert_eq!(logs.len(), 1); assert_eq!(logs[0].address(), TEST_ADDRESS); - assert_eq!(logs[0].transaction_hash, Some(INCREMENT_HASH)); + assert_eq!(logs[0].transaction_hash, Some(setup.txn_details.counter_increment_hash)); Ok(()) } @@ -682,7 +729,7 @@ async fn test_get_logs_topic_filtering() -> Result<()> { let logs = provider.get_logs(&filter).await?; assert_eq!(logs.len(), 1); - assert_eq!(logs[0].address(), COUNTER_ADDRESS); + assert_eq!(logs[0].address(), setup.txn_details.counter_address); assert_eq!(logs[0].topics()[1], TEST_LOG_TOPIC_1); Ok(()) @@ -706,7 +753,10 @@ async fn test_get_logs_mixed_block_ranges() -> Result<()> { // Should now include pending logs (2 logs from our test setup) assert_eq!(logs.len(), 2); - assert!(logs.iter().all(|log| log.transaction_hash == Some(INCREMENT_HASH))); + assert!( + logs.iter() + .all(|log| log.transaction_hash == Some(setup.txn_details.counter_increment_hash)) + ); // Test fromBlock: latest, toBlock: pending let logs = provider @@ -719,7 +769,10 @@ async fn test_get_logs_mixed_block_ranges() -> Result<()> { // Should include pending logs (historical part is empty in our test setup) assert_eq!(logs.len(), 2); - assert!(logs.iter().all(|log| log.transaction_hash == Some(INCREMENT_HASH))); + assert!( + logs.iter() + .all(|log| log.transaction_hash == Some(setup.txn_details.counter_increment_hash)) + ); // Test fromBlock: earliest, toBlock: pending let logs = provider @@ -732,7 +785,10 @@ async fn test_get_logs_mixed_block_ranges() -> Result<()> { // Should include pending logs (historical part is empty in our test setup) assert_eq!(logs.len(), 2); - assert!(logs.iter().all(|log| log.transaction_hash == Some(INCREMENT_HASH))); + assert!( + logs.iter() + .all(|log| log.transaction_hash == Some(setup.txn_details.counter_increment_hash)) + ); Ok(()) } @@ -764,7 +820,7 @@ async fn test_base_subscribe_new_flashblocks() -> eyre::Result<()> { assert_eq!(sub["id"], 1); let subscription_id = sub["result"].as_str().expect("subscription id expected"); - setup.send_flashblock(create_first_payload()).await?; + setup.send_flashblock(setup.create_first_payload()).await?; let notification = ws_stream.next().await.unwrap()?; let notif: serde_json::Value = serde_json::from_str(notification.to_text()?)?; @@ -805,7 +861,7 @@ async fn test_base_subscribe_multiple_flashblocks() -> eyre::Result<()> { let sub: serde_json::Value = serde_json::from_str(response.to_text()?)?; let subscription_id = sub["result"].as_str().expect("subscription id expected"); - setup.send_flashblock(create_first_payload()).await?; + setup.send_flashblock(setup.create_first_payload()).await?; let notif1 = ws_stream.next().await.unwrap()?; let notif1: serde_json::Value = serde_json::from_str(notif1.to_text()?)?; @@ -815,7 +871,7 @@ async fn test_base_subscribe_multiple_flashblocks() -> eyre::Result<()> { assert_eq!(block1["number"], "0x1"); assert_eq!(block1["transactions"].as_array().unwrap().len(), 1); - setup.send_flashblock(create_second_payload()).await?; + setup.send_flashblock(setup.create_second_payload()).await?; let notif2 = ws_stream.next().await.unwrap()?; let notif2: serde_json::Value = serde_json::from_str(notif2.to_text()?)?; @@ -894,7 +950,7 @@ async fn test_base_subscribe_multiple_clients() -> eyre::Result<()> { let _sub1 = ws1.next().await.unwrap()?; let _sub2 = ws2.next().await.unwrap()?; - setup.send_flashblock(create_first_payload()).await?; + setup.send_flashblock(setup.create_first_payload()).await?; let notif1 = ws1.next().await.unwrap()?; let notif1: serde_json::Value = serde_json::from_str(notif1.to_text()?)?; diff --git a/crates/test-utils/contracts/script/Counter.s.sol b/crates/test-utils/contracts/script/Counter.s.sol deleted file mode 100644 index f01d69c3..00000000 --- a/crates/test-utils/contracts/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterScript is Script { - Counter public counter; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - counter = new Counter(); - - vm.stopBroadcast(); - } -} diff --git a/crates/test-utils/contracts/script/DeployDoubleCounter.s.sol b/crates/test-utils/contracts/script/DeployDoubleCounter.s.sol new file mode 100644 index 00000000..be4c3ccc --- /dev/null +++ b/crates/test-utils/contracts/script/DeployDoubleCounter.s.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {DoubleCounter} from "../src/DoubleCounter.sol"; + +contract DeployDoubleCounterScript is Script { + function run() public { + vm.startBroadcast(); + DoubleCounter counter = new DoubleCounter(); + console.log("DoubleCounter deployed at: ", address(counter)); + vm.stopBroadcast(); + } +} diff --git a/crates/test-utils/contracts/src/Counter.sol b/crates/test-utils/contracts/src/Counter.sol deleted file mode 100644 index aded7997..00000000 --- a/crates/test-utils/contracts/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/crates/test-utils/contracts/src/DoubleCounter.sol b/crates/test-utils/contracts/src/DoubleCounter.sol new file mode 100644 index 00000000..894b8a7a --- /dev/null +++ b/crates/test-utils/contracts/src/DoubleCounter.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract DoubleCounter { + uint256 public count1 = 1; + uint256 public count2 = 1; + + function increment() public { + count1++; + } + + function increment2() public { + count2++; + } +} diff --git a/crates/test-utils/contracts/test/Counter.t.sol b/crates/test-utils/contracts/test/Counter.t.sol deleted file mode 100644 index 48319108..00000000 --- a/crates/test-utils/contracts/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} diff --git a/crates/test-utils/src/accounts.rs b/crates/test-utils/src/accounts.rs index 29f6854b..0dae741e 100644 --- a/crates/test-utils/src/accounts.rs +++ b/crates/test-utils/src/accounts.rs @@ -1,11 +1,16 @@ //! Test accounts with pre-funded balances for integration testing -use alloy_consensus::{SignableTransaction, TxLegacy}; +use alloy_consensus::{SignableTransaction, Transaction}; use alloy_eips::eip2718::Encodable2718; -use alloy_primitives::{Address, Bytes, FixedBytes, U256, address, hex}; +use alloy_primitives::{Address, Bytes, FixedBytes, TxHash, address, hex}; use alloy_signer::SignerSync; use alloy_signer_local::PrivateKeySigner; use eyre::Result; +use op_alloy_network::TransactionBuilder; +use op_alloy_rpc_types::OpTransactionRequest; +use reth::{revm::context::TransactionType, rpc::compat::SignTxRequestError}; + +use crate::node::BASE_CHAIN_ID; /// Hardcoded test account with a fixed private key #[derive(Debug, Clone)] @@ -19,32 +24,62 @@ pub struct Account { } impl Account { - /// Sign a simple ETH transfer transaction and return the signed bytes - pub fn sign_transaction_bytes( + /// Constructs a signed CREATE transaction with a given nonce and + /// returns the signed bytes, contract address, and transaction hash + pub fn create_deployment_tx( &self, - to: Address, - value: U256, + bytecode: Bytes, nonce: u64, - chain_id: u64, - ) -> Result { - let key_bytes = hex::decode(self.private_key)?; - let key_fixed: FixedBytes<32> = FixedBytes::from_slice(&key_bytes); - let signer = PrivateKeySigner::from_bytes(&key_fixed)?; - - let tx = TxLegacy { - chain_id: Some(chain_id), - nonce, - gas_price: 200, - gas_limit: 21_000, - to: alloy_primitives::TxKind::Call(to), - value, - input: Bytes::new(), - }; - - let signature = signer.sign_hash_sync(&tx.signature_hash())?; + ) -> Result<(Bytes, Address, TxHash)> { + let tx_request = OpTransactionRequest::default() + .from(self.address) + .transaction_type(TransactionType::Eip1559.into()) + .with_gas_limit(500_000) + .with_max_fee_per_gas(1_000_000_000) + .with_max_priority_fee_per_gas(0) + .with_chain_id(BASE_CHAIN_ID) + .with_deploy_code(bytecode) + .with_nonce(nonce); + + let tx = tx_request + .build_typed_tx() + .map_err(|_| SignTxRequestError::InvalidTransactionRequest)?; + let signature = self.signer().sign_hash_sync(&tx.signature_hash())?; + let signed_tx = tx.into_signed(signature); + let signed_tx_bytes = signed_tx.encoded_2718().into(); + + let contract_address = self.address.create(signed_tx.nonce()); + Ok((signed_tx_bytes, contract_address, signed_tx.hash().clone())) + } + + /// Sign a TransactionRequest and return the signed bytes + pub fn sign_txn_request(&self, tx_request: OpTransactionRequest) -> Result<(Bytes, TxHash)> { + let tx_request = tx_request + .from(self.address) + .transaction_type(TransactionType::Eip1559.into()) + .with_gas_limit(500_000) + .with_chain_id(BASE_CHAIN_ID) + .with_max_fee_per_gas(1_000_000_000) + .with_max_priority_fee_per_gas(0); + + let tx = tx_request + .build_typed_tx() + .map_err(|_| SignTxRequestError::InvalidTransactionRequest)?; + let signature = self.signer().sign_hash_sync(&tx.signature_hash())?; let signed_tx = tx.into_signed(signature); + let signed_tx_bytes = signed_tx.encoded_2718().into(); + let tx_hash = signed_tx.hash(); + Ok((signed_tx_bytes, tx_hash.clone())) + } - Ok(signed_tx.encoded_2718().into()) + /// Constructs and returns a PrivateKeySigner for the TestAccount + pub fn signer(&self) -> PrivateKeySigner { + let key_bytes = + hex::decode(self.private_key).expect("should be able to decode private key"); + let key_fixed: FixedBytes<32> = FixedBytes::from_slice(&key_bytes); + PrivateKeySigner::from_bytes(&key_fixed) + .expect("should be able to build the PrivateKeySigner") + .into() } } From 681d2c9c5547a00ce6ada750ee69d6276d146c7f Mon Sep 17 00:00:00 2001 From: Haardik H Date: Mon, 8 Dec 2025 18:22:20 +0530 Subject: [PATCH 3/6] build-contracts as dep for build --- Justfile | 2 +- crates/test-utils/contracts/foundry.lock | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Justfile b/Justfile index e08b102c..4386f8cf 100644 --- a/Justfile +++ b/Justfile @@ -65,7 +65,7 @@ clippy-fix: cargo clippy --all-targets --fix --allow-dirty --allow-staged # Builds the workspace with release -build: +build: build-contracts cargo build --release # Builds all targets in debug mode diff --git a/crates/test-utils/contracts/foundry.lock b/crates/test-utils/contracts/foundry.lock index fee8a957..08f2f874 100644 --- a/crates/test-utils/contracts/foundry.lock +++ b/crates/test-utils/contracts/foundry.lock @@ -4,5 +4,8 @@ "name": "v1.11.0", "rev": "8e40513d678f392f398620b3ef2b418648b33e89" } + }, + "lib/solmate": { + "rev": "89365b880c4f3c786bdd453d4b8e8fe410344a69" } } \ No newline at end of file From abc46fcb84b52bf23041117277792f85853d11c1 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Mon, 8 Dec 2025 18:25:14 +0530 Subject: [PATCH 4/6] build-contracts as dep for test --- Justfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Justfile b/Justfile index 4386f8cf..75b055ea 100644 --- a/Justfile +++ b/Justfile @@ -39,7 +39,7 @@ zepter-fix: zepter format features --fix # Runs tests across workspace with all features enabled -test: +test: build-contracts @command -v cargo-nextest >/dev/null 2>&1 || cargo install cargo-nextest RUSTFLAGS="-D warnings" cargo nextest run --workspace --all-features @@ -65,7 +65,7 @@ clippy-fix: cargo clippy --all-targets --fix --allow-dirty --allow-staged # Builds the workspace with release -build: build-contracts +build: cargo build --release # Builds all targets in debug mode From 871855df4d6de2e600deee83ed6eacf12e27f015 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Mon, 8 Dec 2025 18:29:12 +0530 Subject: [PATCH 5/6] install foundry in CI --- .github/workflows/ci.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4acb993e..49731dc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: cache-on-failure: true - add-rust-environment-hash-key: 'false' + add-rust-environment-hash-key: "false" key: stable-${{ hashFiles('Cargo.lock') }} - name: Install just uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 @@ -41,12 +41,14 @@ jobs: egress-policy: audit - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive - uses: dtolnay/rust-toolchain@4305c38b25d97ef35a8ad1f985ccf2d2242004f2 # stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: cache-on-failure: true - add-rust-environment-hash-key: 'false' + add-rust-environment-hash-key: "false" key: stable-${{ hashFiles('Cargo.lock') }} - name: Install just uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 @@ -54,6 +56,10 @@ jobs: uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60 with: tool: cargo-nextest + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@50d5a8956f2e319df19e6b57539d7e2acb9f8c1e # v1.5.0 + with: + version: stable - name: Run tests run: just test @@ -73,7 +79,7 @@ jobs: - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: cache-on-failure: true - add-rust-environment-hash-key: 'false' + add-rust-environment-hash-key: "false" key: nightly-${{ hashFiles('Cargo.lock') }} - name: Install just uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 @@ -95,7 +101,7 @@ jobs: - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: cache-on-failure: true - add-rust-environment-hash-key: 'false' + add-rust-environment-hash-key: "false" key: stable-${{ hashFiles('Cargo.lock') }} - name: Install just uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 @@ -140,10 +146,10 @@ jobs: - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: cache-on-failure: true - add-rust-environment-hash-key: 'false' + add-rust-environment-hash-key: "false" key: nightly-${{ hashFiles('Cargo.lock') }} - uses: taiki-e/install-action@3575e532701a5fc614b0c842e4119af4cc5fd16d # v2.62.60 - with: + with: tool: cargo-udeps - name: Install just uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 From 4fc3da4feb25456887f42d5b73cf7958978d9e29 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Mon, 8 Dec 2025 18:38:22 +0530 Subject: [PATCH 6/6] setup foundry and build contracts for udeps CI --- .github/workflows/ci.yml | 4 ++++ Justfile | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49731dc8..19d9e814 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,4 +153,8 @@ jobs: tool: cargo-udeps - name: Install just uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@50d5a8956f2e319df19e6b57539d7e2acb9f8c1e # v1.5.0 + with: + version: stable - run: just check-udeps diff --git a/Justfile b/Justfile index 75b055ea..83b56582 100644 --- a/Justfile +++ b/Justfile @@ -89,12 +89,12 @@ clean: cargo clean # Checks if there are any unused dependencies -check-udeps: +check-udeps: build-contracts @command -v cargo-udeps >/dev/null 2>&1 || cargo install cargo-udeps cargo +nightly udeps --workspace --all-features --all-targets # Watches tests -watch-test: +watch-test: build-contracts cargo watch -x test # Watches checks