From 8c143b0c8c01da7a33a49e9054bad17998c7cc81 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Fri, 21 Feb 2025 14:57:51 -0300 Subject: [PATCH 01/20] feat: working erc20 deploy --- cmd/ethrex_l2/src/commands/erc20.bin | 1 + cmd/ethrex_l2/src/commands/test.rs | 108 ++++++++++++++------------- 2 files changed, 58 insertions(+), 51 deletions(-) create mode 100644 cmd/ethrex_l2/src/commands/erc20.bin diff --git a/cmd/ethrex_l2/src/commands/erc20.bin b/cmd/ethrex_l2/src/commands/erc20.bin new file mode 100644 index 0000000000..e1682b6861 --- /dev/null +++ b/cmd/ethrex_l2/src/commands/erc20.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506040518060400160405280600881526020017f46756e546f6b656e0000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f46554e0000000000000000000000000000000000000000000000000000000000815250816003908161008c91906105bb565b50806004908161009c91906105bb565b5050506100b93369d3c21bcecceda10000006100be60201b60201c565b6107ad565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036101305760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161012791906106ce565b60405180910390fd5b6101426000838361014660201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361019857806002600082825461018c9190610718565b9250508190555061026b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610224578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161021b9392919061075b565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036102b45780600260008282540392505081905550610301565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161035e9190610792565b60405180910390a3505050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806103ec57607f821691505b6020821081036103ff576103fe6103a5565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026104677fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8261042a565b610471868361042a565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006104b86104b36104ae84610489565b610493565b610489565b9050919050565b6000819050919050565b6104d28361049d565b6104e66104de826104bf565b848454610437565b825550505050565b600090565b6104fb6104ee565b6105068184846104c9565b505050565b5b8181101561052a5761051f6000826104f3565b60018101905061050c565b5050565b601f82111561056f5761054081610405565b6105498461041a565b81016020851015610558578190505b61056c6105648561041a565b83018261050b565b50505b505050565b600082821c905092915050565b600061059260001984600802610574565b1980831691505092915050565b60006105ab8383610581565b9150826002028217905092915050565b6105c48261036b565b67ffffffffffffffff8111156105dd576105dc610376565b5b6105e782546103d4565b6105f282828561052e565b600060209050601f8311600181146106255760008415610613578287015190505b61061d858261059f565b865550610685565b601f19841661063386610405565b60005b8281101561065b57848901518255600182019150602085019450602081019050610636565b868310156106785784890151610674601f891682610581565b8355505b6001600288020188555050505b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106b88261068d565b9050919050565b6106c8816106ad565b82525050565b60006020820190506106e360008301846106bf565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061072382610489565b915061072e83610489565b9250828201905080821115610746576107456106e9565b5b92915050565b61075581610489565b82525050565b600060608201905061077060008301866106bf565b61077d602083018561074c565b61078a604083018461074c565b949350505050565b60006020820190506107a7600083018461074c565b92915050565b610e56806107bc6000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce5671461013457806370a082311461015257806395d89b4114610182578063a9059cbb146101a0578063dd62ed3e146101d057610093565b806306fdde0314610098578063095ea7b3146100b657806318160ddd146100e657806323b872dd14610104575b600080fd5b6100a0610200565b6040516100ad9190610aaa565b60405180910390f35b6100d060048036038101906100cb9190610b65565b610292565b6040516100dd9190610bc0565b60405180910390f35b6100ee6102b5565b6040516100fb9190610bea565b60405180910390f35b61011e60048036038101906101199190610c05565b6102bf565b60405161012b9190610bc0565b60405180910390f35b61013c6102ee565b6040516101499190610c74565b60405180910390f35b61016c60048036038101906101679190610c8f565b6102f7565b6040516101799190610bea565b60405180910390f35b61018a61033f565b6040516101979190610aaa565b60405180910390f35b6101ba60048036038101906101b59190610b65565b6103d1565b6040516101c79190610bc0565b60405180910390f35b6101ea60048036038101906101e59190610cbc565b6103f4565b6040516101f79190610bea565b60405180910390f35b60606003805461020f90610d2b565b80601f016020809104026020016040519081016040528092919081815260200182805461023b90610d2b565b80156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b5050505050905090565b60008061029d61047b565b90506102aa818585610483565b600191505092915050565b6000600254905090565b6000806102ca61047b565b90506102d7858285610495565b6102e285858561052a565b60019150509392505050565b60006012905090565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606004805461034e90610d2b565b80601f016020809104026020016040519081016040528092919081815260200182805461037a90610d2b565b80156103c75780601f1061039c576101008083540402835291602001916103c7565b820191906000526020600020905b8154815290600101906020018083116103aa57829003601f168201915b5050505050905090565b6000806103dc61047b565b90506103e981858561052a565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b610490838383600161061e565b505050565b60006104a184846103f4565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105245781811015610514578281836040517ffb8f41b200000000000000000000000000000000000000000000000000000000815260040161050b93929190610d6b565b60405180910390fd5b6105238484848403600061061e565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361059c5760006040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081526004016105939190610da2565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361060e5760006040517fec442f050000000000000000000000000000000000000000000000000000000081526004016106059190610da2565b60405180910390fd5b6106198383836107f5565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036106905760006040517fe602df050000000000000000000000000000000000000000000000000000000081526004016106879190610da2565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036107025760006040517f94280d620000000000000000000000000000000000000000000000000000000081526004016106f99190610da2565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555080156107ef578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516107e69190610bea565b60405180910390a35b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361084757806002600082825461083b9190610dec565b9250508190555061091a565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156108d3578381836040517fe450d38c0000000000000000000000000000000000000000000000000000000081526004016108ca93929190610d6b565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361096357806002600082825403925050819055506109b0565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610a0d9190610bea565b60405180910390a3505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610a54578082015181840152602081019050610a39565b60008484015250505050565b6000601f19601f8301169050919050565b6000610a7c82610a1a565b610a868185610a25565b9350610a96818560208601610a36565b610a9f81610a60565b840191505092915050565b60006020820190508181036000830152610ac48184610a71565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610afc82610ad1565b9050919050565b610b0c81610af1565b8114610b1757600080fd5b50565b600081359050610b2981610b03565b92915050565b6000819050919050565b610b4281610b2f565b8114610b4d57600080fd5b50565b600081359050610b5f81610b39565b92915050565b60008060408385031215610b7c57610b7b610acc565b5b6000610b8a85828601610b1a565b9250506020610b9b85828601610b50565b9150509250929050565b60008115159050919050565b610bba81610ba5565b82525050565b6000602082019050610bd56000830184610bb1565b92915050565b610be481610b2f565b82525050565b6000602082019050610bff6000830184610bdb565b92915050565b600080600060608486031215610c1e57610c1d610acc565b5b6000610c2c86828701610b1a565b9350506020610c3d86828701610b1a565b9250506040610c4e86828701610b50565b9150509250925092565b600060ff82169050919050565b610c6e81610c58565b82525050565b6000602082019050610c896000830184610c65565b92915050565b600060208284031215610ca557610ca4610acc565b5b6000610cb384828501610b1a565b91505092915050565b60008060408385031215610cd357610cd2610acc565b5b6000610ce185828601610b1a565b9250506020610cf285828601610b1a565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610d4357607f821691505b602082108103610d5657610d55610cfc565b5b50919050565b610d6581610af1565b82525050565b6000606082019050610d806000830186610d5c565b610d8d6020830185610bdb565b610d9a6040830184610bdb565b949350505050565b6000602082019050610db76000830184610d5c565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610df782610b2f565b9150610e0283610b2f565b9250828201905080821115610e1a57610e19610dbd565b5b9291505056fea26469706673582212204381ed75544c16386ece861caa46377c9a9e141573c84f2195760d75a29ac26964736f6c634300081c0033 diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 4912e28d67..e1e08915c5 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -15,7 +15,9 @@ use std::{ time::Duration, }; -#[derive(Subcommand)] +const ERC20: &str = include_str!("./erc20.bin").trim_ascii(); + +#[derive(Subcommand, Debug)] pub(crate) enum Command { #[clap(about = "Make a load test sending transactions from a list of private keys.")] Load { @@ -155,6 +157,8 @@ async fn test_connection(cfg: EthrexL2Config) -> bool { impl Command { pub async fn run(self, cfg: EthrexL2Config) -> eyre::Result<()> { + println!("RUNNING"); + dbg!(&self); match self { Command::Load { path, @@ -164,6 +168,7 @@ impl Command { verbose, contract, } => { + println!("INSIDE LOAD"); let Ok(lines) = read_lines(path) else { return Ok(()); }; @@ -176,56 +181,57 @@ impl Command { Some(address) => address, None => Address::random(), }; - - let calldata: Bytes = if contract { - // This is the bytecode for the contract with the following functions - // version() -> always returns 2 - // function fibonacci(uint n) public pure returns (uint) -> returns the nth fib number - let init_code = hex::decode("6080604052348015600e575f5ffd5b506103198061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806354fd4d501461003857806361047ff414610056575b5f5ffd5b610040610086565b60405161004d9190610152565b60405180910390f35b610070600480360381019061006b9190610199565b61008b565b60405161007d9190610152565b60405180910390f35b600281565b5f5f8210156100cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100c69061021e565b60405180910390fd5b5f82036100de575f9050610135565b600182036100ef5760019050610135565b5f5f90505f600190505f600290505b84811161012e575f82905083836101159190610269565b92508093505080806101269061029c565b9150506100fe565b5080925050505b919050565b5f819050919050565b61014c8161013a565b82525050565b5f6020820190506101655f830184610143565b92915050565b5f5ffd5b6101788161013a565b8114610182575f5ffd5b50565b5f813590506101938161016f565b92915050565b5f602082840312156101ae576101ad61016b565b5b5f6101bb84828501610185565b91505092915050565b5f82825260208201905092915050565b7f496e707574206d757374206265206e6f6e2d6e656761746976650000000000005f82015250565b5f610208601a836101c4565b9150610213826101d4565b602082019050919050565b5f6020820190508181035f830152610235816101fc565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6102738261013a565b915061027e8361013a565b92508282019050808211156102965761029561023c565b5b92915050565b5f6102a68261013a565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036102d8576102d761023c565b5b60018201905091905056fea264697066735822122021e2c2b56b7e23b9555cc95390dfb2979a8526595038818d133d5bb772c01a6564736f6c634300081c0033")?; - let client = EthClient::new(&cfg.network.l2_rpc_url); - - let (_, contract_address) = client - .deploy( - cfg.wallet.address, - cfg.wallet.private_key, - init_code.into(), - Overrides::default(), - ) - .await?; - - to_address = contract_address; - - calldata::encode_calldata( - "fibonacci(uint256)", - &[Value::Uint(100000000000000_u64.into())], - )? - .into() - } else { - Bytes::new() - }; - - println!("Sending to: {to_address:#x}"); - - let mut threads = vec![]; - for pk in lines.map_while(Result::ok) { - let thread = tokio::spawn(transfer_from( - pk, - to_address, - value, - iterations, - verbose, - calldata.clone(), - cfg.clone(), - )); - threads.push(thread); - } - - let mut retries = 0; - for thread in threads { - retries += thread.await?; - } - - println!("Total retries: {retries}"); + println!("BEFORE INIT CODE"); + // let calldata: Bytes = if contract { + // This is the bytecode for the contract with the following functions + // version() -> always returns 2 + // function fibonacci(uint n) public pure returns (uint) -> returns the nth fib number + // let init_code = hex::decode("6080604052348015600e575f5ffd5b506103198061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806354fd4d501461003857806361047ff414610056575b5f5ffd5b610040610086565b60405161004d9190610152565b60405180910390f35b610070600480360381019061006b9190610199565b61008b565b60405161007d9190610152565b60405180910390f35b600281565b5f5f8210156100cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100c69061021e565b60405180910390fd5b5f82036100de575f9050610135565b600182036100ef5760019050610135565b5f5f90505f600190505f600290505b84811161012e575f82905083836101159190610269565b92508093505080806101269061029c565b9150506100fe565b5080925050505b919050565b5f819050919050565b61014c8161013a565b82525050565b5f6020820190506101655f830184610143565b92915050565b5f5ffd5b6101788161013a565b8114610182575f5ffd5b50565b5f813590506101938161016f565b92915050565b5f602082840312156101ae576101ad61016b565b5b5f6101bb84828501610185565b91505092915050565b5f82825260208201905092915050565b7f496e707574206d757374206265206e6f6e2d6e656761746976650000000000005f82015250565b5f610208601a836101c4565b9150610213826101d4565b602082019050919050565b5f6020820190508181035f830152610235816101fc565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6102738261013a565b915061027e8361013a565b92508282019050808211156102965761029561023c565b5b92915050565b5f6102a68261013a565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036102d8576102d761023c565b5b60018201905091905056fea264697066735822122021e2c2b56b7e23b9555cc95390dfb2979a8526595038818d133d5bb772c01a6564736f6c634300081c0033")?; + let init_code = hex::decode(ERC20).unwrap(); + let client = EthClient::new(&cfg.network.l2_rpc_url); + + let (_, contract_address) = client + .deploy( + cfg.wallet.address, + cfg.wallet.private_key, + init_code.into(), + Overrides::default(), + ) + .await?; + println!("{:?}", contract_address); + to_address = contract_address; + + // calldata::encode_calldata( + // "fibonacci(uint256)", + // &[Value::Uint(100000000000000_u64.into())], + // )? + // .into(); + // } else { + // Bytes::new() + // }; + + // println!("Sending to: {to_address:#x}"); + + // let mut threads = vec![]; + // for pk in lines.map_while(Result::ok) { + // let thread = tokio::spawn(transfer_from( + // pk, + // to_address, + // value, + // iterations, + // verbose, + // calldata.clone(), + // cfg.clone(), + // )); + // threads.push(thread); + // } + + // let mut retries = 0; + // for thread in threads { + // retries += thread.await?; + // } + + // println!("Total retries: {retries}"); Ok(()) } } From e7b41fbb616f3ed421b75c36c8bfa636e9bf9f29 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Fri, 21 Feb 2025 16:57:52 -0300 Subject: [PATCH 02/20] feat: balanceOf(self) working --- cmd/ethrex_l2/src/commands/test.rs | 51 +++++++++++------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index e1e08915c5..1ef76027b9 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -97,7 +97,7 @@ async fn transfer_from( let mut retries = 0; - for i in nonce..nonce + iterations { + for i in 0..1 { if verbose { println!("transfer {i} from {pk}"); } @@ -109,7 +109,7 @@ async fn transfer_from( calldata.clone(), Overrides { chain_id: Some(cfg.network.l2_chain_id), - nonce: Some(i), + nonce: Some(nonce), value: if calldata.is_empty() { Some(value) } else { @@ -201,37 +201,22 @@ impl Command { println!("{:?}", contract_address); to_address = contract_address; - // calldata::encode_calldata( - // "fibonacci(uint256)", - // &[Value::Uint(100000000000000_u64.into())], - // )? - // .into(); - // } else { - // Bytes::new() - // }; - - // println!("Sending to: {to_address:#x}"); - - // let mut threads = vec![]; - // for pk in lines.map_while(Result::ok) { - // let thread = tokio::spawn(transfer_from( - // pk, - // to_address, - // value, - // iterations, - // verbose, - // calldata.clone(), - // cfg.clone(), - // )); - // threads.push(thread); - // } - - // let mut retries = 0; - // for thread in threads { - // retries += thread.await?; - // } - - // println!("Total retries: {retries}"); + let calldata = calldata::encode_calldata( + "balanceOf(address)", + &[Value::Address(cfg.wallet.address)], + )?; + let tx = client + .build_eip1559_transaction( + contract_address, + cfg.wallet.address, + calldata.into(), + Default::default(), + 100, + ) + .await + .unwrap(); + let res = client.send_eip1559_transaction(&tx, &cfg.wallet.private_key).await.unwrap(); + println!("THE RES: {res}"); Ok(()) } } From 7520b5e120062f9c610987b20ed29e9225aff748 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Fri, 21 Feb 2025 18:51:03 -0300 Subject: [PATCH 03/20] feat: add erc20 load test function --- cmd/ethrex_l2/src/commands/test.rs | 65 ++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 1ef76027b9..367387697f 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -3,10 +3,12 @@ use bytes::Bytes; use clap::Subcommand; use ethereum_types::{Address, H256, U256}; use ethrex_blockchain::constants::TX_GAS_COST; +use ethrex_common::H160; use ethrex_l2_sdk::calldata::{self, Value}; use ethrex_rpc::clients::eth::{eth_sender::Overrides, EthClient}; use keccak_hash::keccak; use secp256k1::SecretKey; +use std::str::FromStr; use std::{ fs::File, io::{self, BufRead}, @@ -61,6 +63,13 @@ pub(crate) enum Command { help = "send value to address with contract" )] contract: bool, + #[clap( + short = 'e', + long = "erc20", + default_value = "false", + help = "run the erc-20 transfer load test" + )] + erc20: bool, }, } @@ -155,10 +164,45 @@ async fn test_connection(cfg: EthrexL2Config) -> bool { false } +async fn erc20_load_test(config: &EthrexL2Config) -> eyre::Result<()> { + let client = EthClient::new(&config.network.l2_rpc_url); + let erc20_bytecode = hex::decode(ERC20)?; + let (_, contract_address) = client + .deploy( + config.wallet.address, + config.wallet.private_key, + erc20_bytecode.into(), + Overrides::default(), + ) + .await + .expect("Failed to deploy ERC20 with config: {config}"); + println!("ERC20 deployed to address: {contract_address}"); + println!("Checking ERC20 contract is reachable..."); + let balance_calldata = calldata::encode_calldata( + "balanceOf(address)", + &[Value::Address(config.wallet.address)], + )?; + let balance_request_tx = client + .build_eip1559_transaction( + contract_address, + config.wallet.address, + balance_calldata.into(), + Default::default(), + 100, + ) + .await + .unwrap(); + let balance_response = client + .send_eip1559_transaction(&balance_request_tx, &config.wallet.private_key) + .await + .expect("Failed to query balance for test load account"); + println!("ERC20 contract is reachable. Starting load test with balance: {balance_response}"); + + Ok(()) +} + impl Command { pub async fn run(self, cfg: EthrexL2Config) -> eyre::Result<()> { - println!("RUNNING"); - dbg!(&self); match self { Command::Load { path, @@ -167,8 +211,8 @@ impl Command { iterations, verbose, contract, + erc20, } => { - println!("INSIDE LOAD"); let Ok(lines) = read_lines(path) else { return Ok(()); }; @@ -202,8 +246,13 @@ impl Command { to_address = contract_address; let calldata = calldata::encode_calldata( - "balanceOf(address)", - &[Value::Address(cfg.wallet.address)], + "transfer(address,uint256)", + &[ + Value::Address( + H160::from_str("C257274276a4E539741Ca11b590B9447B26A8051").unwrap(), + ), + Value::Uint(U256::one()), + ], )?; let tx = client .build_eip1559_transaction( @@ -215,8 +264,10 @@ impl Command { ) .await .unwrap(); - let res = client.send_eip1559_transaction(&tx, &cfg.wallet.private_key).await.unwrap(); - println!("THE RES: {res}"); + let res = client + .send_eip1559_transaction(&tx, &cfg.wallet.private_key) + .await + .unwrap(); Ok(()) } } From 9e755c0f9bca951a29ea81a7417a0bc28d27509d Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Fri, 21 Feb 2025 20:07:56 -0300 Subject: [PATCH 04/20] feat: finish erc20 load test function --- cmd/ethrex_l2/src/commands/test.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 367387697f..be346627ee 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -164,7 +164,8 @@ async fn test_connection(cfg: EthrexL2Config) -> bool { false } -async fn erc20_load_test(config: &EthrexL2Config) -> eyre::Result<()> { +async fn erc20_load_test(config: &EthrexL2Config, count: Option) -> eyre::Result<()> { + let count = count.unwrap_or_else(|| 10_000_u64); let client = EthClient::new(&config.network.l2_rpc_url); let erc20_bytecode = hex::decode(ERC20)?; let (_, contract_address) = client From ca35a29f793ba5b4b5b75d5236c863488535e36a Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Mon, 24 Feb 2025 15:11:43 -0300 Subject: [PATCH 05/20] feat: restore old feats, use erc20 command --- cmd/ethrex_l2/src/commands/test.rs | 121 ++++++++++++++++------------- 1 file changed, 67 insertions(+), 54 deletions(-) diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index be346627ee..97ce194cc6 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -3,12 +3,10 @@ use bytes::Bytes; use clap::Subcommand; use ethereum_types::{Address, H256, U256}; use ethrex_blockchain::constants::TX_GAS_COST; -use ethrex_common::H160; use ethrex_l2_sdk::calldata::{self, Value}; use ethrex_rpc::clients::eth::{eth_sender::Overrides, EthClient}; use keccak_hash::keccak; use secp256k1::SecretKey; -use std::str::FromStr; use std::{ fs::File, io::{self, BufRead}, @@ -17,9 +15,13 @@ use std::{ time::Duration, }; +// ERC20 compiled artifact generated from this tutorial: +// https://medium.com/@kaishinaw/erc20-using-hardhat-a-comprehensive-guide-3211efba98d4 +// If you want to create a new one, follow the steps above and take +// the resulting artifact from the hardhat project. const ERC20: &str = include_str!("./erc20.bin").trim_ascii(); -#[derive(Subcommand, Debug)] +#[derive(Subcommand)] pub(crate) enum Command { #[clap(about = "Make a load test sending transactions from a list of private keys.")] Load { @@ -63,13 +65,15 @@ pub(crate) enum Command { help = "send value to address with contract" )] contract: bool, + }, + ERC20 { #[clap( - short = 'e', - long = "erc20", - default_value = "false", - help = "run the erc-20 transfer load test" + short = 't', + long = "transactions", + default_value = "10000", + help = "Number of ERC20 transactions to do" )] - erc20: bool, + transactions: u64, }, } @@ -106,7 +110,7 @@ async fn transfer_from( let mut retries = 0; - for i in 0..1 { + for i in nonce..nonce + iterations { if verbose { println!("transfer {i} from {pk}"); } @@ -118,7 +122,7 @@ async fn transfer_from( calldata.clone(), Overrides { chain_id: Some(cfg.network.l2_chain_id), - nonce: Some(nonce), + nonce: Some(i), value: if calldata.is_empty() { Some(value) } else { @@ -164,8 +168,7 @@ async fn test_connection(cfg: EthrexL2Config) -> bool { false } -async fn erc20_load_test(config: &EthrexL2Config, count: Option) -> eyre::Result<()> { - let count = count.unwrap_or_else(|| 10_000_u64); +async fn erc20_load_test(config: &EthrexL2Config, count: u64) -> eyre::Result<()> { let client = EthClient::new(&config.network.l2_rpc_url); let erc20_bytecode = hex::decode(ERC20)?; let (_, contract_address) = client @@ -212,7 +215,6 @@ impl Command { iterations, verbose, contract, - erc20, } => { let Ok(lines) = read_lines(path) else { return Ok(()); @@ -226,49 +228,60 @@ impl Command { Some(address) => address, None => Address::random(), }; - println!("BEFORE INIT CODE"); - // let calldata: Bytes = if contract { - // This is the bytecode for the contract with the following functions - // version() -> always returns 2 - // function fibonacci(uint n) public pure returns (uint) -> returns the nth fib number - // let init_code = hex::decode("6080604052348015600e575f5ffd5b506103198061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806354fd4d501461003857806361047ff414610056575b5f5ffd5b610040610086565b60405161004d9190610152565b60405180910390f35b610070600480360381019061006b9190610199565b61008b565b60405161007d9190610152565b60405180910390f35b600281565b5f5f8210156100cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100c69061021e565b60405180910390fd5b5f82036100de575f9050610135565b600182036100ef5760019050610135565b5f5f90505f600190505f600290505b84811161012e575f82905083836101159190610269565b92508093505080806101269061029c565b9150506100fe565b5080925050505b919050565b5f819050919050565b61014c8161013a565b82525050565b5f6020820190506101655f830184610143565b92915050565b5f5ffd5b6101788161013a565b8114610182575f5ffd5b50565b5f813590506101938161016f565b92915050565b5f602082840312156101ae576101ad61016b565b5b5f6101bb84828501610185565b91505092915050565b5f82825260208201905092915050565b7f496e707574206d757374206265206e6f6e2d6e656761746976650000000000005f82015250565b5f610208601a836101c4565b9150610213826101d4565b602082019050919050565b5f6020820190508181035f830152610235816101fc565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6102738261013a565b915061027e8361013a565b92508282019050808211156102965761029561023c565b5b92915050565b5f6102a68261013a565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036102d8576102d761023c565b5b60018201905091905056fea264697066735822122021e2c2b56b7e23b9555cc95390dfb2979a8526595038818d133d5bb772c01a6564736f6c634300081c0033")?; - let init_code = hex::decode(ERC20).unwrap(); - let client = EthClient::new(&cfg.network.l2_rpc_url); - let (_, contract_address) = client - .deploy( - cfg.wallet.address, - cfg.wallet.private_key, - init_code.into(), - Overrides::default(), - ) - .await?; - println!("{:?}", contract_address); - to_address = contract_address; + let calldata: Bytes = if contract { + // This is the bytecode for the contract with the following functions + // version() -> always returns 2 + // function fibonacci(uint n) public pure returns (uint) -> returns the nth fib number + let init_code = hex::decode("6080604052348015600e575f5ffd5b506103198061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806354fd4d501461003857806361047ff414610056575b5f5ffd5b610040610086565b60405161004d9190610152565b60405180910390f35b610070600480360381019061006b9190610199565b61008b565b60405161007d9190610152565b60405180910390f35b600281565b5f5f8210156100cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100c69061021e565b60405180910390fd5b5f82036100de575f9050610135565b600182036100ef5760019050610135565b5f5f90505f600190505f600290505b84811161012e575f82905083836101159190610269565b92508093505080806101269061029c565b9150506100fe565b5080925050505b919050565b5f819050919050565b61014c8161013a565b82525050565b5f6020820190506101655f830184610143565b92915050565b5f5ffd5b6101788161013a565b8114610182575f5ffd5b50565b5f813590506101938161016f565b92915050565b5f602082840312156101ae576101ad61016b565b5b5f6101bb84828501610185565b91505092915050565b5f82825260208201905092915050565b7f496e707574206d757374206265206e6f6e2d6e656761746976650000000000005f82015250565b5f610208601a836101c4565b9150610213826101d4565b602082019050919050565b5f6020820190508181035f830152610235816101fc565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6102738261013a565b915061027e8361013a565b92508282019050808211156102965761029561023c565b5b92915050565b5f6102a68261013a565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036102d8576102d761023c565b5b60018201905091905056fea264697066735822122021e2c2b56b7e23b9555cc95390dfb2979a8526595038818d133d5bb772c01a6564736f6c634300081c0033")?; + let client = EthClient::new(&cfg.network.l2_rpc_url); + + let (_, contract_address) = client + .deploy( + cfg.wallet.address, + cfg.wallet.private_key, + init_code.into(), + Overrides::default(), + ) + .await?; + + to_address = contract_address; + + calldata::encode_calldata( + "fibonacci(uint256)", + &[Value::Uint(100000000000000_u64.into())], + )? + .into() + } else { + Bytes::new() + }; + + println!("Sending to: {to_address:#x}"); + + let mut threads = vec![]; + for pk in lines.map_while(Result::ok) { + let thread = tokio::spawn(transfer_from( + pk, + to_address, + value, + iterations, + verbose, + calldata.clone(), + cfg.clone(), + )); + threads.push(thread); + } - let calldata = calldata::encode_calldata( - "transfer(address,uint256)", - &[ - Value::Address( - H160::from_str("C257274276a4E539741Ca11b590B9447B26A8051").unwrap(), - ), - Value::Uint(U256::one()), - ], - )?; - let tx = client - .build_eip1559_transaction( - contract_address, - cfg.wallet.address, - calldata.into(), - Default::default(), - 100, - ) - .await - .unwrap(); - let res = client - .send_eip1559_transaction(&tx, &cfg.wallet.private_key) - .await - .unwrap(); + let mut retries = 0; + for thread in threads { + retries += thread.await?; + } + + println!("Total retries: {retries}"); + Ok(()) + } + Command::ERC20 { transactions } => { + erc20_load_test(&cfg, transactions).await?; Ok(()) } } From 2ed7b7364e7c2800504e96d4a47e7e94a84562c3 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Mon, 24 Feb 2025 15:20:49 -0300 Subject: [PATCH 06/20] feat: restore every async transfer --- cmd/ethrex_l2/src/commands/test.rs | 34 +++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 97ce194cc6..5aa2b38969 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -3,6 +3,7 @@ use bytes::Bytes; use clap::Subcommand; use ethereum_types::{Address, H256, U256}; use ethrex_blockchain::constants::TX_GAS_COST; +use ethrex_common::H160; use ethrex_l2_sdk::calldata::{self, Value}; use ethrex_rpc::clients::eth::{eth_sender::Overrides, EthClient}; use keccak_hash::keccak; @@ -14,6 +15,7 @@ use std::{ thread::sleep, time::Duration, }; +use tokio::task::JoinSet; // ERC20 compiled artifact generated from this tutorial: // https://medium.com/@kaishinaw/erc20-using-hardhat-a-comprehensive-guide-3211efba98d4 @@ -201,7 +203,37 @@ async fn erc20_load_test(config: &EthrexL2Config, count: u64) -> eyre::Result<() .await .expect("Failed to query balance for test load account"); println!("ERC20 contract is reachable. Starting load test with balance: {balance_response}"); - + let mut tasks = JoinSet::new(); + let send_calldata = calldata::encode_calldata( + "transfer(address,uint256)", + &[ + Value::Address(config.wallet.address), + Value::Uint(U256::one()), + ], + ) + .unwrap(); + let send_tx = client + .build_eip1559_transaction( + H160::random(), + config.wallet.address.clone(), + send_calldata.into(), + Default::default(), + 1, + ) + .await?; + for i in 0..count { + let tx = send_tx.clone(); + let client = client.clone(); + let pk = config.wallet.private_key.clone(); + tasks.spawn(async move { + let sent = client.send_eip1559_transaction(&tx, &pk).await; + match sent { + Ok(_) => println!("ERC-20 transfer number {i} sent!"), + Err(_) => println!("ERC-20 transfer number {i} FAILED"), + } + }); + } + tasks.join_all().await; Ok(()) } From da45aed95ef5da1e9899209ccd66ea089cf1446e Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Mon, 24 Feb 2025 16:19:40 -0300 Subject: [PATCH 07/20] feat: retries and receipt fetch --- cmd/ethrex_l2/src/commands/test.rs | 20 ++++++++++++++++++-- crates/networking/rpc/clients/eth/mod.rs | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 5aa2b38969..0866e69148 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -5,7 +5,10 @@ use ethereum_types::{Address, H256, U256}; use ethrex_blockchain::constants::TX_GAS_COST; use ethrex_common::H160; use ethrex_l2_sdk::calldata::{self, Value}; -use ethrex_rpc::clients::eth::{eth_sender::Overrides, EthClient}; +use ethrex_rpc::{ + clients::eth::{eth_sender::Overrides, EthClient}, + types::receipt::RpcReceipt, +}; use keccak_hash::keccak; use secp256k1::SecretKey; use std::{ @@ -228,7 +231,20 @@ async fn erc20_load_test(config: &EthrexL2Config, count: u64) -> eyre::Result<() tasks.spawn(async move { let sent = client.send_eip1559_transaction(&tx, &pk).await; match sent { - Ok(_) => println!("ERC-20 transfer number {i} sent!"), + Ok(hash) => { + println!("ERC-20 transfer number {i} sent! Waiting for receipt..."); + let mut retries = 10_u64; + for _ in 0..retries { + match client.get_transaction_receipt(hash).await { + Ok(Some(RpcReceipt { receipt, .. })) if receipt.status => { + println!("ERC-20 transfer number {i} was sucessful"); + } + _ => { + let _ = tokio::time::sleep(Duration::from_secs(1)).await; + } + } + } + } Err(_) => println!("ERC-20 transfer number {i} FAILED"), } }); diff --git a/crates/networking/rpc/clients/eth/mod.rs b/crates/networking/rpc/clients/eth/mod.rs index dc6bb553d6..a1d7cc98fd 100644 --- a/crates/networking/rpc/clients/eth/mod.rs +++ b/crates/networking/rpc/clients/eth/mod.rs @@ -1060,6 +1060,7 @@ impl EthClient { "Transaction receipt is None".to_owned(), )) } + } pub fn from_hex_string_to_u256(hex_str: &str) -> Result { From 78322f1318951946ebc0bc65e7824bfc3db7d5b7 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Mon, 24 Feb 2025 16:28:37 -0300 Subject: [PATCH 08/20] lint: cargo fmt --- crates/networking/rpc/clients/eth/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/networking/rpc/clients/eth/mod.rs b/crates/networking/rpc/clients/eth/mod.rs index a1d7cc98fd..dc6bb553d6 100644 --- a/crates/networking/rpc/clients/eth/mod.rs +++ b/crates/networking/rpc/clients/eth/mod.rs @@ -1060,7 +1060,6 @@ impl EthClient { "Transaction receipt is None".to_owned(), )) } - } pub fn from_hex_string_to_u256(hex_str: &str) -> Result { From 62dfdd3ab17068fa483e4fff5ca78c3963b8faf4 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Mon, 24 Feb 2025 17:18:46 -0300 Subject: [PATCH 09/20] chore: update gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index c33a8c30ba..3cd8b9e6ff 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,8 @@ cmd/ef_tests/state/levm_perfgraphs *.svg prof_*.json prof.json + +# Used by the ERC 20 under cmd/ethrex_l2 +artifacts +node_modules +cache From 59ce927a51e65d2f33c6baa0eb3eead6c3b2afa2 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Tue, 25 Feb 2025 14:56:55 -0300 Subject: [PATCH 10/20] chore: more changes for multiple accounts --- cmd/ethrex_l2/src/commands/erc20.bin | 1 - cmd/ethrex_l2/src/commands/test.rs | 144 ++++++++++++++++++++++----- 2 files changed, 117 insertions(+), 28 deletions(-) delete mode 100644 cmd/ethrex_l2/src/commands/erc20.bin diff --git a/cmd/ethrex_l2/src/commands/erc20.bin b/cmd/ethrex_l2/src/commands/erc20.bin deleted file mode 100644 index e1682b6861..0000000000 --- a/cmd/ethrex_l2/src/commands/erc20.bin +++ /dev/null @@ -1 +0,0 @@ -608060405234801561001057600080fd5b506040518060400160405280600881526020017f46756e546f6b656e0000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f46554e0000000000000000000000000000000000000000000000000000000000815250816003908161008c91906105bb565b50806004908161009c91906105bb565b5050506100b93369d3c21bcecceda10000006100be60201b60201c565b6107ad565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036101305760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161012791906106ce565b60405180910390fd5b6101426000838361014660201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361019857806002600082825461018c9190610718565b9250508190555061026b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610224578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161021b9392919061075b565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036102b45780600260008282540392505081905550610301565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161035e9190610792565b60405180910390a3505050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806103ec57607f821691505b6020821081036103ff576103fe6103a5565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026104677fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8261042a565b610471868361042a565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006104b86104b36104ae84610489565b610493565b610489565b9050919050565b6000819050919050565b6104d28361049d565b6104e66104de826104bf565b848454610437565b825550505050565b600090565b6104fb6104ee565b6105068184846104c9565b505050565b5b8181101561052a5761051f6000826104f3565b60018101905061050c565b5050565b601f82111561056f5761054081610405565b6105498461041a565b81016020851015610558578190505b61056c6105648561041a565b83018261050b565b50505b505050565b600082821c905092915050565b600061059260001984600802610574565b1980831691505092915050565b60006105ab8383610581565b9150826002028217905092915050565b6105c48261036b565b67ffffffffffffffff8111156105dd576105dc610376565b5b6105e782546103d4565b6105f282828561052e565b600060209050601f8311600181146106255760008415610613578287015190505b61061d858261059f565b865550610685565b601f19841661063386610405565b60005b8281101561065b57848901518255600182019150602085019450602081019050610636565b868310156106785784890151610674601f891682610581565b8355505b6001600288020188555050505b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106b88261068d565b9050919050565b6106c8816106ad565b82525050565b60006020820190506106e360008301846106bf565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061072382610489565b915061072e83610489565b9250828201905080821115610746576107456106e9565b5b92915050565b61075581610489565b82525050565b600060608201905061077060008301866106bf565b61077d602083018561074c565b61078a604083018461074c565b949350505050565b60006020820190506107a7600083018461074c565b92915050565b610e56806107bc6000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce5671461013457806370a082311461015257806395d89b4114610182578063a9059cbb146101a0578063dd62ed3e146101d057610093565b806306fdde0314610098578063095ea7b3146100b657806318160ddd146100e657806323b872dd14610104575b600080fd5b6100a0610200565b6040516100ad9190610aaa565b60405180910390f35b6100d060048036038101906100cb9190610b65565b610292565b6040516100dd9190610bc0565b60405180910390f35b6100ee6102b5565b6040516100fb9190610bea565b60405180910390f35b61011e60048036038101906101199190610c05565b6102bf565b60405161012b9190610bc0565b60405180910390f35b61013c6102ee565b6040516101499190610c74565b60405180910390f35b61016c60048036038101906101679190610c8f565b6102f7565b6040516101799190610bea565b60405180910390f35b61018a61033f565b6040516101979190610aaa565b60405180910390f35b6101ba60048036038101906101b59190610b65565b6103d1565b6040516101c79190610bc0565b60405180910390f35b6101ea60048036038101906101e59190610cbc565b6103f4565b6040516101f79190610bea565b60405180910390f35b60606003805461020f90610d2b565b80601f016020809104026020016040519081016040528092919081815260200182805461023b90610d2b565b80156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b5050505050905090565b60008061029d61047b565b90506102aa818585610483565b600191505092915050565b6000600254905090565b6000806102ca61047b565b90506102d7858285610495565b6102e285858561052a565b60019150509392505050565b60006012905090565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606004805461034e90610d2b565b80601f016020809104026020016040519081016040528092919081815260200182805461037a90610d2b565b80156103c75780601f1061039c576101008083540402835291602001916103c7565b820191906000526020600020905b8154815290600101906020018083116103aa57829003601f168201915b5050505050905090565b6000806103dc61047b565b90506103e981858561052a565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b610490838383600161061e565b505050565b60006104a184846103f4565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105245781811015610514578281836040517ffb8f41b200000000000000000000000000000000000000000000000000000000815260040161050b93929190610d6b565b60405180910390fd5b6105238484848403600061061e565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361059c5760006040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081526004016105939190610da2565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361060e5760006040517fec442f050000000000000000000000000000000000000000000000000000000081526004016106059190610da2565b60405180910390fd5b6106198383836107f5565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036106905760006040517fe602df050000000000000000000000000000000000000000000000000000000081526004016106879190610da2565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036107025760006040517f94280d620000000000000000000000000000000000000000000000000000000081526004016106f99190610da2565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555080156107ef578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516107e69190610bea565b60405180910390a35b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361084757806002600082825461083b9190610dec565b9250508190555061091a565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156108d3578381836040517fe450d38c0000000000000000000000000000000000000000000000000000000081526004016108ca93929190610d6b565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361096357806002600082825403925050819055506109b0565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610a0d9190610bea565b60405180910390a3505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610a54578082015181840152602081019050610a39565b60008484015250505050565b6000601f19601f8301169050919050565b6000610a7c82610a1a565b610a868185610a25565b9350610a96818560208601610a36565b610a9f81610a60565b840191505092915050565b60006020820190508181036000830152610ac48184610a71565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610afc82610ad1565b9050919050565b610b0c81610af1565b8114610b1757600080fd5b50565b600081359050610b2981610b03565b92915050565b6000819050919050565b610b4281610b2f565b8114610b4d57600080fd5b50565b600081359050610b5f81610b39565b92915050565b60008060408385031215610b7c57610b7b610acc565b5b6000610b8a85828601610b1a565b9250506020610b9b85828601610b50565b9150509250929050565b60008115159050919050565b610bba81610ba5565b82525050565b6000602082019050610bd56000830184610bb1565b92915050565b610be481610b2f565b82525050565b6000602082019050610bff6000830184610bdb565b92915050565b600080600060608486031215610c1e57610c1d610acc565b5b6000610c2c86828701610b1a565b9350506020610c3d86828701610b1a565b9250506040610c4e86828701610b50565b9150509250925092565b600060ff82169050919050565b610c6e81610c58565b82525050565b6000602082019050610c896000830184610c65565b92915050565b600060208284031215610ca557610ca4610acc565b5b6000610cb384828501610b1a565b91505092915050565b60008060408385031215610cd357610cd2610acc565b5b6000610ce185828601610b1a565b9250506020610cf285828601610b1a565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610d4357607f821691505b602082108103610d5657610d55610cfc565b5b50919050565b610d6581610af1565b82525050565b6000606082019050610d806000830186610d5c565b610d8d6020830185610bdb565b610d9a6040830184610bdb565b949350505050565b6000602082019050610db76000830184610d5c565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610df782610b2f565b9150610e0283610b2f565b9250828201905080821115610e1a57610e19610dbd565b5b9291505056fea26469706673582212204381ed75544c16386ece861caa46377c9a9e141573c84f2195760d75a29ac26964736f6c634300081c0033 diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 0866e69148..c2a78017da 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -9,12 +9,17 @@ use ethrex_rpc::{ clients::eth::{eth_sender::Overrides, EthClient}, types::receipt::RpcReceipt, }; +use itertools::Itertools; use keccak_hash::keccak; -use secp256k1::SecretKey; +use secp256k1::{PublicKey, SecretKey}; use std::{ fs::File, io::{self, BufRead}, path::Path, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, thread::sleep, time::Duration, }; @@ -24,7 +29,7 @@ use tokio::task::JoinSet; // https://medium.com/@kaishinaw/erc20-using-hardhat-a-comprehensive-guide-3211efba98d4 // If you want to create a new one, follow the steps above and take // the resulting artifact from the hardhat project. -const ERC20: &str = include_str!("./erc20.bin").trim_ascii(); +const ERC20: &str = include_str!("../ERC20.bin/ERC20.bin").trim_ascii(); #[derive(Subcommand)] pub(crate) enum Command { @@ -79,6 +84,12 @@ pub(crate) enum Command { help = "Number of ERC20 transactions to do" )] transactions: u64, + #[clap( + short = 'p', + long = "path", + help = "Path to the file containing private keys." + )] + path: String, }, } @@ -90,6 +101,15 @@ where Ok(io::BufReader::new(file).lines()) } +fn address_from_pub_key(public_key: PublicKey) -> H160 { + let bytes = public_key.serialize_uncompressed(); + let hash = keccak(&bytes[1..]); + let address_bytes: [u8; 20] = hash.as_ref().get(12..32).unwrap().try_into().unwrap(); + let address = Address::from(address_bytes); + + return address; +} + async fn transfer_from( pk: String, to_address: Address, @@ -173,10 +193,30 @@ async fn test_connection(cfg: EthrexL2Config) -> bool { false } -async fn erc20_load_test(config: &EthrexL2Config, count: u64) -> eyre::Result<()> { +async fn wait_receipt( + client: EthClient, + tx_hash: H256, + retries: Option, +) -> eyre::Result { + let retries = retries.unwrap_or_else(|| 10_u64); + for _ in 0..retries { + match client.get_transaction_receipt(tx_hash).await { + Err(_) | Ok(None) => { + let _ = tokio::time::sleep(Duration::from_secs(1)).await; + } + Ok(Some(receipt)) => return Ok(receipt), + }; + } + Err(eyre::eyre!( + "Failed to fetch receipt for tx with hash: {}", + tx_hash + )) +} + +async fn erc20_deploy(config: &EthrexL2Config) -> eyre::Result
{ let client = EthClient::new(&config.network.l2_rpc_url); let erc20_bytecode = hex::decode(ERC20)?; - let (_, contract_address) = client + let (tx_hash, contract_address) = client .deploy( config.wallet.address, config.wallet.private_key, @@ -185,27 +225,53 @@ async fn erc20_load_test(config: &EthrexL2Config, count: u64) -> eyre::Result<() ) .await .expect("Failed to deploy ERC20 with config: {config}"); - println!("ERC20 deployed to address: {contract_address}"); - println!("Checking ERC20 contract is reachable..."); - let balance_calldata = calldata::encode_calldata( - "balanceOf(address)", - &[Value::Address(config.wallet.address)], - )?; - let balance_request_tx = client - .build_eip1559_transaction( - contract_address, - config.wallet.address, - balance_calldata.into(), - Default::default(), - 100, - ) - .await - .unwrap(); - let balance_response = client - .send_eip1559_transaction(&balance_request_tx, &config.wallet.private_key) - .await - .expect("Failed to query balance for test load account"); - println!("ERC20 contract is reachable. Starting load test with balance: {balance_response}"); + let receipt = wait_receipt(client, tx_hash, None).await?; + match receipt { + RpcReceipt { receipt, .. } if receipt.status => Ok(contract_address), + _ => Err(eyre::eyre!("ERC20 deploy failed: deploy tx failed")), + } +} + +async fn claim_erc20_balances( + cfg: EthrexL2Config, + contract_address: Address, + private_keys: Vec, +) { + let accounts = private_keys + .iter() + .map(|pk| (pk.clone(), pk.public_key(secp256k1::SECP256K1))) + .collect_vec(); + let mut tasks = JoinSet::new(); + + for (sk, pk) in accounts { + let contract = contract_address.clone(); + let url = cfg.network.l2_rpc_url.clone(); + tasks.spawn(async move { + let client = EthClient::new(url.as_str()); + let claim_balance_calldata = calldata::encode_calldata("freeMint()", &[]).unwrap(); + let claim_tx = client + .build_eip1559_transaction( + contract, + address_from_pub_key(pk), + claim_balance_calldata.into(), + Default::default(), + 10, + ) + .await + .unwrap(); + let tx_hash = client.send_eip1559_transaction(&claim_tx, &sk).await.unwrap(); + wait_receipt(client, tx_hash, None) + }); + } +} + +async fn erc20_load_test( + config: &EthrexL2Config, + count: u64, + contract_address: Address, + senders: Vec, +) -> eyre::Result<()> { + let client = EthClient::new(&config.network.l2_rpc_url); let mut tasks = JoinSet::new(); let send_calldata = calldata::encode_calldata( "transfer(address,uint256)", @@ -224,10 +290,12 @@ async fn erc20_load_test(config: &EthrexL2Config, count: u64) -> eyre::Result<() 1, ) .await?; + let successful_txs = Arc::new(AtomicU64::new(0)); for i in 0..count { let tx = send_tx.clone(); let client = client.clone(); let pk = config.wallet.private_key.clone(); + let count = successful_txs.clone(); tasks.spawn(async move { let sent = client.send_eip1559_transaction(&tx, &pk).await; match sent { @@ -238,6 +306,7 @@ async fn erc20_load_test(config: &EthrexL2Config, count: u64) -> eyre::Result<() match client.get_transaction_receipt(hash).await { Ok(Some(RpcReceipt { receipt, .. })) if receipt.status => { println!("ERC-20 transfer number {i} was sucessful"); + count.fetch_add(1_u64, Ordering::Relaxed); } _ => { let _ = tokio::time::sleep(Duration::from_secs(1)).await; @@ -249,6 +318,14 @@ async fn erc20_load_test(config: &EthrexL2Config, count: u64) -> eyre::Result<() } }); } + if successful_txs.load(Ordering::Relaxed) == count { + println!("Every transaction was sucessful!"); + } else { + eprintln!( + "Only {} out of {count} were successful", + successful_txs.load(Ordering::Relaxed) + ); + } tasks.join_all().await; Ok(()) } @@ -328,8 +405,21 @@ impl Command { println!("Total retries: {retries}"); Ok(()) } - Command::ERC20 { transactions } => { - erc20_load_test(&cfg, transactions).await?; + Command::ERC20 { + transactions: transaction_count, + path, + } => { + let contract_address = erc20_deploy(&cfg).await?; + let private_keys: Result, _> = read_lines(path)? + .into_iter() + .map(|pk| { + pk.unwrap() + .parse::() + .expect("One of the private keys is invalid") + }) + .map(|pk| SecretKey::from_slice(pk.as_bytes())) + .collect(); + erc20_load_test(&cfg, transaction_count, contract_address, private_keys?).await?; Ok(()) } } From 99f0a3fac86eb3a4d3f1804cf50b5eacd2446d0a Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Tue, 25 Feb 2025 17:25:23 -0300 Subject: [PATCH 11/20] fix: remove print --- cmd/ethrex_l2/src/commands/test.rs | 123 ++++++++++++++++------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index c2a78017da..b22c53cd30 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -233,10 +233,10 @@ async fn erc20_deploy(config: &EthrexL2Config) -> eyre::Result
{ } async fn claim_erc20_balances( - cfg: EthrexL2Config, + cfg: &EthrexL2Config, contract_address: Address, - private_keys: Vec, -) { + private_keys: &Vec, +) -> eyre::Result<()> { let accounts = private_keys .iter() .map(|pk| (pk.clone(), pk.public_key(secp256k1::SECP256K1))) @@ -259,72 +259,85 @@ async fn claim_erc20_balances( ) .await .unwrap(); - let tx_hash = client.send_eip1559_transaction(&claim_tx, &sk).await.unwrap(); - wait_receipt(client, tx_hash, None) + let tx_hash = client + .send_eip1559_transaction(&claim_tx, &sk) + .await + .unwrap(); + wait_receipt(client, tx_hash, None).await }); } + let responses = tasks.join_all().await; + let every_account_has_balance = responses.iter().all(|response| match response { + Ok(RpcReceipt { receipt, .. }) => receipt.status, + Err(_) => false, + }); + + if every_account_has_balance { + Ok(()) + } else { + Err(eyre::eyre!("Failed to give balance to every account")) + } } async fn erc20_load_test( config: &EthrexL2Config, count: u64, contract_address: Address, - senders: Vec, + senders: &Vec, ) -> eyre::Result<()> { let client = EthClient::new(&config.network.l2_rpc_url); let mut tasks = JoinSet::new(); - let send_calldata = calldata::encode_calldata( - "transfer(address,uint256)", - &[ - Value::Address(config.wallet.address), - Value::Uint(U256::one()), - ], - ) - .unwrap(); - let send_tx = client - .build_eip1559_transaction( - H160::random(), - config.wallet.address.clone(), - send_calldata.into(), - Default::default(), - 1, - ) - .await?; let successful_txs = Arc::new(AtomicU64::new(0)); - for i in 0..count { - let tx = send_tx.clone(); - let client = client.clone(); - let pk = config.wallet.private_key.clone(); - let count = successful_txs.clone(); - tasks.spawn(async move { - let sent = client.send_eip1559_transaction(&tx, &pk).await; - match sent { - Ok(hash) => { - println!("ERC-20 transfer number {i} sent! Waiting for receipt..."); - let mut retries = 10_u64; - for _ in 0..retries { - match client.get_transaction_receipt(hash).await { - Ok(Some(RpcReceipt { receipt, .. })) if receipt.status => { - println!("ERC-20 transfer number {i} was sucessful"); - count.fetch_add(1_u64, Ordering::Relaxed); - } - _ => { - let _ = tokio::time::sleep(Duration::from_secs(1)).await; + let accounts = senders + .iter() + .map(|pk| (pk.clone(), pk.public_key(secp256k1::SECP256K1))) + .collect_vec(); + let transfer_number = Arc::new(AtomicU64::new(0)); + for (sk, pk) in accounts { + for i in 0..count { + let send_calldata = calldata::encode_calldata( + "transfer(address,uint256)", + &[Value::Address(H160::random()), Value::Uint(U256::one())], + ) + .unwrap(); + let send_tx = client + .build_eip1559_transaction( + contract_address, + address_from_pub_key(pk), + send_calldata.into(), + Default::default(), + 1, + ) + .await?; + let client = client.clone(); + let count = successful_txs.clone(); + tasks.spawn(async move { + let sent = client.send_eip1559_transaction(&send_tx, &sk).await; + let transfer_number = tx_number.fetch_add(1, Ordering::Relaxed); + match sent { + Ok(hash) => { + println!( + "ERC-20 transfer number {transfer_number} sent! Waiting for receipt..." + ); + let mut retries = 10_u64; + 'retry_loop: for _ in 0..retries { + match client.get_transaction_receipt(hash).await { + Ok(Some(RpcReceipt { receipt, .. })) if receipt.status => { + println!( + "ERC-20 transfer number {transfer_number} was sucessful" + ); + break 'retry_loop; + } + _ => { + let _ = tokio::time::sleep(Duration::from_secs(1)).await; + } } } } + Err(_) => println!("ERC-20 transfer number {transfer_number} FAILED"), } - Err(_) => println!("ERC-20 transfer number {i} FAILED"), - } - }); - } - if successful_txs.load(Ordering::Relaxed) == count { - println!("Every transaction was sucessful!"); - } else { - eprintln!( - "Only {} out of {count} were successful", - successful_txs.load(Ordering::Relaxed) - ); + }); + } } tasks.join_all().await; Ok(()) @@ -419,7 +432,9 @@ impl Command { }) .map(|pk| SecretKey::from_slice(pk.as_bytes())) .collect(); - erc20_load_test(&cfg, transaction_count, contract_address, private_keys?).await?; + let private_keys = private_keys?; + claim_erc20_balances(&cfg, contract_address, &private_keys).await?; + erc20_load_test(&cfg, transaction_count, contract_address, &private_keys).await?; Ok(()) } } From 467d773ddf477c5518f7618e6da02ae2a5cb6955 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Tue, 25 Feb 2025 17:58:20 -0300 Subject: [PATCH 12/20] feat: final changes --- cmd/ethrex_l2/README.md | 17 + .../src/ERC20-source/ERC20.bin/Context.bin | 0 .../src/ERC20-source/ERC20.bin/ERC20.bin | 0 .../ERC20-source/ERC20.bin/IERC1155Errors.bin | 0 .../src/ERC20-source/ERC20.bin/IERC20.bin | 0 .../ERC20-source/ERC20.bin/IERC20Errors.bin | 0 .../ERC20-source/ERC20.bin/IERC20Metadata.bin | 0 .../ERC20-source/ERC20.bin/IERC721Errors.bin | 0 .../src/ERC20-source/ERC20.bin/TestToken.bin | 1 + cmd/ethrex_l2/src/ERC20-source/ERC20.sol | 18 + cmd/ethrex_l2/src/ERC20-source/deps.sol | 624 ++++++++++++++++++ cmd/ethrex_l2/src/commands/test.rs | 52 +- 12 files changed, 683 insertions(+), 29 deletions(-) create mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/Context.bin create mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/ERC20.bin create mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC1155Errors.bin create mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20.bin create mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Errors.bin create mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Metadata.bin create mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC721Errors.bin create mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/TestToken.bin create mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.sol create mode 100644 cmd/ethrex_l2/src/ERC20-source/deps.sol diff --git a/cmd/ethrex_l2/README.md b/cmd/ethrex_l2/README.md index 7441580c11..53f43f453e 100644 --- a/cmd/ethrex_l2/README.md +++ b/cmd/ethrex_l2/README.md @@ -39,6 +39,7 @@ Commands: config CLI config commands. autocomplete Generate shell completion scripts. help Print this message or the help of the given subcommand(s) + test Run a load test Options: -h, --help Print help @@ -127,6 +128,22 @@ Options: -h, --help Print help ``` +### `test` + +``` +Run tests. + +Usage: ethrex_l2 test + +Commands: + load Make a load test sending transactions from a list of private keys. + erc20 Load test that deploys an ERC20 and runs transactions + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` + ## Examples ### `config` diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/Context.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/Context.bin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/ERC20.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/ERC20.bin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC1155Errors.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC1155Errors.bin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20.bin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Errors.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Errors.bin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Metadata.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Metadata.bin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC721Errors.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC721Errors.bin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/TestToken.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/TestToken.bin new file mode 100644 index 0000000000..ba80b3806a --- /dev/null +++ b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/TestToken.bin @@ -0,0 +1 @@ +608060405234801561000f575f5ffd5b506040518060400160405280600881526020017f46756e546f6b656e0000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f46554e0000000000000000000000000000000000000000000000000000000000815250816003908161008b9190610598565b50806004908161009b9190610598565b5050506100b83369d3c21bcecceda10000006100bd60201b60201c565b61077c565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361012d575f6040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161012491906106a6565b60405180910390fd5b61013e5f838361014260201b60201c565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610192578060025f82825461018691906106ec565b92505081905550610260565b5f5f5f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205490508181101561021b578381836040517fe450d38c0000000000000000000000000000000000000000000000000000000081526004016102129392919061072e565b60405180910390fd5b8181035f5f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2081905550505b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036102a7578060025f82825403925050819055506102f1565b805f5f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161034e9190610763565b60405180910390a3505050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806103d657607f821691505b6020821081036103e9576103e8610392565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f6008830261044b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610410565b6104558683610410565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f61049961049461048f8461046d565b610476565b61046d565b9050919050565b5f819050919050565b6104b28361047f565b6104c66104be826104a0565b84845461041c565b825550505050565b5f5f905090565b6104dd6104ce565b6104e88184846104a9565b505050565b5b8181101561050b576105005f826104d5565b6001810190506104ee565b5050565b601f82111561055057610521816103ef565b61052a84610401565b81016020851015610539578190505b61054d61054585610401565b8301826104ed565b50505b505050565b5f82821c905092915050565b5f6105705f1984600802610555565b1980831691505092915050565b5f6105888383610561565b9150826002028217905092915050565b6105a18261035b565b67ffffffffffffffff8111156105ba576105b9610365565b5b6105c482546103bf565b6105cf82828561050f565b5f60209050601f831160018114610600575f84156105ee578287015190505b6105f8858261057d565b86555061065f565b601f19841661060e866103ef565b5f5b8281101561063557848901518255600182019150602085019450602081019050610610565b86831015610652578489015161064e601f891682610561565b8355505b6001600288020188555050505b505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61069082610667565b9050919050565b6106a081610686565b82525050565b5f6020820190506106b95f830184610697565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6106f68261046d565b91506107018361046d565b9250828201905080821115610719576107186106bf565b5b92915050565b6107288161046d565b82525050565b5f6060820190506107415f830186610697565b61074e602083018561071f565b61075b604083018461071f565b949350505050565b5f6020820190506107765f83018461071f565b92915050565b610e8c806107895f395ff3fe608060405234801561000f575f5ffd5b506004361061009c575f3560e01c80635b70ea9f116100645780635b70ea9f1461015a57806370a082311461016457806395d89b4114610194578063a9059cbb146101b2578063dd62ed3e146101e25761009c565b806306fdde03146100a0578063095ea7b3146100be57806318160ddd146100ee57806323b872dd1461010c578063313ce5671461013c575b5f5ffd5b6100a8610212565b6040516100b59190610b05565b60405180910390f35b6100d860048036038101906100d39190610bb6565b6102a2565b6040516100e59190610c0e565b60405180910390f35b6100f66102c4565b6040516101039190610c36565b60405180910390f35b61012660048036038101906101219190610c4f565b6102cd565b6040516101339190610c0e565b60405180910390f35b6101446102fb565b6040516101519190610cba565b60405180910390f35b610162610303565b005b61017e60048036038101906101799190610cd3565b610319565b60405161018b9190610c36565b60405180910390f35b61019c61035e565b6040516101a99190610b05565b60405180910390f35b6101cc60048036038101906101c79190610bb6565b6103ee565b6040516101d99190610c0e565b60405180910390f35b6101fc60048036038101906101f79190610cfe565b610410565b6040516102099190610c36565b60405180910390f35b60606003805461022190610d69565b80601f016020809104026020016040519081016040528092919081815260200182805461024d90610d69565b80156102985780601f1061026f57610100808354040283529160200191610298565b820191905f5260205f20905b81548152906001019060200180831161027b57829003601f168201915b5050505050905090565b5f5f6102ac610492565b90506102b9818585610499565b600191505092915050565b5f600254905090565b5f5f6102d7610492565b90506102e48582856104ab565b6102ef85858561053e565b60019150509392505050565b5f6012905090565b6103173369d3c21bcecceda100000061062e565b565b5f5f5f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20549050919050565b60606004805461036d90610d69565b80601f016020809104026020016040519081016040528092919081815260200182805461039990610d69565b80156103e45780601f106103bb576101008083540402835291602001916103e4565b820191905f5260205f20905b8154815290600101906020018083116103c757829003601f168201915b5050505050905090565b5f5f6103f8610492565b905061040581858561053e565b600191505092915050565b5f60015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054905092915050565b5f33905090565b6104a683838360016106ad565b505050565b5f6104b68484610410565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105385781811015610529578281836040517ffb8f41b200000000000000000000000000000000000000000000000000000000815260040161052093929190610da8565b60405180910390fd5b61053784848484035f6106ad565b5b50505050565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036105ae575f6040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081526004016105a59190610ddd565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361061e575f6040517fec442f050000000000000000000000000000000000000000000000000000000081526004016106159190610ddd565b60405180910390fd5b61062983838361087c565b505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361069e575f6040517fec442f050000000000000000000000000000000000000000000000000000000081526004016106959190610ddd565b60405180910390fd5b6106a95f838361087c565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361071d575f6040517fe602df050000000000000000000000000000000000000000000000000000000081526004016107149190610ddd565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361078d575f6040517f94280d620000000000000000000000000000000000000000000000000000000081526004016107849190610ddd565b60405180910390fd5b8160015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055508015610876578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161086d9190610c36565b60405180910390a35b50505050565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108cc578060025f8282546108c09190610e23565b9250508190555061099a565b5f5f5f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054905081811015610955578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161094c93929190610da8565b60405180910390fd5b8181035f5f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2081905550505b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036109e1578060025f8282540392505081905550610a2b565b805f5f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610a889190610c36565b60405180910390a3505050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f610ad782610a95565b610ae18185610a9f565b9350610af1818560208601610aaf565b610afa81610abd565b840191505092915050565b5f6020820190508181035f830152610b1d8184610acd565b905092915050565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610b5282610b29565b9050919050565b610b6281610b48565b8114610b6c575f5ffd5b50565b5f81359050610b7d81610b59565b92915050565b5f819050919050565b610b9581610b83565b8114610b9f575f5ffd5b50565b5f81359050610bb081610b8c565b92915050565b5f5f60408385031215610bcc57610bcb610b25565b5b5f610bd985828601610b6f565b9250506020610bea85828601610ba2565b9150509250929050565b5f8115159050919050565b610c0881610bf4565b82525050565b5f602082019050610c215f830184610bff565b92915050565b610c3081610b83565b82525050565b5f602082019050610c495f830184610c27565b92915050565b5f5f5f60608486031215610c6657610c65610b25565b5b5f610c7386828701610b6f565b9350506020610c8486828701610b6f565b9250506040610c9586828701610ba2565b9150509250925092565b5f60ff82169050919050565b610cb481610c9f565b82525050565b5f602082019050610ccd5f830184610cab565b92915050565b5f60208284031215610ce857610ce7610b25565b5b5f610cf584828501610b6f565b91505092915050565b5f5f60408385031215610d1457610d13610b25565b5b5f610d2185828601610b6f565b9250506020610d3285828601610b6f565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680610d8057607f821691505b602082108103610d9357610d92610d3c565b5b50919050565b610da281610b48565b82525050565b5f606082019050610dbb5f830186610d99565b610dc86020830185610c27565b610dd56040830184610c27565b949350505050565b5f602082019050610df05f830184610d99565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610e2d82610b83565b9150610e3883610b83565b9250828201905080821115610e5057610e4f610df6565b5b9291505056fea2646970667358221220c2ace90351a6254148d1d6fc391d67d42f65e41f9290478674caf67a0ec34ec964736f6c634300081b0033 \ No newline at end of file diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.sol b/cmd/ethrex_l2/src/ERC20-source/ERC20.sol new file mode 100644 index 0000000000..b68ba88a10 --- /dev/null +++ b/cmd/ethrex_l2/src/ERC20-source/ERC20.sol @@ -0,0 +1,18 @@ +import "./deps.sol"; + +pragma solidity ^0.8.0; + +contract TestToken is ERC20 { + + uint256 constant defaultMint = 1000000 * (10**18); + + constructor() ERC20("TestToken", "TEST") { + _mint(msg.sender, defaultMint); + } + + // Mint a free amount for whoever + // calls the function + function freeMint() public { + _mint(msg.sender, defaultMint); + } +} diff --git a/cmd/ethrex_l2/src/ERC20-source/deps.sol b/cmd/ethrex_l2/src/ERC20-source/deps.sol new file mode 100644 index 0000000000..1851a2ebee --- /dev/null +++ b/cmd/ethrex_l2/src/ERC20-source/deps.sol @@ -0,0 +1,624 @@ +// Sources flattened with hardhat v2.22.18 https://hardhat.org + +// SPDX-License-Identifier: MIT + +// File @openzeppelin/contracts/interfaces/draft-IERC6093.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol) +pragma solidity ^0.8.20; + +/** + * @dev Standard ERC-20 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens. + */ +interface IERC20Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC20InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC20InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. + * @param spender Address that may be allowed to operate on tokens without being their owner. + * @param allowance Amount of tokens a `spender` is allowed to operate with. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC20InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `spender` to be approved. Used in approvals. + * @param spender Address that may be allowed to operate on tokens without being their owner. + */ + error ERC20InvalidSpender(address spender); +} + +/** + * @dev Standard ERC-721 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens. + */ +interface IERC721Errors { + /** + * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20. + * Used in balance queries. + * @param owner Address of the current owner of a token. + */ + error ERC721InvalidOwner(address owner); + + /** + * @dev Indicates a `tokenId` whose `owner` is the zero address. + * @param tokenId Identifier number of a token. + */ + error ERC721NonexistentToken(uint256 tokenId); + + /** + * @dev Indicates an error related to the ownership over a particular token. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param tokenId Identifier number of a token. + * @param owner Address of the current owner of a token. + */ + error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC721InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC721InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param tokenId Identifier number of a token. + */ + error ERC721InsufficientApproval(address operator, uint256 tokenId); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC721InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC721InvalidOperator(address operator); +} + +/** + * @dev Standard ERC-1155 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens. + */ +interface IERC1155Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + * @param tokenId Identifier number of a token. + */ + error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC1155InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC1155InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param owner Address of the current owner of a token. + */ + error ERC1155MissingApprovalForAll(address operator, address owner); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC1155InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC1155InvalidOperator(address operator); + + /** + * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. + * Used in batch transfers. + * @param idsLength Length of the array of token identifiers + * @param valuesLength Length of the array of token amounts + */ + error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); +} + + +// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Interface of the ERC-20 standard as defined in the ERC. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the value of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the value of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves a `value` amount of tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 value) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets a `value` amount of tokens as the allowance of `spender` over the + * caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 value) external returns (bool); + + /** + * @dev Moves a `value` amount of tokens from `from` to `to` using the + * allowance mechanism. `value` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 value) external returns (bool); +} + + +// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Interface for the optional metadata functions from the ERC-20 standard. + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} + + +// File @openzeppelin/contracts/utils/Context.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal view virtual returns (uint256) { + return 0; + } +} + + +// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.20; + + + + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * + * TIP: For a detailed writeup see our guide + * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * The default value of {decimals} is 18. To change this, you should override + * this function so it returns a different value. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC-20 + * applications. + */ +abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { + mapping(address account => uint256) private _balances; + + mapping(address account => mapping(address spender => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the default value returned by this function, unless + * it's overridden. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `value`. + */ + function transfer(address to, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, value); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, value); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Skips emitting an {Approval} event indicating an allowance update. This is not + * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve]. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `value`. + * - the caller must have allowance for ``from``'s tokens of at least + * `value`. + */ + function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, value); + _transfer(from, to, value); + return true; + } + + /** + * @dev Moves a `value` amount of tokens from `from` to `to`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _transfer(address from, address to, uint256 value) internal { + if (from == address(0)) { + revert ERC20InvalidSender(address(0)); + } + if (to == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(from, to, value); + } + + /** + * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` + * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding + * this function. + * + * Emits a {Transfer} event. + */ + function _update(address from, address to, uint256 value) internal virtual { + if (from == address(0)) { + // Overflow check required: The rest of the code assumes that totalSupply never overflows + _totalSupply += value; + } else { + uint256 fromBalance = _balances[from]; + if (fromBalance < value) { + revert ERC20InsufficientBalance(from, fromBalance, value); + } + unchecked { + // Overflow not possible: value <= fromBalance <= totalSupply. + _balances[from] = fromBalance - value; + } + } + + if (to == address(0)) { + unchecked { + // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. + _totalSupply -= value; + } + } else { + unchecked { + // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. + _balances[to] += value; + } + } + + emit Transfer(from, to, value); + } + + /** + * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). + * Relies on the `_update` mechanism + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _mint(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(address(0), account, value); + } + + /** + * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. + * Relies on the `_update` mechanism. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead + */ + function _burn(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidSender(address(0)); + } + _update(account, address(0), value); + } + + /** + * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + * + * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. + */ + function _approve(address owner, address spender, uint256 value) internal { + _approve(owner, spender, value, true); + } + + /** + * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. + * + * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by + * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any + * `Approval` event during `transferFrom` operations. + * + * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to + * true using the following override: + * + * ```solidity + * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { + * super._approve(owner, spender, value, true); + * } + * ``` + * + * Requirements are the same as {_approve}. + */ + function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { + if (owner == address(0)) { + revert ERC20InvalidApprover(address(0)); + } + if (spender == address(0)) { + revert ERC20InvalidSpender(address(0)); + } + _allowances[owner][spender] = value; + if (emitEvent) { + emit Approval(owner, spender, value); + } + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `value`. + * + * Does not update the allowance value in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Does not emit an {Approval} event. + */ + function _spendAllowance(address owner, address spender, uint256 value) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance < type(uint256).max) { + if (currentAllowance < value) { + revert ERC20InsufficientAllowance(spender, currentAllowance, value); + } + unchecked { + _approve(owner, spender, currentAllowance - value, false); + } + } + } +} diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index b22c53cd30..47df667195 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -27,9 +27,9 @@ use tokio::task::JoinSet; // ERC20 compiled artifact generated from this tutorial: // https://medium.com/@kaishinaw/erc20-using-hardhat-a-comprehensive-guide-3211efba98d4 -// If you want to create a new one, follow the steps above and take -// the resulting artifact from the hardhat project. -const ERC20: &str = include_str!("../ERC20.bin/ERC20.bin").trim_ascii(); +// If you want to modify the behaviour of the contract, edit the ERC20.sol file, +// and compile it with solc. +const ERC20: &str = include_str!("../ERC20-source/ERC20.bin/ERC20.bin").trim_ascii(); #[derive(Subcommand)] pub(crate) enum Command { @@ -76,12 +76,12 @@ pub(crate) enum Command { )] contract: bool, }, + #[clap(about = "Load test that deploys an ERC20 and runs transactions")] ERC20 { #[clap( short = 't', - long = "transactions", - default_value = "10000", - help = "Number of ERC20 transactions to do" + long = "transactions_amount", + help = "How many transactions each given account will do" )] transactions: u64, #[clap( @@ -213,6 +213,7 @@ async fn wait_receipt( )) } +// Deploy the ERC20 from the raw bytecode. async fn erc20_deploy(config: &EthrexL2Config) -> eyre::Result
{ let client = EthClient::new(&config.network.l2_rpc_url); let erc20_bytecode = hex::decode(ERC20)?; @@ -232,6 +233,8 @@ async fn erc20_deploy(config: &EthrexL2Config) -> eyre::Result
{ } } +// Given a vector of private keys, derive an address and claim +// ERC20 balance for each one of them. async fn claim_erc20_balances( cfg: &EthrexL2Config, contract_address: Address, @@ -267,34 +270,29 @@ async fn claim_erc20_balances( }); } let responses = tasks.join_all().await; - let every_account_has_balance = responses.iter().all(|response| match response { - Ok(RpcReceipt { receipt, .. }) => receipt.status, - Err(_) => false, - }); - - if every_account_has_balance { - Ok(()) - } else { - Err(eyre::eyre!("Failed to give balance to every account")) - } + let Some(Err(failed_reponse)) = responses.iter().find(|response| response.is_err()) else { + return Ok(()); + }; + return Err(eyre::eyre!( + "Failed to give balance to at least an account!: {failed_reponse}" + )); } async fn erc20_load_test( config: &EthrexL2Config, - count: u64, + tx_amount: u64, contract_address: Address, senders: &Vec, ) -> eyre::Result<()> { let client = EthClient::new(&config.network.l2_rpc_url); let mut tasks = JoinSet::new(); - let successful_txs = Arc::new(AtomicU64::new(0)); let accounts = senders .iter() .map(|pk| (pk.clone(), pk.public_key(secp256k1::SECP256K1))) .collect_vec(); let transfer_number = Arc::new(AtomicU64::new(0)); for (sk, pk) in accounts { - for i in 0..count { + for _ in 0..tx_amount { let send_calldata = calldata::encode_calldata( "transfer(address,uint256)", &[Value::Address(H160::random()), Value::Uint(U256::one())], @@ -310,22 +308,18 @@ async fn erc20_load_test( ) .await?; let client = client.clone(); - let count = successful_txs.clone(); + let transfer_number = transfer_number.clone(); tasks.spawn(async move { + let tx_number = transfer_number.fetch_add(1, Ordering::Relaxed); let sent = client.send_eip1559_transaction(&send_tx, &sk).await; - let transfer_number = tx_number.fetch_add(1, Ordering::Relaxed); match sent { Ok(hash) => { - println!( - "ERC-20 transfer number {transfer_number} sent! Waiting for receipt..." - ); - let mut retries = 10_u64; + println!("ERC-20 transfer number {tx_number} sent! Waiting for receipt..."); + let retries = 10_u64; 'retry_loop: for _ in 0..retries { match client.get_transaction_receipt(hash).await { Ok(Some(RpcReceipt { receipt, .. })) if receipt.status => { - println!( - "ERC-20 transfer number {transfer_number} was sucessful" - ); + println!("ERC-20 transfer number {tx_number} was sucessful"); break 'retry_loop; } _ => { @@ -334,7 +328,7 @@ async fn erc20_load_test( } } } - Err(_) => println!("ERC-20 transfer number {transfer_number} FAILED"), + Err(_) => println!("ERC-20 transfer number {tx_number} FAILED"), } }); } From 1ff28cf3da5a64c32a4f90213f9707e559a21dbc Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Tue, 25 Feb 2025 18:22:38 -0300 Subject: [PATCH 13/20] chore: remove from gitignore --- .gitignore | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitignore b/.gitignore index 3cd8b9e6ff..c33a8c30ba 100644 --- a/.gitignore +++ b/.gitignore @@ -63,8 +63,3 @@ cmd/ef_tests/state/levm_perfgraphs *.svg prof_*.json prof.json - -# Used by the ERC 20 under cmd/ethrex_l2 -artifacts -node_modules -cache From b592af5721ff6e0659ace01878961fd9efba6cbc Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Tue, 25 Feb 2025 18:40:07 -0300 Subject: [PATCH 14/20] chore: remove redundant .bin files --- cmd/ethrex_l2/src/ERC20-source/ERC20.bin/Context.bin | 0 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/ERC20.bin | 0 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC1155Errors.bin | 0 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20.bin | 0 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Errors.bin | 0 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Metadata.bin | 0 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC721Errors.bin | 0 cmd/ethrex_l2/src/commands/test.rs | 5 ++--- 8 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/Context.bin delete mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/ERC20.bin delete mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC1155Errors.bin delete mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20.bin delete mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Errors.bin delete mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Metadata.bin delete mode 100644 cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC721Errors.bin diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/Context.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/Context.bin deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/ERC20.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/ERC20.bin deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC1155Errors.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC1155Errors.bin deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20.bin deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Errors.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Errors.bin deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Metadata.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC20Metadata.bin deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC721Errors.bin b/cmd/ethrex_l2/src/ERC20-source/ERC20.bin/IERC721Errors.bin deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 2873e02bb4..8097a62523 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -11,8 +11,7 @@ use ethrex_rpc::{ }; use itertools::Itertools; use keccak_hash::keccak; -use secp256k1::{PublicKey, SecretKey}; -use secp256k1::SecretKey; +use secp256k1::{SecretKey, PublicKey}; use std::time::Instant; use std::{ fs::File, @@ -31,7 +30,7 @@ use tokio::task::JoinSet; // https://medium.com/@kaishinaw/erc20-using-hardhat-a-comprehensive-guide-3211efba98d4 // If you want to modify the behaviour of the contract, edit the ERC20.sol file, // and compile it with solc. -const ERC20: &str = include_str!("../ERC20-source/ERC20.bin/ERC20.bin").trim_ascii(); +const ERC20: &str = include_str!("../ERC20-source/ERC20.bin/TestToken.bin").trim_ascii(); #[derive(Subcommand)] pub(crate) enum Command { From 9a6ef8f527e135a7692911919b8f3aaf4cd36c7b Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Tue, 25 Feb 2025 19:35:07 -0300 Subject: [PATCH 15/20] fix: better error handling, move test data files --- cmd/ethrex_l2/src/commands/test.rs | 30 +- test_data/ERC20/ERC20.bin/TestToken.bin | 1 + test_data/ERC20/ERC20.sol | 18 + test_data/ERC20/deps.sol | 624 ++++++++++++++++++++++++ 4 files changed, 664 insertions(+), 9 deletions(-) create mode 100644 test_data/ERC20/ERC20.bin/TestToken.bin create mode 100644 test_data/ERC20/ERC20.sol create mode 100644 test_data/ERC20/deps.sol diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 8097a62523..9974c13881 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -11,7 +11,7 @@ use ethrex_rpc::{ }; use itertools::Itertools; use keccak_hash::keccak; -use secp256k1::{SecretKey, PublicKey}; +use secp256k1::{PublicKey, SecretKey}; use std::time::Instant; use std::{ fs::File, @@ -30,7 +30,8 @@ use tokio::task::JoinSet; // https://medium.com/@kaishinaw/erc20-using-hardhat-a-comprehensive-guide-3211efba98d4 // If you want to modify the behaviour of the contract, edit the ERC20.sol file, // and compile it with solc. -const ERC20: &str = include_str!("../ERC20-source/ERC20.bin/TestToken.bin").trim_ascii(); +const ERC20: &str = + include_str!("../../../../test_data/ERC20/ERC20.bin/TestToken.bin").trim_ascii(); #[derive(Subcommand)] pub(crate) enum Command { @@ -270,13 +271,24 @@ async fn claim_erc20_balances( wait_receipt(client, tx_hash, None).await }); } - let responses = tasks.join_all().await; - let Some(Err(failed_reponse)) = responses.iter().find(|response| response.is_err()) else { - return Ok(()); - }; - return Err(eyre::eyre!( - "Failed to give balance to at least an account!: {failed_reponse}" - )); + for response in tasks.join_all().await { + match response { + Ok(RpcReceipt { receipt, .. }) if !receipt.status => { + return Err(eyre::eyre!( + "Failed to assign balance to an account, tx failed with receipt: {receipt:?}" + )) + } + Err(err) => { + return Err(eyre::eyre!( + "Failed to assign balance to an account, tx failed: {err}" + )) + } + Ok(_) => { + continue; + } + } + } + return Ok(()); } async fn erc20_load_test( diff --git a/test_data/ERC20/ERC20.bin/TestToken.bin b/test_data/ERC20/ERC20.bin/TestToken.bin new file mode 100644 index 0000000000..ba80b3806a --- /dev/null +++ b/test_data/ERC20/ERC20.bin/TestToken.bin @@ -0,0 +1 @@ +608060405234801561000f575f5ffd5b506040518060400160405280600881526020017f46756e546f6b656e0000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f46554e0000000000000000000000000000000000000000000000000000000000815250816003908161008b9190610598565b50806004908161009b9190610598565b5050506100b83369d3c21bcecceda10000006100bd60201b60201c565b61077c565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361012d575f6040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161012491906106a6565b60405180910390fd5b61013e5f838361014260201b60201c565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610192578060025f82825461018691906106ec565b92505081905550610260565b5f5f5f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205490508181101561021b578381836040517fe450d38c0000000000000000000000000000000000000000000000000000000081526004016102129392919061072e565b60405180910390fd5b8181035f5f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2081905550505b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036102a7578060025f82825403925050819055506102f1565b805f5f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161034e9190610763565b60405180910390a3505050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806103d657607f821691505b6020821081036103e9576103e8610392565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f6008830261044b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610410565b6104558683610410565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f61049961049461048f8461046d565b610476565b61046d565b9050919050565b5f819050919050565b6104b28361047f565b6104c66104be826104a0565b84845461041c565b825550505050565b5f5f905090565b6104dd6104ce565b6104e88184846104a9565b505050565b5b8181101561050b576105005f826104d5565b6001810190506104ee565b5050565b601f82111561055057610521816103ef565b61052a84610401565b81016020851015610539578190505b61054d61054585610401565b8301826104ed565b50505b505050565b5f82821c905092915050565b5f6105705f1984600802610555565b1980831691505092915050565b5f6105888383610561565b9150826002028217905092915050565b6105a18261035b565b67ffffffffffffffff8111156105ba576105b9610365565b5b6105c482546103bf565b6105cf82828561050f565b5f60209050601f831160018114610600575f84156105ee578287015190505b6105f8858261057d565b86555061065f565b601f19841661060e866103ef565b5f5b8281101561063557848901518255600182019150602085019450602081019050610610565b86831015610652578489015161064e601f891682610561565b8355505b6001600288020188555050505b505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61069082610667565b9050919050565b6106a081610686565b82525050565b5f6020820190506106b95f830184610697565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6106f68261046d565b91506107018361046d565b9250828201905080821115610719576107186106bf565b5b92915050565b6107288161046d565b82525050565b5f6060820190506107415f830186610697565b61074e602083018561071f565b61075b604083018461071f565b949350505050565b5f6020820190506107765f83018461071f565b92915050565b610e8c806107895f395ff3fe608060405234801561000f575f5ffd5b506004361061009c575f3560e01c80635b70ea9f116100645780635b70ea9f1461015a57806370a082311461016457806395d89b4114610194578063a9059cbb146101b2578063dd62ed3e146101e25761009c565b806306fdde03146100a0578063095ea7b3146100be57806318160ddd146100ee57806323b872dd1461010c578063313ce5671461013c575b5f5ffd5b6100a8610212565b6040516100b59190610b05565b60405180910390f35b6100d860048036038101906100d39190610bb6565b6102a2565b6040516100e59190610c0e565b60405180910390f35b6100f66102c4565b6040516101039190610c36565b60405180910390f35b61012660048036038101906101219190610c4f565b6102cd565b6040516101339190610c0e565b60405180910390f35b6101446102fb565b6040516101519190610cba565b60405180910390f35b610162610303565b005b61017e60048036038101906101799190610cd3565b610319565b60405161018b9190610c36565b60405180910390f35b61019c61035e565b6040516101a99190610b05565b60405180910390f35b6101cc60048036038101906101c79190610bb6565b6103ee565b6040516101d99190610c0e565b60405180910390f35b6101fc60048036038101906101f79190610cfe565b610410565b6040516102099190610c36565b60405180910390f35b60606003805461022190610d69565b80601f016020809104026020016040519081016040528092919081815260200182805461024d90610d69565b80156102985780601f1061026f57610100808354040283529160200191610298565b820191905f5260205f20905b81548152906001019060200180831161027b57829003601f168201915b5050505050905090565b5f5f6102ac610492565b90506102b9818585610499565b600191505092915050565b5f600254905090565b5f5f6102d7610492565b90506102e48582856104ab565b6102ef85858561053e565b60019150509392505050565b5f6012905090565b6103173369d3c21bcecceda100000061062e565b565b5f5f5f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20549050919050565b60606004805461036d90610d69565b80601f016020809104026020016040519081016040528092919081815260200182805461039990610d69565b80156103e45780601f106103bb576101008083540402835291602001916103e4565b820191905f5260205f20905b8154815290600101906020018083116103c757829003601f168201915b5050505050905090565b5f5f6103f8610492565b905061040581858561053e565b600191505092915050565b5f60015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054905092915050565b5f33905090565b6104a683838360016106ad565b505050565b5f6104b68484610410565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105385781811015610529578281836040517ffb8f41b200000000000000000000000000000000000000000000000000000000815260040161052093929190610da8565b60405180910390fd5b61053784848484035f6106ad565b5b50505050565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036105ae575f6040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081526004016105a59190610ddd565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361061e575f6040517fec442f050000000000000000000000000000000000000000000000000000000081526004016106159190610ddd565b60405180910390fd5b61062983838361087c565b505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361069e575f6040517fec442f050000000000000000000000000000000000000000000000000000000081526004016106959190610ddd565b60405180910390fd5b6106a95f838361087c565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361071d575f6040517fe602df050000000000000000000000000000000000000000000000000000000081526004016107149190610ddd565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361078d575f6040517f94280d620000000000000000000000000000000000000000000000000000000081526004016107849190610ddd565b60405180910390fd5b8160015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055508015610876578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161086d9190610c36565b60405180910390a35b50505050565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108cc578060025f8282546108c09190610e23565b9250508190555061099a565b5f5f5f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054905081811015610955578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161094c93929190610da8565b60405180910390fd5b8181035f5f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2081905550505b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036109e1578060025f8282540392505081905550610a2b565b805f5f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610a889190610c36565b60405180910390a3505050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f610ad782610a95565b610ae18185610a9f565b9350610af1818560208601610aaf565b610afa81610abd565b840191505092915050565b5f6020820190508181035f830152610b1d8184610acd565b905092915050565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610b5282610b29565b9050919050565b610b6281610b48565b8114610b6c575f5ffd5b50565b5f81359050610b7d81610b59565b92915050565b5f819050919050565b610b9581610b83565b8114610b9f575f5ffd5b50565b5f81359050610bb081610b8c565b92915050565b5f5f60408385031215610bcc57610bcb610b25565b5b5f610bd985828601610b6f565b9250506020610bea85828601610ba2565b9150509250929050565b5f8115159050919050565b610c0881610bf4565b82525050565b5f602082019050610c215f830184610bff565b92915050565b610c3081610b83565b82525050565b5f602082019050610c495f830184610c27565b92915050565b5f5f5f60608486031215610c6657610c65610b25565b5b5f610c7386828701610b6f565b9350506020610c8486828701610b6f565b9250506040610c9586828701610ba2565b9150509250925092565b5f60ff82169050919050565b610cb481610c9f565b82525050565b5f602082019050610ccd5f830184610cab565b92915050565b5f60208284031215610ce857610ce7610b25565b5b5f610cf584828501610b6f565b91505092915050565b5f5f60408385031215610d1457610d13610b25565b5b5f610d2185828601610b6f565b9250506020610d3285828601610b6f565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680610d8057607f821691505b602082108103610d9357610d92610d3c565b5b50919050565b610da281610b48565b82525050565b5f606082019050610dbb5f830186610d99565b610dc86020830185610c27565b610dd56040830184610c27565b949350505050565b5f602082019050610df05f830184610d99565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610e2d82610b83565b9150610e3883610b83565b9250828201905080821115610e5057610e4f610df6565b5b9291505056fea2646970667358221220c2ace90351a6254148d1d6fc391d67d42f65e41f9290478674caf67a0ec34ec964736f6c634300081b0033 \ No newline at end of file diff --git a/test_data/ERC20/ERC20.sol b/test_data/ERC20/ERC20.sol new file mode 100644 index 0000000000..b68ba88a10 --- /dev/null +++ b/test_data/ERC20/ERC20.sol @@ -0,0 +1,18 @@ +import "./deps.sol"; + +pragma solidity ^0.8.0; + +contract TestToken is ERC20 { + + uint256 constant defaultMint = 1000000 * (10**18); + + constructor() ERC20("TestToken", "TEST") { + _mint(msg.sender, defaultMint); + } + + // Mint a free amount for whoever + // calls the function + function freeMint() public { + _mint(msg.sender, defaultMint); + } +} diff --git a/test_data/ERC20/deps.sol b/test_data/ERC20/deps.sol new file mode 100644 index 0000000000..1851a2ebee --- /dev/null +++ b/test_data/ERC20/deps.sol @@ -0,0 +1,624 @@ +// Sources flattened with hardhat v2.22.18 https://hardhat.org + +// SPDX-License-Identifier: MIT + +// File @openzeppelin/contracts/interfaces/draft-IERC6093.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol) +pragma solidity ^0.8.20; + +/** + * @dev Standard ERC-20 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens. + */ +interface IERC20Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC20InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC20InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. + * @param spender Address that may be allowed to operate on tokens without being their owner. + * @param allowance Amount of tokens a `spender` is allowed to operate with. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC20InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `spender` to be approved. Used in approvals. + * @param spender Address that may be allowed to operate on tokens without being their owner. + */ + error ERC20InvalidSpender(address spender); +} + +/** + * @dev Standard ERC-721 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens. + */ +interface IERC721Errors { + /** + * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20. + * Used in balance queries. + * @param owner Address of the current owner of a token. + */ + error ERC721InvalidOwner(address owner); + + /** + * @dev Indicates a `tokenId` whose `owner` is the zero address. + * @param tokenId Identifier number of a token. + */ + error ERC721NonexistentToken(uint256 tokenId); + + /** + * @dev Indicates an error related to the ownership over a particular token. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param tokenId Identifier number of a token. + * @param owner Address of the current owner of a token. + */ + error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC721InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC721InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param tokenId Identifier number of a token. + */ + error ERC721InsufficientApproval(address operator, uint256 tokenId); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC721InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC721InvalidOperator(address operator); +} + +/** + * @dev Standard ERC-1155 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens. + */ +interface IERC1155Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + * @param tokenId Identifier number of a token. + */ + error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC1155InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC1155InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param owner Address of the current owner of a token. + */ + error ERC1155MissingApprovalForAll(address operator, address owner); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC1155InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC1155InvalidOperator(address operator); + + /** + * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. + * Used in batch transfers. + * @param idsLength Length of the array of token identifiers + * @param valuesLength Length of the array of token amounts + */ + error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); +} + + +// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Interface of the ERC-20 standard as defined in the ERC. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the value of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the value of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves a `value` amount of tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 value) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets a `value` amount of tokens as the allowance of `spender` over the + * caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 value) external returns (bool); + + /** + * @dev Moves a `value` amount of tokens from `from` to `to` using the + * allowance mechanism. `value` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 value) external returns (bool); +} + + +// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Interface for the optional metadata functions from the ERC-20 standard. + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} + + +// File @openzeppelin/contracts/utils/Context.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal view virtual returns (uint256) { + return 0; + } +} + + +// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v5.2.0 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.20; + + + + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * + * TIP: For a detailed writeup see our guide + * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * The default value of {decimals} is 18. To change this, you should override + * this function so it returns a different value. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC-20 + * applications. + */ +abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { + mapping(address account => uint256) private _balances; + + mapping(address account => mapping(address spender => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the default value returned by this function, unless + * it's overridden. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `value`. + */ + function transfer(address to, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, value); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, value); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Skips emitting an {Approval} event indicating an allowance update. This is not + * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve]. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `value`. + * - the caller must have allowance for ``from``'s tokens of at least + * `value`. + */ + function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, value); + _transfer(from, to, value); + return true; + } + + /** + * @dev Moves a `value` amount of tokens from `from` to `to`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _transfer(address from, address to, uint256 value) internal { + if (from == address(0)) { + revert ERC20InvalidSender(address(0)); + } + if (to == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(from, to, value); + } + + /** + * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` + * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding + * this function. + * + * Emits a {Transfer} event. + */ + function _update(address from, address to, uint256 value) internal virtual { + if (from == address(0)) { + // Overflow check required: The rest of the code assumes that totalSupply never overflows + _totalSupply += value; + } else { + uint256 fromBalance = _balances[from]; + if (fromBalance < value) { + revert ERC20InsufficientBalance(from, fromBalance, value); + } + unchecked { + // Overflow not possible: value <= fromBalance <= totalSupply. + _balances[from] = fromBalance - value; + } + } + + if (to == address(0)) { + unchecked { + // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. + _totalSupply -= value; + } + } else { + unchecked { + // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. + _balances[to] += value; + } + } + + emit Transfer(from, to, value); + } + + /** + * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). + * Relies on the `_update` mechanism + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _mint(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(address(0), account, value); + } + + /** + * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. + * Relies on the `_update` mechanism. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead + */ + function _burn(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidSender(address(0)); + } + _update(account, address(0), value); + } + + /** + * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + * + * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. + */ + function _approve(address owner, address spender, uint256 value) internal { + _approve(owner, spender, value, true); + } + + /** + * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. + * + * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by + * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any + * `Approval` event during `transferFrom` operations. + * + * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to + * true using the following override: + * + * ```solidity + * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { + * super._approve(owner, spender, value, true); + * } + * ``` + * + * Requirements are the same as {_approve}. + */ + function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { + if (owner == address(0)) { + revert ERC20InvalidApprover(address(0)); + } + if (spender == address(0)) { + revert ERC20InvalidSpender(address(0)); + } + _allowances[owner][spender] = value; + if (emitEvent) { + emit Approval(owner, spender, value); + } + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `value`. + * + * Does not update the allowance value in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Does not emit an {Approval} event. + */ + function _spendAllowance(address owner, address spender, uint256 value) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance < type(uint256).max) { + if (currentAllowance < value) { + revert ERC20InsufficientAllowance(spender, currentAllowance, value); + } + unchecked { + _approve(owner, spender, currentAllowance - value, false); + } + } + } +} From 576dc867b0c97227bfb936633db94e99d52069da Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Wed, 26 Feb 2025 12:30:55 -0300 Subject: [PATCH 16/20] makefile: add erc20 load test --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 32c301b26c..3b97ab2f55 100644 --- a/Makefile +++ b/Makefile @@ -209,3 +209,8 @@ rm-test-db: ## 🛑 Removes the DB used by the ethrex client used for testing flamegraph: ## 🚧 Runs a load-test. Run make start-node-with-flamegraph and in a new terminal make flamegraph sudo bash scripts/flamegraph.sh + +test_data/ERC20/ERC20.bin: ## 🔨 Build the ERC20 contract for the load test + solc ./test_data/ERC20.sol -o $@ +load-test-erc20: test_data/ERC20/ERC20.bin install-cli + ethrex_l2 test erc20 --path test_data/private_keys.txt -t 100 From 6a3f67d2d738b481e4c5d6b6ba52ff7fa94a5304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Rodr=C3=ADguez=20Chatruc?= <49622509+jrchatruc@users.noreply.github.com> Date: Wed, 26 Feb 2025 12:27:07 -0300 Subject: [PATCH 17/20] Remove l2/metrics from cmd/ethrex/Cargo.toml --- cmd/ethrex/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ethrex/Cargo.toml b/cmd/ethrex/Cargo.toml index 538fe6b999..6e4505dc06 100644 --- a/cmd/ethrex/Cargo.toml +++ b/cmd/ethrex/Cargo.toml @@ -44,7 +44,7 @@ path = "./ethrex.rs" default = ["libmdbx", "c-kzg", "blst"] dev = ["dep:ethrex-dev"] c-kzg = ["ethrex-vm/c-kzg", "ethrex-common/c-kzg", "ethrex-blockchain/c-kzg", "ethrex-p2p/c-kzg"] -metrics = ["ethrex-blockchain/metrics", "ethrex-l2/metrics"] +metrics = ["ethrex-blockchain/metrics"] libmdbx = ["ethrex-storage/libmdbx"] redb = ["dep:redb", "ethrex-storage/redb"] blst = ["ethrex-vm/blst"] From f9cc0f291416a1074f6904cc3bb72139aeb94236 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Wed, 26 Feb 2025 13:08:39 -0300 Subject: [PATCH 18/20] debug: remove cache --- .github/workflows/ci_l1.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_l1.yaml b/.github/workflows/ci_l1.yaml index 1e36f4b0d0..454a137bb6 100644 --- a/.github/workflows/ci_l1.yaml +++ b/.github/workflows/ci_l1.yaml @@ -61,8 +61,8 @@ jobs: with: toolchain: ${{ env.RUST_VERSION }} - - name: Caching - uses: Swatinem/rust-cache@v2 + # - name: Caching + # uses: Swatinem/rust-cache@v2 - name: Run tests run: | From f3e3acbb616b51f1a2a3dc5dcc95247d136a219f Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Wed, 26 Feb 2025 13:15:02 -0300 Subject: [PATCH 19/20] lint: cargo fmt --- cmd/ethrex_l2/src/commands/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 9974c13881..e366e95e95 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -283,7 +283,7 @@ async fn claim_erc20_balances( "Failed to assign balance to an account, tx failed: {err}" )) } - Ok(_) => { + Ok(_) => { continue; } } From b6a1d5f22154bc962b811faac6bd5a5b4fb64fe9 Mon Sep 17 00:00:00 2001 From: fkrause98 Date: Wed, 26 Feb 2025 13:15:24 -0300 Subject: [PATCH 20/20] Revert "debug: remove cache" This reverts commit f9cc0f291416a1074f6904cc3bb72139aeb94236. --- .github/workflows/ci_l1.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_l1.yaml b/.github/workflows/ci_l1.yaml index 454a137bb6..1e36f4b0d0 100644 --- a/.github/workflows/ci_l1.yaml +++ b/.github/workflows/ci_l1.yaml @@ -61,8 +61,8 @@ jobs: with: toolchain: ${{ env.RUST_VERSION }} - # - name: Caching - # uses: Swatinem/rust-cache@v2 + - name: Caching + uses: Swatinem/rust-cache@v2 - name: Run tests run: |