Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: anvil API #313

Merged
merged 36 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
052530b
added evm_setAccountNonce alias
vbar Jul 10, 2024
a2d1f0d
added anvil_setNonce alias
vbar Jul 10, 2024
7ab50f0
added anvil_impersonateAccount & anvil_stopImpersonatingAccount aliases
vbar Jul 10, 2024
37e6bf5
added anvil_mine alias
vbar Jul 10, 2024
f083ab2
added anvil_reset alias
vbar Jul 10, 2024
08eaafa
added anvil_setBalance alias
vbar Jul 10, 2024
f76d5e6
added anvil_setCode alias
vbar Jul 10, 2024
6f4c64d
added anvil_setStorageAt alias
vbar Jul 10, 2024
b5dc622
added anvil_* aliases
vbar Jul 24, 2024
7b8392b
added anvil_mine test
vbar Jul 24, 2024
34c9dd0
modified hardhat_impersonateAccount & hardhat_stopImpersonatingAccount
vbar Jul 24, 2024
0373993
added anvil_impersonateAccount & anvil_stopImpersonatingAccount test
vbar Jul 24, 2024
97b943b
added anvil_setCode test
vbar Jul 24, 2024
ac62abf
added hardhat_setStorageAt and anvil_setStorageAt tests
vbar Jul 24, 2024
a14ca13
formatted
vbar Jul 24, 2024
90a1216
Merge branch 'main' into anvil-api
vbar Jul 25, 2024
d36852f
removed links to anvil namespace
vbar Jul 31, 2024
4a54fe8
split tables
vbar Jul 31, 2024
bcb3397
Revert "split tables"
vbar Jul 31, 2024
17f7559
Merge branch 'main' into anvil-api
vbar Aug 14, 2024
db1269b
renamed AnvilNamespaceT::hardhat_mine to anvil_mine
vbar Aug 14, 2024
781c5a0
Update SUPPORTED_APIS.md
vbar Aug 14, 2024
0ed672c
added section for evm_setAccountNonce
vbar Aug 14, 2024
5857b4b
changed var to let to satisfy linter
vbar Aug 14, 2024
879ac94
using random address in anvil_setCode test
vbar Aug 14, 2024
6befae3
added semicolon
vbar Aug 14, 2024
5ded038
named test account address
vbar Aug 14, 2024
ee3da1e
changed var to let to satisfy linter
vbar Aug 15, 2024
2440f93
Update src/namespaces/anvil.rs
vbar Aug 15, 2024
5ff2701
removed to.be.within checks
vbar Aug 15, 2024
1f8a1f4
supressing TypeScript errors
vbar Aug 15, 2024
2fd9f3f
supressing TypeScript errors
vbar Aug 15, 2024
bf3e63b
Merge branch 'main' into anvil-api
vbar Aug 20, 2024
18fb136
changed anvil_setCode code type
vbar Aug 20, 2024
bb26813
using find instead of an explicit loop
vbar Aug 27, 2024
0c62782
Merge branch 'main' into anvil-api
AnastasiiaVashchuk Aug 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SUPPORTED_APIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ The `status` options are:
| [`EVM`](#evm-namespace) | [`evm_revert`](#evm_revert) | `SUPPORTED` | Revert the state of the blockchain to a previous snapshot |
| `EVM` | `evm_setAccountBalance` | `NOT IMPLEMENTED` | Sets the given account's balance to the specified WEI value |
| `EVM` | `evm_setAccountCode` | `NOT IMPLEMENTED` | Sets the given account's code to the specified data |
| `EVM` | `evm_setAccountNonce` | `NOT IMPLEMENTED` | Sets the given account's nonce to the specified value |
| [`EVM`](#evm-namespace) | [`evm_setAccountNonce`](#evm_setaccountnonce) | `SUPPORTED` | Sets the given account's nonce to the specified value |
AnastasiiaVashchuk marked this conversation as resolved.
Show resolved Hide resolved
| `EVM` | `evm_setAccountStorageAt` | `NOT IMPLEMENTED` | Sets the given account's storage slot to the specified data |
| `EVM` | `evm_setAutomine` | `NOT IMPLEMENTED` | Enables or disables the automatic mining of new blocks with each new transaction submitted to the network |
| `EVM` | `evm_setBlockGasLimit` | `NOT IMPLEMENTED` | Sets the Block Gas Limit of the network |
Expand Down
36 changes: 36 additions & 0 deletions e2e-tests/test/anvil-apis.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { expect } from "chai";
import { Wallet } from "zksync-web3";
import { getTestProvider } from "../helpers/utils";
import { ethers } from "hardhat";

const provider = getTestProvider();

describe("anvil_setBalance", function () {
AnastasiiaVashchuk marked this conversation as resolved.
Show resolved Hide resolved
AnastasiiaVashchuk marked this conversation as resolved.
Show resolved Hide resolved
it("Should update the balance of an account", async function () {
// Arrange
const userWallet = Wallet.createRandom().connect(provider);
const newBalance = ethers.utils.parseEther("42");

// Act
await provider.send("anvil_setBalance", [userWallet.address, newBalance._hex]);

// Assert
const balance = await userWallet.getBalance();
expect(balance.eq(newBalance)).to.true;
});
});

describe("anvil_setNonce", function () {
it("Should update the nonce of an account", async function () {
// Arrange
const userWallet = Wallet.createRandom().connect(provider);
const newNonce = 42;

// Act
await provider.send("anvil_setNonce", [userWallet.address, ethers.utils.hexlify(newNonce)]);

// Assert
const nonce = await userWallet.getNonce();
expect(nonce).to.equal(newNonce);
});
});
15 changes: 15 additions & 0 deletions e2e-tests/test/evm-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ import { Deployer } from "@matterlabs/hardhat-zksync-deploy";

const provider = getTestProvider();

describe("evm_setAccountNonce", function () {
it("Should update the nonce of an account", async function () {
// Arrange
const userWallet = Wallet.createRandom().connect(provider);
const newNonce = 42;

// Act
await provider.send("evm_setAccountNonce", [userWallet.address, ethers.utils.hexlify(newNonce)]);

// Assert
const nonce = await userWallet.getNonce();
expect(nonce).to.equal(newNonce);
});
});

describe("evm_mine", function () {
it("Should mine one block", async function () {
// Arrange
Expand Down
6 changes: 4 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ use jsonrpc_core::MetaIoHandler;
use zksync_basic_types::H160;

use crate::namespaces::{
ConfigurationApiNamespaceT, DebugNamespaceT, EthNamespaceT, EthTestNodeNamespaceT,
EvmNamespaceT, HardhatNamespaceT, NetNamespaceT, Web3NamespaceT, ZksNamespaceT,
AnvilNamespaceT, ConfigurationApiNamespaceT, DebugNamespaceT, EthNamespaceT,
EthTestNodeNamespaceT, EvmNamespaceT, HardhatNamespaceT, NetNamespaceT, Web3NamespaceT,
ZksNamespaceT,
};

/// List of legacy wallets (address, private key) that we seed with tokens at start.
Expand Down Expand Up @@ -165,6 +166,7 @@ async fn build_json_http<
io.extend_with(DebugNamespaceT::to_delegate(node.clone()));
io.extend_with(EthNamespaceT::to_delegate(node.clone()));
io.extend_with(EthTestNodeNamespaceT::to_delegate(node.clone()));
io.extend_with(AnvilNamespaceT::to_delegate(node.clone()));
io.extend_with(EvmNamespaceT::to_delegate(node.clone()));
io.extend_with(HardhatNamespaceT::to_delegate(node.clone()));
io.extend_with(ZksNamespaceT::to_delegate(node));
Expand Down
115 changes: 115 additions & 0 deletions src/namespaces/anvil.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use jsonrpc_derive::rpc;
use zksync_basic_types::{Address, U256, U64};

use super::{ResetRequest, RpcResult};

#[rpc]
pub trait AnvilNamespaceT {
/// Sets the balance of the given address to the given balance.
///
/// # Arguments
///
/// * `address` - The `Address` whose balance will be edited
/// * `balance` - The new balance to set for the given address, in wei
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "anvil_setBalance")]
fn set_balance(&self, address: Address, balance: U256) -> RpcResult<bool>;

/// Modifies an account's nonce by overwriting it.
///
/// # Arguments
///
/// * `address` - The `Address` whose nonce is to be changed
/// * `nonce` - The new nonce
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "anvil_setNonce")]
fn set_nonce(&self, address: Address, balance: U256) -> RpcResult<bool>;

/// Sometimes you may want to advance the latest block number of the network by a large number of blocks.
/// One way to do this would be to call the evm_mine RPC method multiple times, but this is too slow if you want to mine thousands of blocks.
/// The hardhat_mine method can mine any number of blocks at once, in constant time. (It exhibits the same performance no matter how many blocks are mined.)
vbar marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Arguments
///
/// * `num_blocks` - The number of blocks to mine, defaults to 1
/// * `interval` - The interval between the timestamps of each block, in seconds, and it also defaults to 1
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "anvil_mine")]
fn hardhat_mine(&self, num_blocks: Option<U64>, interval: Option<U64>) -> RpcResult<bool>;

/// Reset the state of the network back to a fresh forked state, or disable forking.
///
/// # Arguments
///
/// * `reset_spec` - The requested state, defaults to resetting the current network.
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "anvil_reset")]
fn reset_network(&self, reset_spec: Option<ResetRequest>) -> RpcResult<bool>;

