Skip to content

Commit

Permalink
Merge pull request #20 from danhper/anvil
Browse files Browse the repository at this point in the history
Add support for forking with anvil
  • Loading branch information
danhper authored Sep 8, 2024
2 parents d032868 + f450de3 commit 4d172e5
Show file tree
Hide file tree
Showing 12 changed files with 372 additions and 27 deletions.
32 changes: 32 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ foundry-config = { git = "https://github.com/foundry-rs/foundry", tag = "nightly
chisel = { git = "https://github.com/foundry-rs/foundry", tag = "nightly-5ac78a9cd4b94dc53d1fe5e0f42372b28b5a7559" }
url = "2.5.1"
futures = "0.3.30"
tokio = "1.38.0"
tokio = { version = "1.38.0", features = ["rt-multi-thread"] }
alloy = { version = "0.2.0", features = [
"full",
"signer-ledger",
"signer-keystore",
"node-bindings",
"provider-anvil-api",
] }
itertools = "0.13.0"
rpassword = "7.3.1"
Expand Down
41 changes: 40 additions & 1 deletion docs/src/builtin_values.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,44 @@ If the [RPC URL](./configuration.md#rpc-url) is set in the configuration file, t
>> repl.rpc("optimism")
```

### `repl.fork() | repl.fork(string url) -> string`

This creates a fork using Anvil.
If a URL is provided, it will fork that network, otherwise it will use the current RPC (`repl.rpc()`).
This returns the endpoint of the Anvil instance.

```javascript
>> repl.fork()
"http://localhost:54383/"
```

### `repl.startPrank(address account) -> address`

Starts a prank on the given account.
This only works if connected to an RPC that supports `anvil_impersonateAccount` (which is the case after calling `repl.fork()`).

```javascript
>> repl.startPrank(0xCdaa941eB36344c54139CB9d6337Bd2154BBeEfA)
0xCdaa941eB36344c54139CB9d6337Bd2154BBeEfA
```

### `repl.stopPrank()`

Stops the current prank.

```javascript
>> repl.stopPrank()
```

### `repl.deal(address account, uint256 balance)`

Sets the ETH balance of `account` to `balance`.
This only works if connected to an RPC that supports `anvil_setBalance` (which is the case after calling `repl.fork()`).

```javascript
>> repl.deal(0xCdaa941eB36344c54139CB9d6337Bd2154BBeEfA, 1e18)
```

### `repl.block() -> uint256 | string`

Returns the current block in use for contract calls.
Expand All @@ -96,8 +134,9 @@ Returns the current block in use for contract calls.
"latest"
```

### `repl.block(uint256 number) | repl.block(string tag) | repl.block(bytes32 hash)`
NOTE: this is different from `block.number` which returns the current block number of the chain.

### `repl.block(uint256 number) | repl.block(string tag) | repl.block(bytes32 hash)`

Sets the block to use for contract calls.
Can be a number, a tag (e.g. "latest" or "safe"), or a block hash.
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/builtins/address.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;

use alloy::transports::BoxFuture;
use alloy::{providers::Provider, transports::BoxFuture};
use anyhow::Result;
use futures::FutureExt;
use lazy_static::lazy_static;
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/builtins/block.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;

use alloy::{eips::BlockId, rpc::types::BlockTransactionsKind};
use alloy::{eips::BlockId, providers::Provider, rpc::types::BlockTransactionsKind};
use anyhow::{anyhow, Ok, Result};
use futures::{future::BoxFuture, FutureExt};
use lazy_static::lazy_static;
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/builtins/events.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;

use alloy::{primitives::B256, rpc::types::Filter};
use alloy::{primitives::B256, providers::Provider, rpc::types::Filter};
use anyhow::{bail, Result};
use futures::{future::BoxFuture, FutureExt};
use lazy_static::lazy_static;
Expand Down
4 changes: 4 additions & 0 deletions src/interpreter/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ lazy_static! {
repl_methods.insert("types".to_string(), repl::REPL_LIST_TYPES.clone());
repl_methods.insert("connected".to_string(), repl::REPL_IS_CONNECTED.clone());
repl_methods.insert("rpc".to_string(), repl::REPL_RPC.clone());
repl_methods.insert("fork".to_string(), repl::REPL_FORK.clone());
repl_methods.insert("debug".to_string(), repl::REPL_DEBUG.clone());
repl_methods.insert("block".to_string(), repl::REPL_BLOCK.clone());
repl_methods.insert("exec".to_string(), repl::REPL_EXEC.clone());
Expand All @@ -165,6 +166,9 @@ lazy_static! {
repl::REPL_LIST_LEDGER_WALLETS.clone(),
);
repl_methods.insert("loadLedger".to_string(), repl::REPL_LOAD_LEDGER.clone());
repl_methods.insert("startPrank".to_string(), repl::REPL_START_PRANK.clone());
repl_methods.insert("stopPrank".to_string(), repl::REPL_STOP_PRANK.clone());
repl_methods.insert("deal".to_string(), repl::REPL_DEAL.clone());
m.insert(NonParametricType::Repl, repl_methods);

m
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/builtins/receipt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;

use alloy::providers::PendingTransactionBuilder;
use alloy::providers::{PendingTransactionBuilder, Provider};
use anyhow::{bail, Result};
use futures::{future::BoxFuture, FutureExt};
use lazy_static::lazy_static;
Expand Down
78 changes: 77 additions & 1 deletion src/interpreter/builtins/repl.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{process::Command, sync::Arc};

use alloy::{
providers::Provider,
providers::{ext::AnvilApi, Provider},
signers::local::{LocalSigner, PrivateKeySigner},
};
use anyhow::{anyhow, bail, Ok, Result};
Expand Down Expand Up @@ -55,6 +55,16 @@ fn rpc(env: &mut Env, _receiver: &Value, args: &[Value]) -> Result<Value> {
}
}

fn fork(env: &mut Env, _receiver: &Value, args: &[Value]) -> Result<Value> {
let url = match args {
[Value::Str(url)] => url.clone(),
[] => env.get_rpc_url(),
_ => bail!("fork: invalid arguments"),
};
env.fork(&url)?;
Ok(Value::Str(env.get_rpc_url()))
}

fn debug(env: &mut Env, _receiver: &Value, args: &[Value]) -> Result<Value> {
match args {
[] => Ok(Value::Bool(env.is_debug())),
Expand Down Expand Up @@ -183,6 +193,52 @@ fn list_ledgers<'a>(
.boxed()
}

fn impersonate<'a>(
env: &'a mut Env,
_receiver: &'a Value,
args: &'a [Value],
) -> BoxFuture<'a, Result<Value>> {
async move {
let address = match args {
[Value::Addr(address)] => *address,
_ => bail!("impersonate: invalid arguments"),
};
env.impersonate(address).await?;
Ok(Value::Addr(address))
}
.boxed()
}

fn stop_impersonate<'a>(
env: &'a mut Env,
_receiver: &'a Value,
_args: &'a [Value],
) -> BoxFuture<'a, Result<Value>> {
async move {
env.stop_impersonate().await?;
Ok(Value::Null)
}
.boxed()
}

