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;