/// Era Test Node allows transactions impersonating specific account and contract addresses.
/// To impersonate an account use this method, passing the address to impersonate as its parameter.
/// After calling this method, any transactions with this sender will be executed without verification.
/// Multiple addresses can be impersonated at once.
///
/// # Arguments
///
/// * `address` - The address to impersonate
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "anvil_impersonateAccount")]
fn impersonate_account(&self, address: Address) -> RpcResult<bool>;

/// Use this method to stop impersonating an account after having previously used `anvil_impersonateAccount`
/// The method returns `true` if the account was being impersonated and `false` otherwise.
///
/// # Arguments
///
/// * `address` - The address to stop impersonating.
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "anvil_stopImpersonatingAccount")]
fn stop_impersonating_account(&self, address: Address) -> RpcResult<bool>;

/// Modifies the bytecode stored at an account's address.
///
/// # Arguments
///
/// * `address` - The address where the given code should be stored.
/// * `code` - The code to be stored.
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "anvil_setCode")]
fn set_code(&self, address: Address, code: Vec<u8>) -> RpcResult<()>;

/// Directly modifies the storage of a contract at a specified slot.
///
/// # Arguments
///
/// * `address` - The contract address whose storage is to be modified.
/// * `slot` - The storage slot to modify.
/// * `value` - The value to be set at the specified slot.
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "anvil_setStorageAt")]
fn set_storage_at(&self, address: Address, slot: U256, value: U256) -> RpcResult<bool>;
}
15 changes: 14 additions & 1 deletion src/namespaces/evm.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use jsonrpc_derive::rpc;
use zksync_basic_types::U64;
use zksync_basic_types::{Address, U256, U64};

