ethers-rs/examples/call_override.rs

71 lines
2.6 KiB
Rust

use ethers::{
prelude::*,
providers::call_raw::RawCall,
utils::{parse_ether, Geth},
};
use std::sync::Arc;
abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",);
#[tokio::main]
async fn main() -> eyre::Result<()> {
let geth = Geth::new().spawn();
let provider = Provider::<Http>::try_from(geth.endpoint()).unwrap();
let client = Arc::new(provider);
// Both empty accounts
let target: Address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c".parse()?;
let from: Address = "0x295a70b2de5e3953354a6a8344e616ed314d7251".parse()?;
// Override the sender's balance for the call
let pay_amt = parse_ether(1u64)?;
let tx = TransactionRequest::pay(target, pay_amt).from(from);
let state = call_raw::balance(from, pay_amt * 2);
// The call succeeds as if the sender had sufficient balance
client.call_raw(&tx.into()).state(&state).await.expect("balance override");
// Get the runtime bytecode for the Greeter contract using eth_call to
// simulate the deploy transaction
let deploy = Greeter::deploy(client.clone(), "Hi".to_string())?;
let runtime_bytecode = deploy.call_raw().await?;
// Instantiate a Greeter, though no bytecode exists at the target address
let greeter = Greeter::new(target, client.clone());
// Override the target account's code, simulating a call to Greeter.greet()
// as if the Greeter contract was deployed at the target address
let state = call_raw::code(target, runtime_bytecode.clone());
let res = greeter.greet().call_raw().state(&state).await?;
// greet() returns the empty string, because the target account's storage is empty
assert_eq!(&res, "");
// Encode the greeting string as solidity expects it to be stored
let greeting = "Hello, world";
let greet_slot = H256::zero();
let greet_val = encode_string_for_storage(greeting);
// Override the target account's code and storage
let mut state = call_raw::state();
state.account(target).code(runtime_bytecode.clone()).store(greet_slot, greet_val);
let res = greeter.greet().call_raw().state(&state).await?;
// The call returns "Hello, world"
assert_eq!(&res, greeting);
Ok(())
}
// Solidity stores strings shorter than 32 bytes in a single storage slot,
// with the lowest order byte storing 2 * length and the higher order
// bytes storing the string data (left aligned)
fn encode_string_for_storage(s: &str) -> H256 {
let mut bytes = s.as_bytes().to_vec();
let len = bytes.len();
assert!(len < 32, "longer strings aren't stored in a single slot");
bytes.resize(31, 0);
bytes.push(len as u8 * 2);
H256::from_slice(&bytes)
}