-
Notifications
You must be signed in to change notification settings - Fork 764
/
call_override.rs
102 lines (86 loc) · 3.33 KB
/
call_override.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use ethers::{
contract::abigen,
core::{
types::{Address, TransactionRequest, H256},
utils::{parse_ether, Geth},
},
providers::{
call_raw::{self, RawCall},
Http, Provider,
},
};
use eyre::Result;
use std::{
process::{Command, Stdio},
sync::Arc,
};
abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",);
#[tokio::main]
async fn main() -> Result<()> {
match geth_version() {
e @ Ok(false) | e @ Err(_) => {
eprint!("Error spawning geth, skipping example");
if let Err(e) = e {
eprint!(": {e}");
}
eprintln!();
return Ok(())
}
Ok(true) => {}
}
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)
}
fn geth_version() -> Result<bool> {
Command::new("geth")
.arg("version")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.map_err(Into::into)
}