use crate::namespaces::RpcResult;

Expand All @@ -15,6 +15,19 @@ pub trait EvmNamespaceT {
#[rpc(name = "evm_increaseTime")]
fn increase_time(&self, time_delta_seconds: u64) -> RpcResult<u64>;

/// Modifies an account's nonce by overwriting it.
///
/// # Arguments
///
/// * `address` - The `Address` whose nonce is to be changed
/// * `nonce` - The new nonce
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "evm_setAccountNonce")]
fn set_nonce(&self, address: Address, balance: U256) -> RpcResult<bool>;

/// Force a single block to be mined.
///
/// Will mine an empty block (containing zero transactions)
Expand Down
2 changes: 2 additions & 0 deletions src/namespaces/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod anvil;
mod config;
mod debug;
mod eth;
Expand All @@ -8,6 +9,7 @@ mod net;
mod web3;
mod zks;

pub use anvil::AnvilNamespaceT;
pub use config::ConfigurationApiNamespaceT;
pub use debug::DebugNamespaceT;
pub use eth::EthNamespaceT;
Expand Down
85 changes: 85 additions & 0 deletions src/node/anvil.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use zksync_basic_types::{Address, U256, U64};
use zksync_web3_decl::error::Web3Error;

use crate::{
fork::ForkSource,
namespaces::{AnvilNamespaceT, ResetRequest, RpcResult},
node::InMemoryNode,
utils::{into_jsrpc_error, into_jsrpc_error_message, IntoBoxedFuture},
};

impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> AnvilNamespaceT
for InMemoryNode<S>
{
fn set_balance(&self, address: Address, balance: U256) -> RpcResult<bool> {
self.set_balance(address, balance)
.map_err(|err| {
tracing::error!("failed setting balance : {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn set_nonce(&self, address: Address, balance: U256) -> RpcResult<bool> {
self.set_nonce(address, balance)
.map_err(|err| {
tracing::error!("failed setting nonce: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn hardhat_mine(&self, num_blocks: Option<U64>, interval: Option<U64>) -> RpcResult<bool> {
AnastasiiaVashchuk marked this conversation as resolved.
Show resolved Hide resolved
self.mine_blocks(num_blocks, interval)
.map_err(|err| {
tracing::error!("failed mining blocks: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn reset_network(&self, reset_spec: Option<ResetRequest>) -> RpcResult<bool> {
self.reset_network(reset_spec)
.map_err(|err| {
tracing::error!("failed reset: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn impersonate_account(&self, address: Address) -> RpcResult<bool> {
self.impersonate_account(address)
.map_err(|err| {
tracing::error!("failed impersonating account: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn stop_impersonating_account(&self, address: Address) -> RpcResult<bool> {
InMemoryNode::<S>::stop_impersonating_account(self, address)
.map_err(|err| {
tracing::error!("failed stopping to impersonate account: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn set_code(&self, address: Address, code: Vec<u8>) -> RpcResult<()> {
self.set_code(address, code)
.map_err(|err| {
tracing::error!("failed setting code: {:?}", err);
into_jsrpc_error_message(err.to_string())
})
.into_boxed_future()
}

fn set_storage_at(&self, address: Address, slot: U256, value: U256) -> RpcResult<bool> {
self.set_storage_at(address, slot, value)
.map_err(|err| {
tracing::error!("failed setting storage: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}
}
11 changes: 10 additions & 1 deletion src/node/evm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use zksync_basic_types::U64;
use zksync_basic_types::{Address, U256, U64};
use zksync_web3_decl::error::Web3Error;

use crate::{
Expand All @@ -20,6 +20,15 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EvmNamespa
.into_boxed_future()
}

fn set_nonce(&self, address: Address, balance: U256) -> RpcResult<bool> {
self.set_nonce(address, balance)
.map_err(|err| {
tracing::error!("failed setting nonce: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn evm_mine(&self) -> RpcResult<String> {
self.mine_block()
.map_err(|err| {
Expand Down
1 change: 1 addition & 0 deletions src/node/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! In-memory node, that supports forking other networks.

mod anvil;
mod config_api;
mod debug;
mod eth;
Expand Down