From 7079022b21b1d942c0398795ce3557a8833ebe99 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Fri, 7 Jul 2023 11:03:38 -0500 Subject: [PATCH 01/13] chore: add Wallet struct and other relevant functions to 'HEVM.sol' --- abi/abi/HEVM.sol | 7 + abi/src/bindings/hevm.rs | 510 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 496 insertions(+), 21 deletions(-) diff --git a/abi/abi/HEVM.sol b/abi/abi/HEVM.sol index 8433fafbbeace..55898983ad142 100644 --- a/abi/abi/HEVM.sol +++ b/abi/abi/HEVM.sol @@ -2,6 +2,7 @@ struct Log { bytes32[] topics; bytes data; } struct Rpc { string name; string url; } struct DirEntry { string errorMessage; string path; uint64 depth; bool isDir; bool isSymlink; } struct FsMetadata { bool isDir; bool isSymlink; uint256 length; bool readOnly; uint256 modified; uint256 accessed; uint256 created; } +struct Wallet { address addr; uint256 publicKeyX; uint256 publicKeyY; uint256 privateKey; } allowCheatcodes(address) @@ -57,6 +58,12 @@ deriveKey(string,uint32,string)(uint256) deriveKey(string,string,uint32,string)(uint256) rememberKey(uint256)(address) +createWallet(string)(Wallet) +createWallet(uint256)(Wallet) +createWallet(uint256,string)(Wallet) +sign(Wallet,bytes32)(uint8,bytes32,bytes32) +getNonce(Wallet)(uint64) + prank(address) prank(address,address) readCallers()(uint256,address,address) diff --git a/abi/src/bindings/hevm.rs b/abi/src/bindings/hevm.rs index d04125eb72e0d..94fec1ded9d1e 100644 --- a/abi/src/bindings/hevm.rs +++ b/abi/src/bindings/hevm.rs @@ -441,6 +441,94 @@ pub mod hevm { }, ], ), + ( + ::std::borrow::ToOwned::to_owned("createWallet"), + ::std::vec![ + ::ethers_core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("createWallet"), + inputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::String, + internal_type: ::core::option::Option::None, + }, + ], + outputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::Tuple( + ::std::vec![ + ::ethers_core::abi::ethabi::ParamType::Address, + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ], + ), + internal_type: ::core::option::Option::None, + }, + ], + constant: ::core::option::Option::None, + state_mutability: ::ethers_core::abi::ethabi::StateMutability::NonPayable, + }, + ::ethers_core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("createWallet"), + inputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + internal_type: ::core::option::Option::None, + }, + ], + outputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::Tuple( + ::std::vec![ + ::ethers_core::abi::ethabi::ParamType::Address, + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ], + ), + internal_type: ::core::option::Option::None, + }, + ], + constant: ::core::option::Option::None, + state_mutability: ::ethers_core::abi::ethabi::StateMutability::NonPayable, + }, + ::ethers_core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("createWallet"), + inputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + internal_type: ::core::option::Option::None, + }, + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::String, + internal_type: ::core::option::Option::None, + }, + ], + outputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::Tuple( + ::std::vec![ + ::ethers_core::abi::ethabi::ParamType::Address, + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ], + ), + internal_type: ::core::option::Option::None, + }, + ], + constant: ::core::option::Option::None, + state_mutability: ::ethers_core::abi::ethabi::StateMutability::NonPayable, + }, + ], + ), ( ::std::borrow::ToOwned::to_owned("deal"), ::std::vec![ @@ -1969,6 +2057,32 @@ pub mod hevm { ( ::std::borrow::ToOwned::to_owned("getNonce"), ::std::vec![ + ::ethers_core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("getNonce"), + inputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::Tuple( + ::std::vec![ + ::ethers_core::abi::ethabi::ParamType::Address, + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ], + ), + internal_type: ::core::option::Option::None, + }, + ], + outputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::Uint(64usize), + internal_type: ::core::option::Option::None, + }, + ], + constant: ::core::option::Option::None, + state_mutability: ::ethers_core::abi::ethabi::StateMutability::NonPayable, + }, ::ethers_core::abi::ethabi::Function { name: ::std::borrow::ToOwned::to_owned("getNonce"), inputs: ::std::vec![ @@ -4208,6 +4322,53 @@ pub mod hevm { constant: ::core::option::Option::None, state_mutability: ::ethers_core::abi::ethabi::StateMutability::NonPayable, }, + ::ethers_core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned("sign"), + inputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::Tuple( + ::std::vec![ + ::ethers_core::abi::ethabi::ParamType::Address, + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ::ethers_core::abi::ethabi::ParamType::Uint(256usize), + ], + ), + internal_type: ::core::option::Option::None, + }, + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::FixedBytes( + 32usize, + ), + internal_type: ::core::option::Option::None, + }, + ], + outputs: ::std::vec![ + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::Uint(8usize), + internal_type: ::core::option::Option::None, + }, + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::FixedBytes( + 32usize, + ), + internal_type: ::core::option::Option::None, + }, + ::ethers_core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers_core::abi::ethabi::ParamType::FixedBytes( + 32usize, + ), + internal_type: ::core::option::Option::None, + }, + ], + constant: ::core::option::Option::None, + state_mutability: ::ethers_core::abi::ethabi::StateMutability::NonPayable, + }, ], ), ( @@ -4896,6 +5057,58 @@ pub mod hevm { .method_hash([152, 104, 0, 52], p0) .expect("method not found (this should never happen)") } + ///Calls the contract's `createWallet` (0x7404f1d2) function + pub fn create_wallet_0( + &self, + p0: ::std::string::String, + ) -> ::ethers_contract::builders::ContractCall< + M, + ( + ::ethers_core::types::Address, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ), + > { + self.0 + .method_hash([116, 4, 241, 210], p0) + .expect("method not found (this should never happen)") + } + ///Calls the contract's `createWallet` (0x7a675bb6) function + pub fn create_wallet_1( + &self, + p0: ::ethers_core::types::U256, + ) -> ::ethers_contract::builders::ContractCall< + M, + ( + ::ethers_core::types::Address, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ), + > { + self.0 + .method_hash([122, 103, 91, 182], p0) + .expect("method not found (this should never happen)") + } + ///Calls the contract's `createWallet` (0xed7c5462) function + pub fn create_wallet_2( + &self, + p0: ::ethers_core::types::U256, + p1: ::std::string::String, + ) -> ::ethers_contract::builders::ContractCall< + M, + ( + ::ethers_core::types::Address, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ), + > { + self.0 + .method_hash([237, 124, 84, 98], (p0, p1)) + .expect("method not found (this should never happen)") + } ///Calls the contract's `deal` (0xc88a5e6d) function pub fn deal( &self, @@ -5530,8 +5743,17 @@ pub mod hevm { .method_hash([40, 162, 73, 176], p0) .expect("method not found (this should never happen)") } + ///Calls the contract's `getNonce` (0xa5748aad) function + pub fn get_nonce_0( + &self, + p0: Wallet, + ) -> ::ethers_contract::builders::ContractCall { + self.0 + .method_hash([165, 116, 138, 173], (p0,)) + .expect("method not found (this should never happen)") + } ///Calls the contract's `getNonce` (0x2d0335ab) function - pub fn get_nonce( + pub fn get_nonce_1( &self, p0: ::ethers_core::types::Address, ) -> ::ethers_contract::builders::ContractCall { @@ -6406,7 +6628,7 @@ pub mod hevm { .expect("method not found (this should never happen)") } ///Calls the contract's `sign` (0xe341eaa4) function - pub fn sign( + pub fn sign_0( &self, p0: ::ethers_core::types::U256, p1: [u8; 32], @@ -6415,6 +6637,16 @@ pub mod hevm { .method_hash([227, 65, 234, 164], (p0, p1)) .expect("method not found (this should never happen)") } + ///Calls the contract's `sign` (0xb25c5a25) function + pub fn sign_1( + &self, + p0: Wallet, + p1: [u8; 32], + ) -> ::ethers_contract::builders::ContractCall { + self.0 + .method_hash([178, 92, 90, 37], (p0, p1)) + .expect("method not found (this should never happen)") + } ///Calls the contract's `skip` (0xdd82d13e) function pub fn skip( &self, @@ -6930,6 +7162,48 @@ pub mod hevm { )] #[ethcall(name = "createSelectFork", abi = "createSelectFork(string)")] pub struct CreateSelectFork0Call(pub ::std::string::String); + ///Container type for all input parameters for the `createWallet` function with signature `createWallet(string)` and selector `0x7404f1d2` + #[derive( + Clone, + ::ethers_contract::EthCall, + ::ethers_contract::EthDisplay, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + #[ethcall(name = "createWallet", abi = "createWallet(string)")] + pub struct CreateWallet0Call(pub ::std::string::String); + ///Container type for all input parameters for the `createWallet` function with signature `createWallet(uint256)` and selector `0x7a675bb6` + #[derive( + Clone, + ::ethers_contract::EthCall, + ::ethers_contract::EthDisplay, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + #[ethcall(name = "createWallet", abi = "createWallet(uint256)")] + pub struct CreateWallet1Call(pub ::ethers_core::types::U256); + ///Container type for all input parameters for the `createWallet` function with signature `createWallet(uint256,string)` and selector `0xed7c5462` + #[derive( + Clone, + ::ethers_contract::EthCall, + ::ethers_contract::EthDisplay, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + #[ethcall(name = "createWallet", abi = "createWallet(uint256,string)")] + pub struct CreateWallet2Call( + pub ::ethers_core::types::U256, + pub ::std::string::String, + ); ///Container type for all input parameters for the `deal` function with signature `deal(address,uint256)` and selector `0xc88a5e6d` #[derive( Clone, @@ -7787,6 +8061,19 @@ pub mod hevm { )] #[ethcall(name = "getLabel", abi = "getLabel(address)")] pub struct GetLabelCall(pub ::ethers_core::types::Address); + ///Container type for all input parameters for the `getNonce` function with signature `getNonce((address,uint256,uint256,uint256))` and selector `0xa5748aad` + #[derive( + Clone, + ::ethers_contract::EthCall, + ::ethers_contract::EthDisplay, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + #[ethcall(name = "getNonce", abi = "getNonce((address,uint256,uint256,uint256))")] + pub struct GetNonce0Call(pub Wallet); ///Container type for all input parameters for the `getNonce` function with signature `getNonce(address)` and selector `0x2d0335ab` #[derive( Clone, @@ -7799,7 +8086,7 @@ pub mod hevm { Hash )] #[ethcall(name = "getNonce", abi = "getNonce(address)")] - pub struct GetNonceCall(pub ::ethers_core::types::Address); + pub struct GetNonce1Call(pub ::ethers_core::types::Address); ///Container type for all input parameters for the `getRecordedLogs` function with signature `getRecordedLogs()` and selector `0x191553a4` #[derive( Clone, @@ -9042,7 +9329,20 @@ pub mod hevm { Hash )] #[ethcall(name = "sign", abi = "sign(uint256,bytes32)")] - pub struct SignCall(pub ::ethers_core::types::U256, pub [u8; 32]); + pub struct Sign0Call(pub ::ethers_core::types::U256, pub [u8; 32]); + ///Container type for all input parameters for the `sign` function with signature `sign((address,uint256,uint256,uint256),bytes32)` and selector `0xb25c5a25` + #[derive( + Clone, + ::ethers_contract::EthCall, + ::ethers_contract::EthDisplay, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + #[ethcall(name = "sign", abi = "sign((address,uint256,uint256,uint256),bytes32)")] + pub struct Sign1Call(pub Wallet, pub [u8; 32]); ///Container type for all input parameters for the `skip` function with signature `skip(bool)` and selector `0xdd82d13e` #[derive( Clone, @@ -9402,6 +9702,9 @@ pub mod hevm { CreateSelectFork1(CreateSelectFork1Call), CreateSelectFork2(CreateSelectFork2Call), CreateSelectFork0(CreateSelectFork0Call), + CreateWallet0(CreateWallet0Call), + CreateWallet1(CreateWallet1Call), + CreateWallet2(CreateWallet2Call), Deal(DealCall), DeriveKey0(DeriveKey0Call), DeriveKey1(DeriveKey1Call), @@ -9460,7 +9763,8 @@ pub mod hevm { GetCode(GetCodeCall), GetDeployedCode(GetDeployedCodeCall), GetLabel(GetLabelCall), - GetNonce(GetNonceCall), + GetNonce0(GetNonce0Call), + GetNonce1(GetNonce1Call), GetRecordedLogs(GetRecordedLogsCall), IsPersistent(IsPersistentCall), Label(LabelCall), @@ -9545,7 +9849,8 @@ pub mod hevm { SetEnv(SetEnvCall), SetNonce(SetNonceCall), SetNonceUnsafe(SetNonceUnsafeCall), - Sign(SignCall), + Sign0(Sign0Call), + Sign1(Sign1Call), Skip(SkipCall), Snapshot(SnapshotCall), StartBroadcast0(StartBroadcast0Call), @@ -9667,6 +9972,18 @@ pub mod hevm { ) { return Ok(Self::CreateSelectFork0(decoded)); } + if let Ok(decoded) + = ::decode(data) { + return Ok(Self::CreateWallet0(decoded)); + } + if let Ok(decoded) + = ::decode(data) { + return Ok(Self::CreateWallet1(decoded)); + } + if let Ok(decoded) + = ::decode(data) { + return Ok(Self::CreateWallet2(decoded)); + } if let Ok(decoded) = ::decode(data) { return Ok(Self::Deal(decoded)); @@ -9906,8 +10223,12 @@ pub mod hevm { return Ok(Self::GetLabel(decoded)); } if let Ok(decoded) - = ::decode(data) { - return Ok(Self::GetNonce(decoded)); + = ::decode(data) { + return Ok(Self::GetNonce0(decoded)); + } + if let Ok(decoded) + = ::decode(data) { + return Ok(Self::GetNonce1(decoded)); } if let Ok(decoded) = ::decode(data) { @@ -10274,8 +10595,12 @@ pub mod hevm { return Ok(Self::SetNonceUnsafe(decoded)); } if let Ok(decoded) - = ::decode(data) { - return Ok(Self::Sign(decoded)); + = ::decode(data) { + return Ok(Self::Sign0(decoded)); + } + if let Ok(decoded) + = ::decode(data) { + return Ok(Self::Sign1(decoded)); } if let Ok(decoded) = ::decode(data) { @@ -10436,6 +10761,15 @@ pub mod hevm { Self::CreateSelectFork0(element) => { ::ethers_core::abi::AbiEncode::encode(element) } + Self::CreateWallet0(element) => { + ::ethers_core::abi::AbiEncode::encode(element) + } + Self::CreateWallet1(element) => { + ::ethers_core::abi::AbiEncode::encode(element) + } + Self::CreateWallet2(element) => { + ::ethers_core::abi::AbiEncode::encode(element) + } Self::Deal(element) => ::ethers_core::abi::AbiEncode::encode(element), Self::DeriveKey0(element) => { ::ethers_core::abi::AbiEncode::encode(element) @@ -10558,7 +10892,12 @@ pub mod hevm { ::ethers_core::abi::AbiEncode::encode(element) } Self::GetLabel(element) => ::ethers_core::abi::AbiEncode::encode(element), - Self::GetNonce(element) => ::ethers_core::abi::AbiEncode::encode(element), + Self::GetNonce0(element) => { + ::ethers_core::abi::AbiEncode::encode(element) + } + Self::GetNonce1(element) => { + ::ethers_core::abi::AbiEncode::encode(element) + } Self::GetRecordedLogs(element) => { ::ethers_core::abi::AbiEncode::encode(element) } @@ -10773,7 +11112,8 @@ pub mod hevm { Self::SetNonceUnsafe(element) => { ::ethers_core::abi::AbiEncode::encode(element) } - Self::Sign(element) => ::ethers_core::abi::AbiEncode::encode(element), + Self::Sign0(element) => ::ethers_core::abi::AbiEncode::encode(element), + Self::Sign1(element) => ::ethers_core::abi::AbiEncode::encode(element), Self::Skip(element) => ::ethers_core::abi::AbiEncode::encode(element), Self::Snapshot(element) => ::ethers_core::abi::AbiEncode::encode(element), Self::StartBroadcast0(element) => { @@ -10868,6 +11208,9 @@ pub mod hevm { Self::CreateSelectFork1(element) => ::core::fmt::Display::fmt(element, f), Self::CreateSelectFork2(element) => ::core::fmt::Display::fmt(element, f), Self::CreateSelectFork0(element) => ::core::fmt::Display::fmt(element, f), + Self::CreateWallet0(element) => ::core::fmt::Display::fmt(element, f), + Self::CreateWallet1(element) => ::core::fmt::Display::fmt(element, f), + Self::CreateWallet2(element) => ::core::fmt::Display::fmt(element, f), Self::Deal(element) => ::core::fmt::Display::fmt(element, f), Self::DeriveKey0(element) => ::core::fmt::Display::fmt(element, f), Self::DeriveKey1(element) => ::core::fmt::Display::fmt(element, f), @@ -10928,7 +11271,8 @@ pub mod hevm { Self::GetCode(element) => ::core::fmt::Display::fmt(element, f), Self::GetDeployedCode(element) => ::core::fmt::Display::fmt(element, f), Self::GetLabel(element) => ::core::fmt::Display::fmt(element, f), - Self::GetNonce(element) => ::core::fmt::Display::fmt(element, f), + Self::GetNonce0(element) => ::core::fmt::Display::fmt(element, f), + Self::GetNonce1(element) => ::core::fmt::Display::fmt(element, f), Self::GetRecordedLogs(element) => ::core::fmt::Display::fmt(element, f), Self::IsPersistent(element) => ::core::fmt::Display::fmt(element, f), Self::Label(element) => ::core::fmt::Display::fmt(element, f), @@ -11025,7 +11369,8 @@ pub mod hevm { Self::SetEnv(element) => ::core::fmt::Display::fmt(element, f), Self::SetNonce(element) => ::core::fmt::Display::fmt(element, f), Self::SetNonceUnsafe(element) => ::core::fmt::Display::fmt(element, f), - Self::Sign(element) => ::core::fmt::Display::fmt(element, f), + Self::Sign0(element) => ::core::fmt::Display::fmt(element, f), + Self::Sign1(element) => ::core::fmt::Display::fmt(element, f), Self::Skip(element) => ::core::fmt::Display::fmt(element, f), Self::Snapshot(element) => ::core::fmt::Display::fmt(element, f), Self::StartBroadcast0(element) => ::core::fmt::Display::fmt(element, f), @@ -11159,6 +11504,21 @@ pub mod hevm { Self::CreateSelectFork0(value) } } + impl ::core::convert::From for HEVMCalls { + fn from(value: CreateWallet0Call) -> Self { + Self::CreateWallet0(value) + } + } + impl ::core::convert::From for HEVMCalls { + fn from(value: CreateWallet1Call) -> Self { + Self::CreateWallet1(value) + } + } + impl ::core::convert::From for HEVMCalls { + fn from(value: CreateWallet2Call) -> Self { + Self::CreateWallet2(value) + } + } impl ::core::convert::From for HEVMCalls { fn from(value: DealCall) -> Self { Self::Deal(value) @@ -11449,9 +11809,14 @@ pub mod hevm { Self::GetLabel(value) } } - impl ::core::convert::From for HEVMCalls { - fn from(value: GetNonceCall) -> Self { - Self::GetNonce(value) + impl ::core::convert::From for HEVMCalls { + fn from(value: GetNonce0Call) -> Self { + Self::GetNonce0(value) + } + } + impl ::core::convert::From for HEVMCalls { + fn from(value: GetNonce1Call) -> Self { + Self::GetNonce1(value) } } impl ::core::convert::From for HEVMCalls { @@ -11874,9 +12239,14 @@ pub mod hevm { Self::SetNonceUnsafe(value) } } - impl ::core::convert::From for HEVMCalls { - fn from(value: SignCall) -> Self { - Self::Sign(value) + impl ::core::convert::From for HEVMCalls { + fn from(value: Sign0Call) -> Self { + Self::Sign0(value) + } + } + impl ::core::convert::From for HEVMCalls { + fn from(value: Sign1Call) -> Self { + Self::Sign1(value) } } impl ::core::convert::From for HEVMCalls { @@ -12115,6 +12485,63 @@ pub mod hevm { Hash )] pub struct CreateSelectFork0Return(pub ::ethers_core::types::U256); + ///Container type for all return fields from the `createWallet` function with signature `createWallet(string)` and selector `0x7404f1d2` + #[derive( + Clone, + ::ethers_contract::EthAbiType, + ::ethers_contract::EthAbiCodec, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + pub struct CreateWallet0Return( + pub ( + ::ethers_core::types::Address, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ), + ); + ///Container type for all return fields from the `createWallet` function with signature `createWallet(uint256)` and selector `0x7a675bb6` + #[derive( + Clone, + ::ethers_contract::EthAbiType, + ::ethers_contract::EthAbiCodec, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + pub struct CreateWallet1Return( + pub ( + ::ethers_core::types::Address, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ), + ); + ///Container type for all return fields from the `createWallet` function with signature `createWallet(uint256,string)` and selector `0xed7c5462` + #[derive( + Clone, + ::ethers_contract::EthAbiType, + ::ethers_contract::EthAbiCodec, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + pub struct CreateWallet2Return( + pub ( + ::ethers_core::types::Address, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ::ethers_core::types::U256, + ), + ); ///Container type for all return fields from the `deriveKey` function with signature `deriveKey(string,uint32)` and selector `0x6229498b` #[derive( Clone, @@ -12545,6 +12972,18 @@ pub mod hevm { Hash )] pub struct GetLabelReturn(pub ::std::string::String); + ///Container type for all return fields from the `getNonce` function with signature `getNonce((address,uint256,uint256,uint256))` and selector `0xa5748aad` + #[derive( + Clone, + ::ethers_contract::EthAbiType, + ::ethers_contract::EthAbiCodec, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + pub struct GetNonce0Return(pub u64); ///Container type for all return fields from the `getRecordedLogs` function with signature `getRecordedLogs()` and selector `0x191553a4` #[derive( Clone, @@ -13216,7 +13655,19 @@ pub mod hevm { Eq, Hash )] - pub struct SignReturn(pub u8, pub [u8; 32], pub [u8; 32]); + pub struct Sign0Return(pub u8, pub [u8; 32], pub [u8; 32]); + ///Container type for all return fields from the `sign` function with signature `sign((address,uint256,uint256,uint256),bytes32)` and selector `0xb25c5a25` + #[derive( + Clone, + ::ethers_contract::EthAbiType, + ::ethers_contract::EthAbiCodec, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + pub struct Sign1Return(pub u8, pub [u8; 32], pub [u8; 32]); ///Container type for all return fields from the `snapshot` function with signature `snapshot()` and selector `0x9711715a` #[derive( Clone, @@ -13297,4 +13748,21 @@ pub mod hevm { pub name: ::std::string::String, pub url: ::std::string::String, } + ///`Wallet(address,uint256,uint256,uint256)` + #[derive( + Clone, + ::ethers_contract::EthAbiType, + ::ethers_contract::EthAbiCodec, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + pub struct Wallet { + pub addr: ::ethers_core::types::Address, + pub public_key_x: ::ethers_core::types::U256, + pub public_key_y: ::ethers_core::types::U256, + pub private_key: ::ethers_core::types::U256, + } } From 66b8bf5c127def080748e3f50ae7b5da21df326c Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Fri, 7 Jul 2023 11:07:06 -0500 Subject: [PATCH 02/13] feat: add 'HEVMCalls::GetNonce0' to env utils for Wallet, rename 'GetNonce' to 'GetNonce1' --- evm/src/executor/inspector/cheatcodes/env.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/evm/src/executor/inspector/cheatcodes/env.rs b/evm/src/executor/inspector/cheatcodes/env.rs index 00501c2574a82..9d06ac4b86c23 100644 --- a/evm/src/executor/inspector/cheatcodes/env.rs +++ b/evm/src/executor/inspector/cheatcodes/env.rs @@ -503,7 +503,7 @@ pub fn apply( Ok(Bytes::new()) }, )??, - HEVMCalls::GetNonce(inner) => { + HEVMCalls::GetNonce1(inner) => { correct_sender_nonce( b160_to_h160(data.env.tx.caller), &mut data.journaled_state, @@ -519,6 +519,22 @@ pub fn apply( let account = data.journaled_state.state().get(&h160_to_b160(inner.0)).unwrap(); abi::encode(&[Token::Uint(account.info.nonce.into())]).into() } + HEVMCalls::GetNonce0(inner) => { + correct_sender_nonce( + b160_to_h160(data.env.tx.caller), + &mut data.journaled_state, + &mut data.db, + state, + )?; + + // TODO: this is probably not a good long-term solution since it might mess up the gas + // calculations + data.journaled_state.load_account(h160_to_b160(inner.0.addr), data.db)?; + + // we can safely unwrap because `load_account` insert inner.0 to DB. + let account = data.journaled_state.state().get(&h160_to_b160(inner.0.addr)).unwrap(); + abi::encode(&[Token::Uint(account.info.nonce.into())]).into() + } HEVMCalls::ChainId(inner) => { ensure!(inner.0 <= U256::from(u64::MAX), "Chain ID must be less than 2^64 - 1"); data.env.cfg.chain_id = inner.0.into(); From e8aeeb60f88cca64023550786c421ca86d3ba047 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Fri, 7 Jul 2023 11:09:11 -0500 Subject: [PATCH 03/13] feat: add CreateWallet/Sign to HEVM match in cheat utils --- evm/src/executor/inspector/cheatcodes/util.rs | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/evm/src/executor/inspector/cheatcodes/util.rs b/evm/src/executor/inspector/cheatcodes/util.rs index abb9aad05aba6..9d381915c67c3 100644 --- a/evm/src/executor/inspector/cheatcodes/util.rs +++ b/evm/src/executor/inspector/cheatcodes/util.rs @@ -12,7 +12,11 @@ use ethers::{ abi::{AbiEncode, Address, ParamType, Token}, core::k256::elliptic_curve::Curve, prelude::{ - k256::{ecdsa::SigningKey, elliptic_curve::bigint::Encoding, Secp256k1}, + k256::{ + ecdsa::SigningKey, + elliptic_curve::{bigint::Encoding, sec1::ToEncodedPoint}, + Secp256k1, + }, LocalWallet, Signer, H160, *, }, signers::{ @@ -23,7 +27,7 @@ use ethers::{ MnemonicBuilder, }, types::{transaction::eip2718::TypedTransaction, NameOrAddress, H256, U256}, - utils, + utils::{self, keccak256}, }; use foundry_common::{fmt::*, RpcUrl}; use revm::{ @@ -120,6 +124,20 @@ fn sign(private_key: U256, digest: H256, chain_id: U256) -> Result { Ok((sig.v, r_bytes, s_bytes).encode().into()) } +fn create_wallet(private_key: U256) -> std::result::Result<(H160, U256, U256, U256), Error> { + let key = parse_private_key(private_key)?; + let addr = utils::secret_key_to_address(&key); + + let pub_key = key.verifying_key().as_affine().to_encoded_point(false); + let pub_key_x = pub_key.x().ok_or("No x coordinate was found")?; + let pub_key_y = pub_key.y().ok_or("No y coordinate was found")?; + + let pub_key_x = U256::from(pub_key_x.as_slice()); + let pub_key_y = U256::from(pub_key_y.as_slice()); + + Ok((addr, pub_key_x, pub_key_y, private_key)) +} + enum WordlistLang { ChineseSimplified, ChineseTraditional, @@ -222,7 +240,21 @@ pub fn apply( ) -> Option { Some(match call { HEVMCalls::Addr(inner) => addr(inner.0), - HEVMCalls::Sign(inner) => sign(inner.0, inner.1.into(), data.env.cfg.chain_id.into()), + HEVMCalls::Sign0(inner) => sign(inner.0, inner.1.into(), data.env.cfg.chain_id.into()), + HEVMCalls::CreateWallet0(inner) => { + let res = create_wallet(U256::from(keccak256(&inner.0))).unwrap(); + state.labels.insert(res.0, inner.0.clone()); + Ok(res.encode().into()) + } + HEVMCalls::CreateWallet1(inner) => Ok(create_wallet(inner.0).unwrap().encode().into()), + HEVMCalls::CreateWallet2(inner) => { + let res = create_wallet(inner.0).unwrap(); + state.labels.insert(res.0, inner.1.clone()); + Ok(res.encode().into()) + } + HEVMCalls::Sign1(inner) => { + sign(inner.0.private_key, inner.1.into(), data.env.cfg.chain_id.into()) + } HEVMCalls::DeriveKey0(inner) => { derive_key::(&inner.0, DEFAULT_DERIVATION_PATH_PREFIX, inner.1) } From 81957b8d431fde0f72f6270079374a014d42a673 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Fri, 7 Jul 2023 11:10:31 -0500 Subject: [PATCH 04/13] chore: add Wallet struct and other relevant functions to 'Cheats.sol' --- testdata/cheats/Vm.sol | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index bf010823928e3..e997721a350cf 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -44,6 +44,14 @@ interface Vm { uint256 created; } + // Returned by 'createWallet'. Used with 'sign' and 'getNonce' + struct Wallet { + address addr; + uint256 publicKeyX; + uint256 publicKeyY; + uint256 privateKey; + } + // Set block.timestamp (newTimestamp) function warp(uint256) external; @@ -90,6 +98,21 @@ interface Vm { // Adds a private key to the local forge wallet and returns the address function rememberKey(uint256) external returns (address); + // Derives a private key from the name, labels the account with that name, and returns the wallet + function createWallet(string calldata) external returns (Wallet memory); + + // Generates a wallet from the private key and returns the wallet + function createWallet(uint256) external returns (Wallet memory); + + // Generates a wallet from the private key, labels the account with that name, and returns the wallet + function createWallet(uint256, string calldata) external returns (Wallet memory); + + // Signs data, (Wallet, digest) => (v, r, s) + function sign(Wallet calldata, bytes32) external returns (uint8, bytes32, bytes32); + + // Get nonce for a Wallet + function getNonce(Wallet calldata) external returns (uint64); + // Performs a foreign function call via terminal, (stringInputs) => (result) function ffi(string[] calldata) external returns (bytes memory); From d837d99661d56e634d7a3594ba1a435c61e3f524 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Fri, 7 Jul 2023 11:11:40 -0500 Subject: [PATCH 05/13] test: add tests for 'createWallet' in Wallet.t.sol --- testdata/cheats/Wallet.t.sol | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 testdata/cheats/Wallet.t.sol diff --git a/testdata/cheats/Wallet.t.sol b/testdata/cheats/Wallet.t.sol new file mode 100644 index 0000000000000..1473424a86e80 --- /dev/null +++ b/testdata/cheats/Wallet.t.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "./Cheats.sol"; + +contract WalletTest is DSTest { + Cheats constant cheats = Cheats(HEVM_ADDRESS); + + function testCreateWalletStringPrivAndLabel() public { + bytes memory privKey = "this is a priv key"; + Cheats.Wallet memory wallet = cheats.createWallet(string(privKey)); + + address expectedAddr = cheats.addr(wallet.privateKey); + assertEq(expectedAddr, wallet.addr); + + string memory label = cheats.getLabel(wallet.addr); + assertEq(label, string(privKey), "labelled address != wallet.addr"); + } + + function testCreateWalletPrivKeyNoLabel(uint248 pk) public { + cheats.assume(pk != 0); + + Cheats.Wallet memory wallet = cheats.createWallet(uint256(pk)); + + address expectedAddr = cheats.addr(wallet.privateKey); + assertEq(expectedAddr, wallet.addr); + } + + function testCreateWalletPrivKeyWithLabel(uint248 pk) public { + string memory label = "labelled wallet"; + + cheats.assume(pk != 0); + Cheats.Wallet memory wallet = cheats.createWallet(pk, label); + + address expectedAddr = cheats.addr(wallet.privateKey); + assertEq(expectedAddr, wallet.addr); + + string memory expectedLabel = cheats.getLabel(wallet.addr); + assertEq(expectedLabel, label, "labelled address != wallet.addr"); + } +} From 6e3fe1df1c40c704123bed3ebb440acfca41dd05 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Fri, 7 Jul 2023 11:26:09 -0500 Subject: [PATCH 06/13] test: add tests for getNonce/sign in Wallet.t.sol --- testdata/cheats/Wallet.t.sol | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/testdata/cheats/Wallet.t.sol b/testdata/cheats/Wallet.t.sol index 1473424a86e80..eee276d2cbd9c 100644 --- a/testdata/cheats/Wallet.t.sol +++ b/testdata/cheats/Wallet.t.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; import "./Cheats.sol"; +contract Foo {} + contract WalletTest is DSTest { Cheats constant cheats = Cheats(HEVM_ADDRESS); @@ -39,4 +41,35 @@ contract WalletTest is DSTest { string memory expectedLabel = cheats.getLabel(wallet.addr); assertEq(expectedLabel, label, "labelled address != wallet.addr"); } + + function testSignWithWalletDigest(uint248 pk, bytes32 digest) public { + cheats.assume(pk != 0); + Cheats.Wallet memory wallet = cheats.createWallet(uint256(pk)); + + (uint8 v, bytes32 r, bytes32 s) = cheats.sign(wallet, digest); + + address recovered = ecrecover(digest, v, r, s); + assertEq(recovered, wallet.addr); + } + + function testSignWithWalletMessage(uint248 pk, bytes memory message) + public + { + testSignWithWalletDigest(pk, keccak256(message)); + } + + function testGetNonceWallet(uint248 pk) public { + cheats.assume(pk != 0); + Cheats.Wallet memory wallet = cheats.createWallet(uint256(pk)); + + uint64 nonce1 = cheats.getNonce(wallet); + + cheats.startPrank(wallet.addr); + new Foo(); + new Foo(); + cheats.stopPrank(); + + uint64 nonce2 = cheats.getNonce(wallet); + assertEq(nonce1 + 2, nonce2); + } } From 1762683ab839177847bbf068c217ab6e0856f044 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Fri, 7 Jul 2023 12:17:03 -0500 Subject: [PATCH 07/13] chore: remove 'unwrap' after 'create_wallet' calls --- evm/src/executor/inspector/cheatcodes/util.rs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/evm/src/executor/inspector/cheatcodes/util.rs b/evm/src/executor/inspector/cheatcodes/util.rs index 9d381915c67c3..8cb0489c12416 100644 --- a/evm/src/executor/inspector/cheatcodes/util.rs +++ b/evm/src/executor/inspector/cheatcodes/util.rs @@ -124,7 +124,7 @@ fn sign(private_key: U256, digest: H256, chain_id: U256) -> Result { Ok((sig.v, r_bytes, s_bytes).encode().into()) } -fn create_wallet(private_key: U256) -> std::result::Result<(H160, U256, U256, U256), Error> { +fn create_wallet(private_key: U256, label: Option, state: &mut Cheatcodes) -> Result { let key = parse_private_key(private_key)?; let addr = utils::secret_key_to_address(&key); @@ -135,7 +135,11 @@ fn create_wallet(private_key: U256) -> std::result::Result<(H160, U256, U256, U2 let pub_key_x = U256::from(pub_key_x.as_slice()); let pub_key_y = U256::from(pub_key_y.as_slice()); - Ok((addr, pub_key_x, pub_key_y, private_key)) + if let Some(label) = label { + state.labels.insert(addr, label); + } + + Ok((addr, pub_key_x, pub_key_y, private_key).encode().into()) } enum WordlistLang { @@ -242,16 +246,10 @@ pub fn apply( HEVMCalls::Addr(inner) => addr(inner.0), HEVMCalls::Sign0(inner) => sign(inner.0, inner.1.into(), data.env.cfg.chain_id.into()), HEVMCalls::CreateWallet0(inner) => { - let res = create_wallet(U256::from(keccak256(&inner.0))).unwrap(); - state.labels.insert(res.0, inner.0.clone()); - Ok(res.encode().into()) - } - HEVMCalls::CreateWallet1(inner) => Ok(create_wallet(inner.0).unwrap().encode().into()), - HEVMCalls::CreateWallet2(inner) => { - let res = create_wallet(inner.0).unwrap(); - state.labels.insert(res.0, inner.1.clone()); - Ok(res.encode().into()) + create_wallet(U256::from(keccak256(&inner.0)), Some(inner.0.clone()), state) } + HEVMCalls::CreateWallet1(inner) => create_wallet(inner.0, None, state), + HEVMCalls::CreateWallet2(inner) => create_wallet(inner.0, Some(inner.1.clone()), state), HEVMCalls::Sign1(inner) => { sign(inner.0.private_key, inner.1.into(), data.env.cfg.chain_id.into()) } From 0dcb4b21af9d83d203f4b092e4aef85b5b8ff418 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Fri, 7 Jul 2023 12:34:55 -0500 Subject: [PATCH 08/13] style: forge fmt 'testdata/cheats/Wallet.t.sol' --- testdata/cheats/Wallet.t.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/testdata/cheats/Wallet.t.sol b/testdata/cheats/Wallet.t.sol index eee276d2cbd9c..607abbb3489ce 100644 --- a/testdata/cheats/Wallet.t.sol +++ b/testdata/cheats/Wallet.t.sol @@ -52,9 +52,7 @@ contract WalletTest is DSTest { assertEq(recovered, wallet.addr); } - function testSignWithWalletMessage(uint248 pk, bytes memory message) - public - { + function testSignWithWalletMessage(uint248 pk, bytes memory message) public { testSignWithWalletDigest(pk, keccak256(message)); } From ee61825110f3b8edbdcdb800ffcd48634c337c2e Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Mon, 10 Jul 2023 08:43:36 -0500 Subject: [PATCH 09/13] test: add x & y to addr check to Wallet.t.sol --- testdata/cheats/Wallet.t.sol | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/testdata/cheats/Wallet.t.sol b/testdata/cheats/Wallet.t.sol index 607abbb3489ce..830d24c244f53 100644 --- a/testdata/cheats/Wallet.t.sol +++ b/testdata/cheats/Wallet.t.sol @@ -9,13 +9,22 @@ contract Foo {} contract WalletTest is DSTest { Cheats constant cheats = Cheats(HEVM_ADDRESS); + function addressOf(uint256 x, uint256 y) internal pure returns (address) { + return address(uint160(uint256(keccak256(abi.encode(x, y))))); + } + function testCreateWalletStringPrivAndLabel() public { bytes memory privKey = "this is a priv key"; Cheats.Wallet memory wallet = cheats.createWallet(string(privKey)); + // check wallet.addr against recovered address using private key address expectedAddr = cheats.addr(wallet.privateKey); assertEq(expectedAddr, wallet.addr); + // check wallet.addr against recovered address using x and y coordinates + expectedAddr = addressOf(wallet.publicKeyX, wallet.publicKeyY); + assertEq(expectedAddr, wallet.addr); + string memory label = cheats.getLabel(wallet.addr); assertEq(label, string(privKey), "labelled address != wallet.addr"); } @@ -25,8 +34,13 @@ contract WalletTest is DSTest { Cheats.Wallet memory wallet = cheats.createWallet(uint256(pk)); + // check wallet.addr against recovered address using private key address expectedAddr = cheats.addr(wallet.privateKey); assertEq(expectedAddr, wallet.addr); + + // check wallet.addr against recovered address using x and y coordinates + expectedAddr = addressOf(wallet.publicKeyX, wallet.publicKeyY); + assertEq(expectedAddr, wallet.addr); } function testCreateWalletPrivKeyWithLabel(uint248 pk) public { @@ -35,9 +49,14 @@ contract WalletTest is DSTest { cheats.assume(pk != 0); Cheats.Wallet memory wallet = cheats.createWallet(pk, label); + // check wallet.addr against recovered address using private key address expectedAddr = cheats.addr(wallet.privateKey); assertEq(expectedAddr, wallet.addr); + // check wallet.addr against recovered address using x and y coordinates + expectedAddr = addressOf(wallet.publicKeyX, wallet.publicKeyY); + assertEq(expectedAddr, wallet.addr); + string memory expectedLabel = cheats.getLabel(wallet.addr); assertEq(expectedLabel, label, "labelled address != wallet.addr"); } From a8a423ae5fef0969d87d28a6d85d38d40cd5371d Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Mon, 10 Jul 2023 10:13:22 -0500 Subject: [PATCH 10/13] chore: rename 'cheats' to 'vm' in Wallet.t.sol --- testdata/cheats/Wallet.t.sol | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/testdata/cheats/Wallet.t.sol b/testdata/cheats/Wallet.t.sol index 830d24c244f53..3713a0ca48797 100644 --- a/testdata/cheats/Wallet.t.sol +++ b/testdata/cheats/Wallet.t.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Cheats.sol"; +import "./Vm.sol"; contract Foo {} contract WalletTest is DSTest { - Cheats constant cheats = Cheats(HEVM_ADDRESS); + Vm constant vm = Vm(HEVM_ADDRESS); function addressOf(uint256 x, uint256 y) internal pure returns (address) { return address(uint160(uint256(keccak256(abi.encode(x, y))))); @@ -15,27 +15,27 @@ contract WalletTest is DSTest { function testCreateWalletStringPrivAndLabel() public { bytes memory privKey = "this is a priv key"; - Cheats.Wallet memory wallet = cheats.createWallet(string(privKey)); + Vm.Wallet memory wallet = vm.createWallet(string(privKey)); // check wallet.addr against recovered address using private key - address expectedAddr = cheats.addr(wallet.privateKey); + address expectedAddr = vm.addr(wallet.privateKey); assertEq(expectedAddr, wallet.addr); // check wallet.addr against recovered address using x and y coordinates expectedAddr = addressOf(wallet.publicKeyX, wallet.publicKeyY); assertEq(expectedAddr, wallet.addr); - string memory label = cheats.getLabel(wallet.addr); + string memory label = vm.getLabel(wallet.addr); assertEq(label, string(privKey), "labelled address != wallet.addr"); } function testCreateWalletPrivKeyNoLabel(uint248 pk) public { - cheats.assume(pk != 0); + vm.assume(pk != 0); - Cheats.Wallet memory wallet = cheats.createWallet(uint256(pk)); + Vm.Wallet memory wallet = vm.createWallet(uint256(pk)); // check wallet.addr against recovered address using private key - address expectedAddr = cheats.addr(wallet.privateKey); + address expectedAddr = vm.addr(wallet.privateKey); assertEq(expectedAddr, wallet.addr); // check wallet.addr against recovered address using x and y coordinates @@ -46,26 +46,26 @@ contract WalletTest is DSTest { function testCreateWalletPrivKeyWithLabel(uint248 pk) public { string memory label = "labelled wallet"; - cheats.assume(pk != 0); - Cheats.Wallet memory wallet = cheats.createWallet(pk, label); + vm.assume(pk != 0); + Vm.Wallet memory wallet = vm.createWallet(pk, label); // check wallet.addr against recovered address using private key - address expectedAddr = cheats.addr(wallet.privateKey); + address expectedAddr = vm.addr(wallet.privateKey); assertEq(expectedAddr, wallet.addr); // check wallet.addr against recovered address using x and y coordinates expectedAddr = addressOf(wallet.publicKeyX, wallet.publicKeyY); assertEq(expectedAddr, wallet.addr); - string memory expectedLabel = cheats.getLabel(wallet.addr); + string memory expectedLabel = vm.getLabel(wallet.addr); assertEq(expectedLabel, label, "labelled address != wallet.addr"); } function testSignWithWalletDigest(uint248 pk, bytes32 digest) public { - cheats.assume(pk != 0); - Cheats.Wallet memory wallet = cheats.createWallet(uint256(pk)); + vm.assume(pk != 0); + Vm.Wallet memory wallet = vm.createWallet(uint256(pk)); - (uint8 v, bytes32 r, bytes32 s) = cheats.sign(wallet, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(wallet, digest); address recovered = ecrecover(digest, v, r, s); assertEq(recovered, wallet.addr); @@ -76,17 +76,17 @@ contract WalletTest is DSTest { } function testGetNonceWallet(uint248 pk) public { - cheats.assume(pk != 0); - Cheats.Wallet memory wallet = cheats.createWallet(uint256(pk)); + vm.assume(pk != 0); + Vm.Wallet memory wallet = vm.createWallet(uint256(pk)); - uint64 nonce1 = cheats.getNonce(wallet); + uint64 nonce1 = vm.getNonce(wallet); - cheats.startPrank(wallet.addr); + vm.startPrank(wallet.addr); new Foo(); new Foo(); - cheats.stopPrank(); + vm.stopPrank(); - uint64 nonce2 = cheats.getNonce(wallet); + uint64 nonce2 = vm.getNonce(wallet); assertEq(nonce1 + 2, nonce2); } } From 3f7b4e89caed11e2f93dfdde5a991dd582c10470 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Thu, 10 Aug 2023 08:07:07 -0500 Subject: [PATCH 11/13] test: change pkSeed to uint256 to check whole private key range --- testdata/cheats/Wallet.t.sol | 37 ++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/testdata/cheats/Wallet.t.sol b/testdata/cheats/Wallet.t.sol index 3713a0ca48797..c5a46028c676c 100644 --- a/testdata/cheats/Wallet.t.sol +++ b/testdata/cheats/Wallet.t.sol @@ -9,6 +9,9 @@ contract Foo {} contract WalletTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); + uint internal constant Q = + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + function addressOf(uint256 x, uint256 y) internal pure returns (address) { return address(uint160(uint256(keccak256(abi.encode(x, y))))); } @@ -29,10 +32,11 @@ contract WalletTest is DSTest { assertEq(label, string(privKey), "labelled address != wallet.addr"); } - function testCreateWalletPrivKeyNoLabel(uint248 pk) public { - vm.assume(pk != 0); + function testCreateWalletPrivKeyNoLabel(uint256 pkSeed) public { + vm.assume(pkSeed != 0); + uint256 pk = bound(pkSeed, 1, Q - 1); - Vm.Wallet memory wallet = vm.createWallet(uint256(pk)); + Vm.Wallet memory wallet = vm.createWallet(pk); // check wallet.addr against recovered address using private key address expectedAddr = vm.addr(wallet.privateKey); @@ -43,10 +47,12 @@ contract WalletTest is DSTest { assertEq(expectedAddr, wallet.addr); } - function testCreateWalletPrivKeyWithLabel(uint248 pk) public { + function testCreateWalletPrivKeyWithLabel(uint256 pkSeed) public { string memory label = "labelled wallet"; - vm.assume(pk != 0); + vm.assume(pkSeed != 0); + uint256 pk = bound(pkSeed, 1, Q - 1); + Vm.Wallet memory wallet = vm.createWallet(pk, label); // check wallet.addr against recovered address using private key @@ -61,9 +67,11 @@ contract WalletTest is DSTest { assertEq(expectedLabel, label, "labelled address != wallet.addr"); } - function testSignWithWalletDigest(uint248 pk, bytes32 digest) public { - vm.assume(pk != 0); - Vm.Wallet memory wallet = vm.createWallet(uint256(pk)); + function testSignWithWalletDigest(uint256 pkSeed, bytes32 digest) public { + vm.assume(pkSeed != 0); + uint256 pk = bound(pkSeed, 1, Q - 1); + + Vm.Wallet memory wallet = vm.createWallet(pk); (uint8 v, bytes32 r, bytes32 s) = vm.sign(wallet, digest); @@ -71,13 +79,18 @@ contract WalletTest is DSTest { assertEq(recovered, wallet.addr); } - function testSignWithWalletMessage(uint248 pk, bytes memory message) public { + function testSignWithWalletMessage( + uint256 pkSeed, + bytes memory message + ) public { testSignWithWalletDigest(pk, keccak256(message)); } - function testGetNonceWallet(uint248 pk) public { - vm.assume(pk != 0); - Vm.Wallet memory wallet = vm.createWallet(uint256(pk)); + function testGetNonceWallet(uint256 pk) public { + vm.assume(pkSeed != 0); + uint256 pk = bound(pkSeed, 1, Q - 1); + + Vm.Wallet memory wallet = vm.createWallet(pk); uint64 nonce1 = vm.getNonce(wallet); From f4e105df3104591c626918d4d37dfef9964a774c Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Thu, 10 Aug 2023 10:12:59 -0500 Subject: [PATCH 12/13] Add 'bound' func, remove 'vm.assume(pk != 0)' --- testdata/cheats/Wallet.t.sol | 45 ++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/testdata/cheats/Wallet.t.sol b/testdata/cheats/Wallet.t.sol index c5a46028c676c..cccb874bdb0fa 100644 --- a/testdata/cheats/Wallet.t.sol +++ b/testdata/cheats/Wallet.t.sol @@ -9,13 +9,41 @@ contract Foo {} contract WalletTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); - uint internal constant Q = - 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + uint256 internal constant Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + uint256 private constant UINT256_MAX = + 115792089237316195423570985008687907853269984665640564039457584007913129639935; function addressOf(uint256 x, uint256 y) internal pure returns (address) { return address(uint160(uint256(keccak256(abi.encode(x, y))))); } + function bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) { + require(min <= max, "min needs to be less than max"); + // If x is between min and max, return x directly. This is to ensure that dictionary values + // do not get shifted if the min is nonzero. More info: https://github.com/foundry-rs/forge-std/issues/188 + if (x >= min && x <= max) return x; + + uint256 size = max - min + 1; + + // If the value is 0, 1, 2, 3, wrap that to min, min+1, min+2, min+3. Similarly for the UINT256_MAX side. + // This helps ensure coverage of the min/max values. + if (x <= 3 && size > x) return min + x; + if (x >= UINT256_MAX - 3 && size > UINT256_MAX - x) return max - (UINT256_MAX - x); + + // Otherwise, wrap x into the range [min, max], i.e. the range is inclusive. + if (x > max) { + uint256 diff = x - max; + uint256 rem = diff % size; + if (rem == 0) return max; + result = min + rem - 1; + } else if (x < min) { + uint256 diff = min - x; + uint256 rem = diff % size; + if (rem == 0) return min; + result = max - rem + 1; + } + } + function testCreateWalletStringPrivAndLabel() public { bytes memory privKey = "this is a priv key"; Vm.Wallet memory wallet = vm.createWallet(string(privKey)); @@ -33,7 +61,6 @@ contract WalletTest is DSTest { } function testCreateWalletPrivKeyNoLabel(uint256 pkSeed) public { - vm.assume(pkSeed != 0); uint256 pk = bound(pkSeed, 1, Q - 1); Vm.Wallet memory wallet = vm.createWallet(pk); @@ -50,7 +77,6 @@ contract WalletTest is DSTest { function testCreateWalletPrivKeyWithLabel(uint256 pkSeed) public { string memory label = "labelled wallet"; - vm.assume(pkSeed != 0); uint256 pk = bound(pkSeed, 1, Q - 1); Vm.Wallet memory wallet = vm.createWallet(pk, label); @@ -68,7 +94,6 @@ contract WalletTest is DSTest { } function testSignWithWalletDigest(uint256 pkSeed, bytes32 digest) public { - vm.assume(pkSeed != 0); uint256 pk = bound(pkSeed, 1, Q - 1); Vm.Wallet memory wallet = vm.createWallet(pk); @@ -79,15 +104,11 @@ contract WalletTest is DSTest { assertEq(recovered, wallet.addr); } - function testSignWithWalletMessage( - uint256 pkSeed, - bytes memory message - ) public { - testSignWithWalletDigest(pk, keccak256(message)); + function testSignWithWalletMessage(uint256 pkSeed, bytes memory message) public { + testSignWithWalletDigest(pkSeed, keccak256(message)); } - function testGetNonceWallet(uint256 pk) public { - vm.assume(pkSeed != 0); + function testGetNonceWallet(uint256 pkSeed) public { uint256 pk = bound(pkSeed, 1, Q - 1); Vm.Wallet memory wallet = vm.createWallet(pk); From 7cc1d3663b2454f90ecefb4f0a729a616f87d584 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Fri, 11 Aug 2023 07:50:25 -0500 Subject: [PATCH 13/13] docs: add doc comments surrounding 'create_wallet' --- evm/src/executor/inspector/cheatcodes/env.rs | 2 ++ evm/src/executor/inspector/cheatcodes/util.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/evm/src/executor/inspector/cheatcodes/env.rs b/evm/src/executor/inspector/cheatcodes/env.rs index e385a933ccab6..35d24ce79ade0 100644 --- a/evm/src/executor/inspector/cheatcodes/env.rs +++ b/evm/src/executor/inspector/cheatcodes/env.rs @@ -507,6 +507,7 @@ pub fn apply( Ok(Bytes::new()) }, )??, + // [function getNonce(address)] returns the current nonce of a given ETH address HEVMCalls::GetNonce1(inner) => { correct_sender_nonce( b160_to_h160(data.env.tx.caller), @@ -523,6 +524,7 @@ pub fn apply( let account = data.journaled_state.state().get(&h160_to_b160(inner.0)).unwrap(); abi::encode(&[Token::Uint(account.info.nonce.into())]).into() } + // [function getNonce(Wallet)] returns the current nonce of the Wallet's ETH address HEVMCalls::GetNonce0(inner) => { correct_sender_nonce( b160_to_h160(data.env.tx.caller), diff --git a/evm/src/executor/inspector/cheatcodes/util.rs b/evm/src/executor/inspector/cheatcodes/util.rs index 8cb0489c12416..b3863c0a55c5d 100644 --- a/evm/src/executor/inspector/cheatcodes/util.rs +++ b/evm/src/executor/inspector/cheatcodes/util.rs @@ -124,6 +124,10 @@ fn sign(private_key: U256, digest: H256, chain_id: U256) -> Result { Ok((sig.v, r_bytes, s_bytes).encode().into()) } +/// Using a given private key, return its public ETH address, its public key affine x and y +/// coodinates, and its private key (see the 'Wallet' struct) +/// +/// If 'label' is set to 'Some()', assign that label to the associated ETH address in state fn create_wallet(private_key: U256, label: Option, state: &mut Cheatcodes) -> Result { let key = parse_private_key(private_key)?; let addr = utils::secret_key_to_address(&key); @@ -244,12 +248,20 @@ pub fn apply( ) -> Option { Some(match call { HEVMCalls::Addr(inner) => addr(inner.0), + // [function sign(uint256,bytes32)] Used to sign bytes32 digests using the given private key HEVMCalls::Sign0(inner) => sign(inner.0, inner.1.into(), data.env.cfg.chain_id.into()), + // [function createWallet(string)] Used to derive private key and label the wallet with the + // same string HEVMCalls::CreateWallet0(inner) => { create_wallet(U256::from(keccak256(&inner.0)), Some(inner.0.clone()), state) } + // [function createWallet(uint256)] creates a new wallet with the given private key HEVMCalls::CreateWallet1(inner) => create_wallet(inner.0, None, state), + // [function createWallet(uint256,string)] creates a new wallet with the given private key + // and labels it with the given string HEVMCalls::CreateWallet2(inner) => create_wallet(inner.0, Some(inner.1.clone()), state), + // [function sign(uint256,bytes32)] Used to sign bytes32 digests using the given Wallet's + // private key HEVMCalls::Sign1(inner) => { sign(inner.0.private_key, inner.1.into(), data.env.cfg.chain_id.into()) }