diff --git a/SUPPORTED_APIS.md b/SUPPORTED_APIS.md
index 221ff473..28ae9c0d 100644
--- a/SUPPORTED_APIS.md
+++ b/SUPPORTED_APIS.md
@@ -14,6 +14,14 @@ The `status` options are:
| Namespace | API |
Status
| Description |
| --- | --- | --- | --- |
+| `ANVIL` | `anvil_setNonce` | `SUPPORTED` | Sets the nonce of an address.|
+| `ANVIL` | `anvil_impersonateAccount` | `SUPPORTED` | Impersonate an account |
+| `ANVIL` | `anvil_stopImpersonatingAccount` | `SUPPORTED` | Stop impersonating an account after having previously used `anvil_impersonateAccount` |
+| `ANVIL` | `anvil_reset` | `PARTIALLY` | Resets the state of the network; cannot revert to past block numbers, unless they're in a fork |
+| `ANVIL` | `anvil_mine` | `SUPPORTED` | Mine any number of blocks at once, in constant time |
+| `ANVIL` | `anvil_setBalance` | `SUPPORTED` | Modifies the balance of an account |
+| `ANVIL` | `anvil_setCode` | `SUPPORTED` | Sets the bytecode of a given account |
+| `ANVIL` | `anvil_setStorageAt` | `SUPPORTED` | Sets the storage value at a given key for a given account |
| [`CONFIG`](#config-namespace) | [`config_getShowCalls`](#config_getshowcalls) | `SUPPORTED` | Gets the current value of `show_calls` that's originally set with `--show-calls` option |
| [`CONFIG`](#config-namespace) | [`config_getShowOutputs`](#config_getshowoutputs) | `SUPPORTED` | Gets the current value of `show_outputs` that's originally set with `--show-outputs` option |
| [`CONFIG`](#config-namespace) | [`config_getCurrentTimestamp`](#config_getcurrenttimestamp) | `SUPPORTED` | Gets the value of `current_timestamp` for the node |
@@ -85,7 +93,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 |
| `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 |
@@ -1669,6 +1677,35 @@ curl --request POST \
}'
```
+### `evm_setAccountNonce`
+
+[source](src/node/evm.rs)
+
+Modifies an account's nonce by overwriting it.
+The new nonce must be greater than the existing nonce.
+
+#### Arguments
+
++ `address: Address` - The `Address` whose nonce is to be changed
++ `nonce: U256` - The new nonce
+
+#### Example
+
+```bash
+curl --request POST \
+ --url http://localhost:8011/ \
+ --header 'content-type: application/json' \
+ --data '{
+ "jsonrpc": "2.0",
+ "id": "1",
+ "method": "evm_setAccountNonce",
+ "params": [
+ "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
+ "0x1337"
+ ]
+ }'
+```
+
### `evm_increaseTime`
[source](src/node/evm.rs)
diff --git a/e2e-tests/test/anvil-apis.test.ts b/e2e-tests/test/anvil-apis.test.ts
new file mode 100644
index 00000000..787b2fd6
--- /dev/null
+++ b/e2e-tests/test/anvil-apis.test.ts
@@ -0,0 +1,191 @@
+import { expect } from "chai";
+import { Wallet } from "zksync-web3";
+import { deployContract, expectThrowsAsync, getTestProvider } from "../helpers/utils";
+import { RichAccounts } from "../helpers/constants";
+import { ethers } from "hardhat";
+import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
+import * as hre from "hardhat";
+import { keccak256 } from "ethers/lib/utils";
+import { BigNumber } from "ethers";
+
+const provider = getTestProvider();
+
+describe("anvil_setBalance", function () {
+ 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);
+ });
+});
+
+describe("anvil_mine", function () {
+ it("Should mine multiple blocks with a given interval", async function () {
+ // Arrange
+ const numberOfBlocks = 100;
+ const intervalInSeconds = 60;
+ const startingBlock = await provider.getBlock("latest");
+ const startingTimestamp: number = await provider.send("config_getCurrentTimestamp", []);
+
+ // Act
+ await provider.send("anvil_mine", [ethers.utils.hexlify(numberOfBlocks), ethers.utils.hexlify(intervalInSeconds)]);
+
+ // Assert
+ const latestBlock = await provider.getBlock("latest");
+ expect(latestBlock.number).to.equal(startingBlock.number + numberOfBlocks, "Block number mismatch");
+ expect(latestBlock.timestamp).to.equal(
+ startingTimestamp + (numberOfBlocks - 1) * intervalInSeconds * 1000 + 1,
+ "Timestamp mismatch"
+ );
+ });
+});
+
+describe("anvil_impersonateAccount & anvil_stopImpersonatingAccount", function () {
+ it("Should allow transfers of funds without knowing the Private Key", async function () {
+ // Arrange
+ const userWallet = Wallet.createRandom().connect(provider);
+ const richAccount = RichAccounts[5].Account;
+ const beforeBalance = await provider.getBalance(richAccount);
+
+ // Act
+ await provider.send("anvil_impersonateAccount", [richAccount]);
+
+ const signer = await ethers.getSigner(richAccount);
+ const tx = {
+ to: userWallet.address,
+ value: ethers.utils.parseEther("0.42"),
+ };
+
+ const recieptTx = await signer.sendTransaction(tx);
+ await recieptTx.wait();
+
+ await provider.send("anvil_stopImpersonatingAccount", [richAccount]);
+
+ // Assert
+ expect((await userWallet.getBalance()).eq(ethers.utils.parseEther("0.42"))).to.true;
+ expect((await provider.getBalance(richAccount)).eq(beforeBalance.sub(ethers.utils.parseEther("0.42")))).to.true;
+ });
+});
+
+describe("anvil_setCode", function () {
+ it("Should set code at an address", async function () {
+ // Arrange
+ const wallet = new Wallet(RichAccounts[0].PrivateKey);
+ const deployer = new Deployer(hre, wallet);
+
+ const randomWallet = Wallet.createRandom();
+ const address = randomWallet.address;
+ const artifact = await deployer.loadArtifact("Return5");
+ const contractCode = artifact.deployedBytecode;
+
+ // Act
+ await provider.send("anvil_setCode", [address, contractCode]);
+
+ // Assert
+ const result = await provider.send("eth_call", [
+ {
+ to: address,
+ data: keccak256(ethers.utils.toUtf8Bytes("value()")).substring(0, 10),
+ from: wallet.address,
+ gas: "0x1000",
+ gasPrice: "0x0ee6b280",
+ value: "0x0",
+ nonce: "0x1",
+ },
+ "latest",
+ ]);
+ expect(BigNumber.from(result).toNumber()).to.eq(5);
+ });
+
+ it("Should reject invalid code", async function () {
+ const action = async () => {
+ // Arrange
+ const wallet = new Wallet(RichAccounts[0].PrivateKey);
+ const deployer = new Deployer(hre, wallet);
+
+ const address = "0x1000000000000000000000000000000000001111";
+ const artifact = await deployer.loadArtifact("Return5");
+ const contractCode = artifact.deployedBytecode;
+ const shortCode = contractCode.slice(0, contractCode.length - 2);
+
+ // Act
+ await provider.send("anvil_setCode", [address, shortCode]);
+ };
+
+ await expectThrowsAsync(action, "bytes must be divisible by 32");
+ });
+
+ it("Should update code with a different smart contract", async function () {
+ // Arrange
+ const wallet = new Wallet(RichAccounts[0].PrivateKey);
+ const deployer = new Deployer(hre, wallet);
+
+ const greeter = await deployContract(deployer, "Greeter", ["Hi"]);
+ expect(await greeter.greet()).to.eq("Hi");
+ const artifact = await deployer.loadArtifact("Return5");
+ const newContractCode = artifact.deployedBytecode;
+
+ // Act
+ await provider.send("anvil_setCode", [greeter.address, newContractCode]);
+
+ // Assert
+ const result = await provider.send("eth_call", [
+ {
+ to: greeter.address,
+ data: keccak256(ethers.utils.toUtf8Bytes("value()")).substring(0, 10),
+ from: wallet.address,
+ gas: "0x1000",
+ gasPrice: "0x0ee6b280",
+ value: "0x0",
+ nonce: "0x1",
+ },
+ "latest",
+ ]);
+ expect(BigNumber.from(result).toNumber()).to.eq(5);
+ });
+});
+
+describe("anvil_setStorageAt", function () {
+ it("Should set storage at an address", async function () {
+ const wallet = new Wallet(RichAccounts[0].PrivateKey, provider);
+ const userWallet = Wallet.createRandom().connect(provider);
+ await wallet.sendTransaction({
+ to: userWallet.address,
+ value: ethers.utils.parseEther("3"),
+ });
+
+ const deployer = new Deployer(hre, userWallet);
+ const artifact = await deployer.loadArtifact("MyERC20");
+ const token = await deployer.deploy(artifact, ["MyToken", "MyToken", 18]);
+
+ const before = await provider.send("eth_getStorageAt", [token.address, "0x0", "latest"]);
+ expect(BigNumber.from(before).toNumber()).to.eq(0);
+
+ const value = ethers.utils.hexlify(ethers.utils.zeroPad("0x10", 32));
+ await provider.send("anvil_setStorageAt", [token.address, "0x0", value]);
+
+ const after = await provider.send("eth_getStorageAt", [token.address, "0x0", "latest"]);
+ expect(BigNumber.from(after).toNumber()).to.eq(16);
+ });
+});
diff --git a/e2e-tests/test/evm-apis.test.ts b/e2e-tests/test/evm-apis.test.ts
index a82f802c..e0db9bb4 100644
--- a/e2e-tests/test/evm-apis.test.ts
+++ b/e2e-tests/test/evm-apis.test.ts
@@ -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
diff --git a/e2e-tests/test/hardhat-apis.test.ts b/e2e-tests/test/hardhat-apis.test.ts
index b5d05e0c..c12695d8 100644
--- a/e2e-tests/test/hardhat-apis.test.ts
+++ b/e2e-tests/test/hardhat-apis.test.ts
@@ -68,12 +68,12 @@ describe("hardhat_impersonateAccount & hardhat_stopImpersonatingAccount", functi
it("Should allow transfers of funds without knowing the Private Key", async function () {
// Arrange
const userWallet = Wallet.createRandom().connect(provider);
- const beforeBalance = await provider.getBalance(RichAccounts[0].Account);
+ const beforeBalance = await provider.getBalance(RichAccounts[5].Account);
// Act
- await provider.send("hardhat_impersonateAccount", [RichAccounts[0].Account]);
+ await provider.send("hardhat_impersonateAccount", [RichAccounts[5].Account]);
- const signer = await ethers.getSigner(RichAccounts[0].Account);
+ const signer = await ethers.getSigner(RichAccounts[5].Account);
const tx = {
to: userWallet.address,
value: ethers.utils.parseEther("0.42"),
@@ -82,9 +82,11 @@ describe("hardhat_impersonateAccount & hardhat_stopImpersonatingAccount", functi
const recieptTx = await signer.sendTransaction(tx);
await recieptTx.wait();
+ await provider.send("hardhat_stopImpersonatingAccount", [RichAccounts[5].Account]);
+
// Assert
expect((await userWallet.getBalance()).eq(ethers.utils.parseEther("0.42"))).to.true;
- expect((await provider.getBalance(RichAccounts[0].Account)).eq(beforeBalance.sub(ethers.utils.parseEther("0.42"))))
+ expect((await provider.getBalance(RichAccounts[5].Account)).eq(beforeBalance.sub(ethers.utils.parseEther("0.42"))))
.to.true;
});
});
@@ -165,3 +167,27 @@ describe("hardhat_setCode", function () {
expect(BigNumber.from(result).toNumber()).to.eq(5);
});
});
+
+describe("hardhat_setStorageAt", function () {
+ it("Should set storage at an address", async function () {
+ const wallet = new Wallet(RichAccounts[0].PrivateKey, provider);
+ const userWallet = Wallet.createRandom().connect(provider);
+ await wallet.sendTransaction({
+ to: userWallet.address,
+ value: ethers.utils.parseEther("3"),
+ });
+
+ const deployer = new Deployer(hre, userWallet);
+ const artifact = await deployer.loadArtifact("MyERC20");
+ const token = await deployer.deploy(artifact, ["MyToken", "MyToken", 18]);
+
+ const before = await provider.send("eth_getStorageAt", [token.address, "0x0", "latest"]);
+ expect(BigNumber.from(before).toNumber()).to.eq(0);
+
+ const value = ethers.utils.hexlify(ethers.utils.zeroPad("0x10", 32));
+ await provider.send("hardhat_setStorageAt", [token.address, "0x0", value]);
+
+ const after = await provider.send("eth_getStorageAt", [token.address, "0x0", "latest"]);
+ expect(BigNumber.from(after).toNumber()).to.eq(16);
+ });
+});
diff --git a/e2e-tests/test/main.test.ts b/e2e-tests/test/main.test.ts
index a5739dad..75d4a217 100644
--- a/e2e-tests/test/main.test.ts
+++ b/e2e-tests/test/main.test.ts
@@ -62,11 +62,13 @@ describe("Greeter Smart Contract", function () {
// Validate log is created
expect(receipt.logs.length).to.greaterThanOrEqual(1);
- const setGreetingLog = receipt.logs[0];
- expect(setGreetingLog.address).to.equal(greeter.address);
+ const setGreetingLog = receipt.logs.find((log) => log.address === greeter.address);
+ expect(setGreetingLog).not.to.equal(null);
const eventInterface = new ethers.utils.Interface(["event LogString(string value)"]);
- expect(eventInterface.parseLog(setGreetingLog).args[0]).to.equal("Greeting is being updated to Luke Skywalker");
+ const parsedLog = eventInterface.parseLog(setGreetingLog!);
+ const parsedLogArg = parsedLog.args[0].toString();
+ expect(parsedLogArg).to.equal("Greeting is being updated to Luke Skywalker");
});
it("Should filter event logs", async function () {
@@ -78,8 +80,12 @@ describe("Greeter Smart Contract", function () {
const setGreetingTx = await greeter.setGreeting("Luke Skywalker");
let receipt: TransactionReceipt = await setGreetingTx.wait();
+ expect(receipt.logs.length).to.greaterThanOrEqual(1);
+ const setGreetingLog = receipt.logs.find((log) => log.address === greeter.address);
+ expect(setGreetingLog).not.to.equal(null);
+
// Create filter
- const topic = receipt.logs[0].topics[0];
+ const topic = setGreetingLog!.topics[0];
const filterId = await provider.send("eth_newFilter", [
{
fromBlock: "earliest",
diff --git a/e2e-tests/test/zks-apis.test.ts b/e2e-tests/test/zks-apis.test.ts
index 2966fe19..424e9ace 100644
--- a/e2e-tests/test/zks-apis.test.ts
+++ b/e2e-tests/test/zks-apis.test.ts
@@ -30,15 +30,12 @@ describe("zks_estimateFee", function () {
// Act
const response: Fee = await provider.send("zks_estimateFee", [transaction]);
// Assert
- expect(ethers.BigNumber.from(response.gas_limit)).to.eql(ethers.BigNumber.from("3606743"), "Unexpected gas_limit");
+ expect(ethers.BigNumber.from(response.gas_limit).toNumber()).to.eql(4048728, "Unexpected gas_limit");
expect(ethers.BigNumber.from(response.gas_per_pubdata_limit)).to.eql(
ethers.BigNumber.from("50000"),
"Unexpected gas_per_pubdata_limit"
);
- expect(ethers.BigNumber.from(response.max_fee_per_gas)).to.eql(
- ethers.BigNumber.from("37500000"),
- "Unexpected max_fee_per_gas"
- );
+ expect(ethers.BigNumber.from(response.max_fee_per_gas).toNumber()).to.eql(37500000, "Unexpected max_fee_per_gas");
expect(ethers.BigNumber.from(response.max_priority_fee_per_gas)).to.eql(
ethers.BigNumber.from("0"),
"Unexpected max_priority_fee_per_gas"
diff --git a/src/main.rs b/src/main.rs
index 2057c380..9783e92f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -45,8 +45,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.
@@ -166,6 +167,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));
diff --git a/src/namespaces/anvil.rs b/src/namespaces/anvil.rs
new file mode 100644
index 00000000..4e332cfb
--- /dev/null
+++ b/src/namespaces/anvil.rs
@@ -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;
+
+ /// 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;
+
+ /// 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 `anvil_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.)
+ ///
+ /// # 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 anvil_mine(&self, num_blocks: Option, interval: Option) -> RpcResult;
+
+ /// 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) -> RpcResult;
+
+ /// 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;
+
+ /// 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;
+
+ /// 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: String) -> 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;
+}
diff --git a/src/namespaces/evm.rs b/src/namespaces/evm.rs
index 3b0bf3e5..ee6472f1 100644
--- a/src/namespaces/evm.rs
+++ b/src/namespaces/evm.rs
@@ -1,5 +1,5 @@
use jsonrpc_derive::rpc;
-use zksync_basic_types::U64;
+use zksync_basic_types::{Address, U256, U64};
use crate::namespaces::RpcResult;
@@ -15,6 +15,19 @@ pub trait EvmNamespaceT {
#[rpc(name = "evm_increaseTime")]
fn increase_time(&self, time_delta_seconds: u64) -> RpcResult;
+ /// 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;
+
/// Force a single block to be mined.
///
/// Will mine an empty block (containing zero transactions)
diff --git a/src/namespaces/mod.rs b/src/namespaces/mod.rs
index 760140fe..f1102fb4 100644
--- a/src/namespaces/mod.rs
+++ b/src/namespaces/mod.rs
@@ -1,3 +1,4 @@
+mod anvil;
mod config;
mod debug;
mod eth;
@@ -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;
diff --git a/src/node/anvil.rs b/src/node/anvil.rs
new file mode 100644
index 00000000..bed94535
--- /dev/null
+++ b/src/node/anvil.rs
@@ -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 AnvilNamespaceT
+ for InMemoryNode
+{
+ fn set_balance(&self, address: Address, balance: U256) -> RpcResult {
+ 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 {
+ self.set_nonce(address, balance)
+ .map_err(|err| {
+ tracing::error!("failed setting nonce: {:?}", err);
+ into_jsrpc_error(Web3Error::InternalError(err))
+ })
+ .into_boxed_future()
+ }
+
+ fn anvil_mine(&self, num_blocks: Option, interval: Option) -> RpcResult {
+ 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) -> RpcResult {
+ 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 {
+ 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 {
+ InMemoryNode::::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: String) -> 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 {
+ 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()
+ }
+}
diff --git a/src/node/evm.rs b/src/node/evm.rs
index aecc98e6..16247484 100644
--- a/src/node/evm.rs
+++ b/src/node/evm.rs
@@ -1,4 +1,4 @@
-use zksync_basic_types::U64;
+use zksync_basic_types::{Address, U256, U64};
use zksync_web3_decl::error::Web3Error;
use crate::{
@@ -20,6 +20,15 @@ impl EvmNamespa
.into_boxed_future()
}
+ fn set_nonce(&self, address: Address, balance: U256) -> RpcResult {
+ 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 {
self.mine_block()
.map_err(|err| {
diff --git a/src/node/mod.rs b/src/node/mod.rs
index c0e434f5..463bb87e 100644
--- a/src/node/mod.rs
+++ b/src/node/mod.rs
@@ -1,5 +1,6 @@
//! In-memory node, that supports forking other networks.
+mod anvil;
mod config_api;
mod debug;
mod eth;