fn set_balance<'a>(
env: &'a mut Env,
_receiver: &'a Value,
args: &'a [Value],
) -> BoxFuture<'a, Result<Value>> {
async move {
let (address, balance) = match args {
[Value::Addr(address), Value::Uint(b, 256)] => (*address, *b),
_ => bail!("setBalance: invalid arguments"),
};
env.get_provider()
.anvil_set_balance(address, balance)
.await?;
Ok(Value::Null)
}
.boxed()
}

fn load_ledger<'a>(
env: &'a mut Env,
_receiver: &'a Value,
Expand Down Expand Up @@ -210,6 +266,11 @@ lazy_static! {
rpc,
vec![vec![], vec![FunctionParam::new("url", Type::String)]]
);
pub static ref REPL_FORK: Arc<dyn FunctionDef> = SyncMethod::arc(
"fork",
fork,
vec![vec![], vec![FunctionParam::new("url", Type::String)]]
);
pub static ref REPL_DEBUG: Arc<dyn FunctionDef> = SyncMethod::arc(
"debug",
debug,
Expand Down Expand Up @@ -280,4 +341,19 @@ lazy_static! {
load_ledger,
vec![vec![], vec![FunctionParam::new("index", Type::Uint(256))]]
);
pub static ref REPL_START_PRANK: Arc<dyn FunctionDef> = AsyncMethod::arc(
"startPrank",
impersonate,
vec![vec![FunctionParam::new("adddress", Type::Address)]]
);
pub static ref REPL_STOP_PRANK: Arc<dyn FunctionDef> =
AsyncMethod::arc("stopPrank", stop_impersonate, vec![vec![]]);
pub static ref REPL_DEAL: Arc<dyn FunctionDef> = AsyncMethod::arc(
"deal",
set_balance,
vec![vec![
FunctionParam::new("adddress", Type::Address),
FunctionParam::new("balance", Type::Uint(256))
]]
);
}
Loading

0 comments on commit 4d172e5

Please sign in to comment.