diff --git a/Cargo.lock b/Cargo.lock index 4f55e94355f1b..8e455700c75c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -752,6 +752,7 @@ dependencies = [ "regex", "rpassword", "rustc-hex", + "semver 1.0.4", "serde_json", "seth", "structopt", @@ -996,7 +997,7 @@ dependencies = [ [[package]] name = "ethers" version = "0.5.4" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "ethers-contract", "ethers-core", @@ -1009,7 +1010,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "0.5.3" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -1027,7 +1028,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "0.5.3" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "Inflector", "anyhow", @@ -1048,7 +1049,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "0.5.3" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -1062,7 +1063,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "0.5.5" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "arrayvec", "bytes", @@ -1090,7 +1091,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "0.1.1" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "ethers-core", "reqwest", @@ -1102,7 +1103,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "0.5.3" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "async-trait", "ethers-contract", @@ -1125,7 +1126,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "0.5.4" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "async-trait", "auto_impl", @@ -1154,7 +1155,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "0.5.3" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "async-trait", "coins-bip32", @@ -1174,7 +1175,7 @@ dependencies = [ [[package]] name = "ethers-solc" version = "0.1.0" -source = "git+https://github.com/gakonst/ethers-rs#8eac1997f4cc09ff4343f94e492aa51f23e51e1f" +source = "git+https://github.com/gakonst/ethers-rs#8a3ee415b73805e0d66c7ba39efca75d5debd91b" dependencies = [ "colored", "ethers-core", diff --git a/README.md b/README.md index ff46447e89ad8..5f7a62c697c35 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ Benchmarks TBD in the future, but: - [ ] `run-tx` - [x] `send` (partial) - [ ] `sign` - - [ ] `storage` + - [x] `storage` - [ ] `tx` - dapp - [ ] test @@ -137,13 +137,16 @@ Benchmarks TBD in the future, but: - [ ] Symbolic execution - [ ] Coverage - [ ] HEVM-style Solidity cheatcodes - - [x] roll - - [x] warp - - [x] ffi - - [x] store - - [x] load - - [ ] sign - - [ ] addr + - [x] roll: Sets block.number + - [x] warp: Sets block.timestamp + - [x] ffi: Perform foreign function call to terminal + - [x] store: Sets address storage slot + - [x] load: Loads address storage slot + - [x] deal: Sets account balance + - [x] prank: Performs a call as another address (changes msg.sender for a call) + - [x] sign: Signs data + - [x] addr: Gets address for a private key + - [x] etch: Sets the code of an address - [ ] makeEOA - ...? - [ ] Structured tracing with abi decoding diff --git a/dapptools/Cargo.toml b/dapptools/Cargo.toml index 26fe655b352a0..0c8cb5715926a 100644 --- a/dapptools/Cargo.toml +++ b/dapptools/Cargo.toml @@ -32,6 +32,7 @@ evmodin = { git = "https://github.com/vorot93/evmodin", optional = true } proptest = "1.0.0" git2 = "0.13.22" glob = "0.3.0" +semver = "1.0.4" [dev-dependencies] tempdir = "0.3.7" diff --git a/dapptools/src/dapp_opts.rs b/dapptools/src/dapp_opts.rs index 589163a967098..9a38a648bdf8d 100644 --- a/dapptools/src/dapp_opts.rs +++ b/dapptools/src/dapp_opts.rs @@ -218,7 +218,13 @@ impl std::convert::TryFrom<&BuildOpts> for Project { let paths = paths_builder.build()?; // build the project w/ allowed paths = root and all the libs - let project = Project::builder().paths(paths).allowed_path(root).build()?; + let mut builder = Project::builder().paths(paths).allowed_path(root); + + if opts.no_auto_detect { + builder = builder.no_auto_detect(); + } + + let project = builder.build()?; Ok(project) } @@ -251,6 +257,12 @@ pub struct BuildOpts { #[structopt(help = "choose the evm version", long, default_value = "london")] pub evm_version: EvmVersion, + + #[structopt( + help = "if set to true, skips auto-detecting solc and uses what is in the user's $PATH ", + long + )] + pub no_auto_detect: bool, } #[derive(Clone, Debug)] pub enum EvmType { diff --git a/dapptools/src/seth.rs b/dapptools/src/seth.rs index 7f18629ea5764..9205df927496a 100644 --- a/dapptools/src/seth.rs +++ b/dapptools/src/seth.rs @@ -18,14 +18,16 @@ async fn main() -> eyre::Result<()> { let opts = Opts::from_args(); match opts.sub { Subcommands::FromUtf8 { text } => { - println!("{}", SimpleSeth::from_utf8(&text)); + let val = unwrap_or_stdin(text)?; + println!("{}", SimpleSeth::from_utf8(&val)); } Subcommands::ToHex { decimal } => { let val = unwrap_or_stdin(decimal)?; println!("{}", SimpleSeth::hex(U256::from_dec_str(&val)?)); } Subcommands::ToHexdata { input } => { - let output = match input { + let val = unwrap_or_stdin(input)?; + let output = match val { s if s.starts_with('@') => { let var = std::env::var(&s[1..])?; var.as_bytes().to_hex() @@ -45,16 +47,20 @@ async fn main() -> eyre::Result<()> { println!("0x{}", output); } Subcommands::ToCheckSumAddress { address } => { - println!("{}", SimpleSeth::checksum_address(&address)?); + let val = unwrap_or_stdin(address)?; + println!("{}", SimpleSeth::checksum_address(&val)?); } Subcommands::ToAscii { hexdata } => { - println!("{}", SimpleSeth::ascii(&hexdata)?); + let val = unwrap_or_stdin(hexdata)?; + println!("{}", SimpleSeth::ascii(&val)?); } Subcommands::ToBytes32 { bytes } => { - println!("{}", SimpleSeth::bytes32(&bytes)?); + let val = unwrap_or_stdin(bytes)?; + println!("{}", SimpleSeth::bytes32(&val)?); } Subcommands::ToDec { hexvalue } => { - println!("{}", SimpleSeth::to_dec(&hexvalue)?); + let val = unwrap_or_stdin(hexvalue)?; + println!("{}", SimpleSeth::to_dec(&val)?); } Subcommands::ToFix { decimals, value } => { let val = unwrap_or_stdin(value)?; @@ -64,7 +70,8 @@ async fn main() -> eyre::Result<()> { ); } Subcommands::ToUint256 { value } => { - println!("{}", SimpleSeth::to_uint256(value)?); + let val = unwrap_or_stdin(value)?; + println!("{}", SimpleSeth::to_uint256(&val)?); } Subcommands::ToWei { value, unit } => { let val = unwrap_or_stdin(value)?; @@ -166,6 +173,11 @@ async fn main() -> eyre::Result<()> { } println!("{}", name); } + Subcommands::Storage { address, slot, rpc_url, block } => { + let provider = Provider::try_from(rpc_url)?; + let value = provider.get_storage_at(address, slot, block).await?; + println!("{:?}", value); + } }; Ok(()) @@ -179,10 +191,9 @@ where Ok(match what { Some(what) => what, None => { - use std::io::Read; - let mut input = std::io::stdin(); + let input = std::io::stdin(); let mut what = String::new(); - input.read_to_string(&mut what)?; + input.read_line(&mut what)?; T::from_str(&what.replace("\n", ""))? } }) diff --git a/dapptools/src/seth_opts.rs b/dapptools/src/seth_opts.rs index 804f1f154e89a..9787a3f20febf 100644 --- a/dapptools/src/seth_opts.rs +++ b/dapptools/src/seth_opts.rs @@ -1,10 +1,11 @@ +use std::{convert::TryFrom, str::FromStr, sync::Arc}; + use ethers::{ providers::{Http, Provider}, signers::{coins_bip39::English, LocalWallet, MnemonicBuilder}, types::{Address, BlockId, BlockNumber, NameOrAddress, H256, U64}, }; use eyre::Result; -use std::{convert::TryFrom, str::FromStr}; use structopt::StructOpt; #[derive(Debug, StructOpt)] @@ -13,7 +14,7 @@ pub enum Subcommands { #[structopt(aliases = &["--from-ascii"])] #[structopt(name = "--from-utf8")] #[structopt(about = "convert text data into hexdata")] - FromUtf8 { text: String }, + FromUtf8 { text: Option }, #[structopt(name = "--to-hex")] #[structopt(about = "convert a decimal number into hex")] ToHex { decimal: Option }, @@ -26,25 +27,26 @@ pub enum Subcommands { - absolute path to file - @tag, where $TAG is defined in environment variables "#)] - ToHexdata { input: String }, + ToHexdata { input: Option }, + #[structopt(aliases = &["--to-checksum"])] // Compatibility with dapptools' seth #[structopt(name = "--to-checksum-address")] #[structopt(about = "convert an address to a checksummed format (EIP-55)")] - ToCheckSumAddress { address: Address }, + ToCheckSumAddress { address: Option
}, #[structopt(name = "--to-ascii")] #[structopt(about = "convert hex data to text data")] - ToAscii { hexdata: String }, + ToAscii { hexdata: Option }, #[structopt(name = "--to-bytes32")] #[structopt(about = "left-pads a hex bytes string to 32 bytes)")] - ToBytes32 { bytes: String }, + ToBytes32 { bytes: Option }, #[structopt(name = "--to-dec")] #[structopt(about = "convert hex value into decimal number")] - ToDec { hexvalue: String }, + ToDec { hexvalue: Option }, #[structopt(name = "--to-fix")] #[structopt(about = "convert integers into fixed point with specified decimals")] ToFix { decimals: Option, value: Option }, #[structopt(name = "--to-uint256")] #[structopt(about = "convert a number into uint256 hex string with 0x prefix")] - ToUint256 { value: String }, + ToUint256 { value: Option }, #[structopt(name = "--to-wei")] #[structopt(about = "convert an ETH amount into wei")] ToWei { value: Option, unit: Option }, @@ -180,6 +182,22 @@ pub enum Subcommands { )] verify: bool, }, + #[structopt(name = "storage", about = "Show the raw value of a contract's storage slot")] + Storage { + #[structopt(help = "the contract address", parse(try_from_str = parse_name_or_address))] + address: NameOrAddress, + #[structopt(help = "the storage slot number (hex or number)", parse(try_from_str = parse_slot))] + slot: H256, + #[structopt(short, long, env = "ETH_RPC_URL")] + rpc_url: String, + #[structopt( + long, + short, + help = "the block you want to query, can also be earliest/latest/pending", + parse(try_from_str = parse_block_id) + )] + block: Option, + }, } fn parse_name_or_address(s: &str) -> eyre::Result { @@ -199,6 +217,15 @@ fn parse_block_id(s: &str) -> eyre::Result { }) } +fn parse_slot(s: &str) -> eyre::Result { + Ok(if s.starts_with("0x") { + let padded = format!("{:0>64}", s.strip_prefix("0x").unwrap()); + H256::from_str(&padded)? + } else { + H256::from_low_u64_be(u64::from_str(s)?) + }) +} + #[derive(Debug, StructOpt)] pub struct Opts { #[structopt(subcommand)] @@ -226,7 +253,6 @@ pub struct EthereumOpts { } // TODO: Improve these so that we return a middleware trait object -use std::sync::Arc; impl EthereumOpts { #[allow(unused)] pub fn provider(&self) -> eyre::Result>> { diff --git a/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs b/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs index a798305f019ea..87ded9be0a40c 100644 --- a/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs +++ b/evm-adapters/src/sputnik/cheatcodes/cheatcode_handler.rs @@ -232,12 +232,16 @@ fn evm_error(retdata: &str) -> Capture<(ExitReason, Vec), Infallible> { impl<'a, 'b, B: Backend, P: PrecompileSet> CheatcodeStackExecutor<'a, 'b, B, P> { /// Decodes the provided calldata as a - fn apply_cheatcode(&mut self, input: Vec) -> Capture<(ExitReason, Vec), Infallible> { + fn apply_cheatcode( + &mut self, + input: Vec, + transfer: Option, + target_gas: Option, + ) -> Capture<(ExitReason, Vec), Infallible> { let mut res = vec![]; // Get a mutable ref to the state so we can apply the cheats let state = self.state_mut(); - let decoded = match HEVMCalls::decode(&input) { Ok(inner) => inner, Err(err) => return evm_error(&err.to_string()), @@ -264,7 +268,7 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> CheatcodeStackExecutor<'a, 'b, B, P> if !self.enable_ffi { return evm_error( "ffi disabled: run again with --ffi if you want to allow tests to call external scripts", - ) + ); } // execute the command & get the stdout @@ -330,10 +334,51 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> CheatcodeStackExecutor<'a, 'b, B, P> Token::FixedBytes(s_bytes.to_vec()), ])]); } + HEVMCalls::Prank(inner) => { + let caller = inner.0; + let address = inner.1; + let input = inner.2; + + let value = if let Some(ref transfer) = transfer { + transfer.value + } else { + U256::zero() + }; + + // change origin + let context = Context { caller, address, apparent_value: value }; + let ret = self.call( + address, + Some(Transfer { source: caller, target: address, value }), + input, + target_gas, + false, + context, + ); + res = match ret { + Capture::Exit((successful, v)) => match successful { + ExitReason::Succeed(_) => { + ethers::abi::encode(&[Token::Bool(true), Token::Bytes(v.to_vec())]) + } + _ => ethers::abi::encode(&[Token::Bool(false), Token::Bytes(v.to_vec())]), + }, + _ => vec![], + }; + } + HEVMCalls::Deal(inner) => { + let who = inner.0; + let value = inner.1; + state.reset_balance(who); + state.deposit(who, value); + } + HEVMCalls::Etch(inner) => { + let who = inner.0; + let code = inner.1; + state.set_code(who, code); + } }; // TODO: Add more cheat codes. - Capture::Exit((ExitReason::Succeed(ExitSucceed::Stopped), res)) } @@ -459,7 +504,6 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> CheatcodeStackExecutor<'a, 'b, B, P> let reason = self.execute(&mut runtime); // // log::debug!(target: "evm", "Call execution using address {}: {:?}", code_address, // reason); - match reason { ExitReason::Succeed(s) => { let _ = self.handler.exit_substate(StackExitKind::Succeeded); @@ -660,7 +704,7 @@ impl<'a, 'b, B: Backend, P: PrecompileSet> Handler for CheatcodeStackExecutor<'a // NB: This is very similar to how Optimism's custom intercept logic to "predeploys" work // (e.g. with the StateManager) if code_address == *CHEATCODE_ADDRESS { - self.apply_cheatcode(input) + self.apply_cheatcode(input, transfer, target_gas) } else { self.handler.call(code_address, transfer, input, target_gas, is_static, context) } diff --git a/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs b/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs index 93a4779f011ed..c285d43e62cf8 100644 --- a/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs +++ b/evm-adapters/src/sputnik/cheatcodes/memory_stackstate_owned.rs @@ -17,6 +17,12 @@ pub struct MemoryStackStateOwned<'config, B> { pub substate: MemoryStackSubstate<'config>, } +impl<'config, B: Backend> MemoryStackStateOwned<'config, B> { + pub fn deposit(&mut self, address: H160, value: U256) { + self.substate.deposit(address, value, &self.backend); + } +} + impl<'config, B: Backend> MemoryStackStateOwned<'config, B> { pub fn new(metadata: StackSubstateMetadata<'config>, backend: B) -> Self { Self { backend, substate: MemoryStackSubstate::new(metadata) } diff --git a/evm-adapters/src/sputnik/cheatcodes/mod.rs b/evm-adapters/src/sputnik/cheatcodes/mod.rs index c47c7e5f4f791..4b5a372da2db2 100644 --- a/evm-adapters/src/sputnik/cheatcodes/mod.rs +++ b/evm-adapters/src/sputnik/cheatcodes/mod.rs @@ -42,6 +42,9 @@ ethers::contract::abigen!( ffi(string[])(bytes) addr(uint256)(address) sign(uint256,bytes32)(uint8,bytes32,bytes32) + prank(address,address,bytes)(bool,bytes) + deal(address,uint256) + etch(address,bytes) ]"#, ); pub use hevm_mod::HEVMCalls; diff --git a/evm-adapters/testdata/CheatCodes.sol b/evm-adapters/testdata/CheatCodes.sol index 458cf8830682b..b3b022d5ebd65 100644 --- a/evm-adapters/testdata/CheatCodes.sol +++ b/evm-adapters/testdata/CheatCodes.sol @@ -6,13 +6,26 @@ pragma experimental ABIEncoderV2; import "./DsTest.sol"; interface Hevm { + // Set block.timestamp (newTimestamp) function warp(uint256) external; + // Set block.height (newHeight) function roll(uint256) external; + // Loads a storage slot from an address (who, slot) function load(address,bytes32) external returns (bytes32); + // Stores a value to an address' storage slot, (who, slot, value) function store(address,bytes32,bytes32) external; + // Signs data, (privateKey, digest) => (r, v, s) function sign(uint256,bytes32) external returns (uint8,bytes32,bytes32); + // Gets address for a given private key, (privateKey) => (address) function addr(uint256) external returns (address); + // Performs a foreign function call via terminal, (stringInputs) => (result) function ffi(string[] calldata) external returns (bytes memory); + // Calls another contract with a specified `msg.sender`, (newSender, contract, input) => (success, returnData) + function prank(address, address, bytes calldata) external payable returns (bool, bytes memory); + // Sets an address' balance, (who, newBalance) + function deal(address, uint256) external; + // Sets an address' code, (who, newCode) + function etch(address, bytes calldata) external; } contract HasStorage { @@ -130,4 +143,95 @@ contract CheatCodes is DSTest { (string memory output) = abi.decode(res, (string)); assertEq(output, "acab"); } + + function testDeal() public { + address addr = address(1337); + hevm.deal(addr, 1337); + assertEq(addr.balance, 1337); + } + + function testPrank() public { + Prank prank = new Prank(); + address new_sender = address(1337); + bytes4 sig = prank.checksOriginAndSender.selector; + string memory input = "And his name is JOHN CENA!"; + bytes memory calld = abi.encodePacked(sig, abi.encode(input)); + address origin = tx.origin; + address sender = msg.sender; + (bool success, bytes memory ret) = hevm.prank(new_sender, address(prank), calld); + assertTrue(success); + string memory expectedRetString = "SUPER SLAM!"; + string memory actualRet = abi.decode(ret, (string)); + assertEq(actualRet, expectedRetString); + + // make sure we returned back to normal + assertEq(origin, tx.origin); + assertEq(sender, msg.sender); + } + + function testPrankValue() public { + Prank prank = new Prank(); + // setup the call + address new_sender = address(1337); + bytes4 sig = prank.checksOriginAndSender.selector; + string memory input = "And his name is JOHN CENA!"; + bytes memory calld = abi.encodePacked(sig, abi.encode(input)); + address origin = tx.origin; + address sender = msg.sender; + + // give the sender some monies + hevm.deal(new_sender, 1337); + + // call the function passing in a value. the eth is pulled from the new sender + sig = hevm.prank.selector; + calld = abi.encodePacked(sig, abi.encode(new_sender, address(prank), calld)); + + // this is nested low level calls effectively + (bool high_level_success, bytes memory outerRet) = address(hevm).call{value: 1}(calld); + assertTrue(high_level_success); + (bool success, bytes memory ret) = abi.decode(outerRet, (bool,bytes)); + assertTrue(success); + string memory expectedRetString = "SUPER SLAM!"; + string memory actualRet = abi.decode(ret, (string)); + assertEq(actualRet, expectedRetString); + + // make sure we returned back to normal + assertEq(origin, tx.origin); + assertEq(sender, msg.sender); + } + + function testEtch() public { + address rewriteCode = address(1337); + + bytes memory newCode = hex"1337"; + hevm.etch(rewriteCode, newCode); + bytes memory n_code = getCode(rewriteCode); + assertEq(string(newCode), string(n_code)); + } + + function getCode(address who) internal returns (bytes memory o_code) { + assembly { + // retrieve the size of the code, this needs assembly + let size := extcodesize(who) + // allocate output byte array - this could also be done without assembly + // by using o_code = new bytes(size) + o_code := mload(0x40) + // new "memory end" including padding + mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + // store length in memory + mstore(o_code, size) + // actually retrieve the code, this needs assembly + extcodecopy(who, add(o_code, 0x20), 0, size) + } + } +} + +contract Prank is DSTest { + function checksOriginAndSender(string calldata input) external payable returns (string memory) { + string memory expectedInput = "And his name is JOHN CENA!"; + assertEq(input, expectedInput); + assertEq(address(1337), msg.sender); + string memory expectedRetString = "SUPER SLAM!"; + return expectedRetString; + } } diff --git a/seth/src/lib.rs b/seth/src/lib.rs index 5ecf40a306a3d..8c5b69b5172f0 100644 --- a/seth/src/lib.rs +++ b/seth/src/lib.rs @@ -385,18 +385,18 @@ impl SimpleSeth { /// use seth::SimpleSeth as Seth; /// /// fn main() -> eyre::Result<()> { - /// assert_eq!(Seth::to_uint256("100".to_string())?, "0x0000000000000000000000000000000000000000000000000000000000000064"); - /// assert_eq!(Seth::to_uint256("192038293923".to_string())?, "0x0000000000000000000000000000000000000000000000000000002cb65fd1a3"); + /// assert_eq!(Seth::to_uint256("100")?, "0x0000000000000000000000000000000000000000000000000000000000000064"); + /// assert_eq!(Seth::to_uint256("192038293923")?, "0x0000000000000000000000000000000000000000000000000000002cb65fd1a3"); /// assert_eq!( - /// Seth::to_uint256("115792089237316195423570985008687907853269984665640564039457584007913129639935".to_string())?, + /// Seth::to_uint256("115792089237316195423570985008687907853269984665640564039457584007913129639935")?, /// "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" /// ); /// /// Ok(()) /// } /// ``` - pub fn to_uint256(value: String) -> Result { - let num_u256 = U256::from_str_radix(&value, 10)?; + pub fn to_uint256(value: &str) -> Result { + let num_u256 = U256::from_str_radix(value, 10)?; let num_hex = format!("{:x}", num_u256); Ok(format!("0x{}{}", "0".repeat(64 - num_hex.len()), num_hex)) } @@ -466,7 +466,7 @@ impl SimpleSeth { eyre::bail!("string >32 bytes"); } - let padded = format!("0x{:0<64}", s); + let padded = format!("{:0<64}", s); // need to use the Debug implementation Ok(format!("{:?}", H256::from_str(&padded)?)) }