Skip to content

Commit

Permalink
feat: add anvil_setTime, anvil_increaseTime, `anvil_setNextBlockT…
Browse files Browse the repository at this point in the history
…imestamp` (#421)

* make existing time methods accept `Numeric`

* implement `anvil_` time API endpoints

* update SUPPORTED_APIS.md

* tweak e2e tests
  • Loading branch information
itegulov authored Nov 25, 2024
1 parent ebc7781 commit 9818ee5
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 35 deletions.
3 changes: 3 additions & 0 deletions SUPPORTED_APIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ The `status` options are:

| Namespace | API | <div style="width:130px">Status</div> | Description |
| --- | --- | --- | --- |
| `ANVIL` | `anvil_setTime` | `SUPPORTED` | Sets the internal clock time to the given timestamp |
| `ANVIL` | `anvil_increaseTime` | `SUPPORTED` | Jump forward in time by the given amount of time, in seconds |
| `ANVIL` | `anvil_setNextBlockTimestamp` | `SUPPORTED` | Works like `anvil_increaseTime`, but takes the exact timestamp that you want in the next block, and increases the time accordingly |
| `ANVIL` | `anvil_autoImpersonateAccount` | `SUPPORTED` | Sets auto impersonation status.|
| `ANVIL` | `anvil_setNonce` | `SUPPORTED` | Sets the nonce of an address.|
| `ANVIL` | `anvil_impersonateAccount` | `SUPPORTED` | Impersonate an account |
Expand Down
72 changes: 72 additions & 0 deletions e2e-tests/test/anvil-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,78 @@ import { BigNumber } from "ethers";

const provider = getTestProvider();

describe("anvil_increaseTime", function () {
it("Should increase current timestamp of the node", async function () {
// Arrange
const timeIncreaseInSeconds = 13;
const wallet = new Wallet(RichAccounts[0].PrivateKey, provider);
const userWallet = Wallet.createRandom().connect(provider);
let expectedTimestamp: number = await provider.send("config_getCurrentTimestamp", []);
expectedTimestamp += timeIncreaseInSeconds;

// Act
await provider.send("anvil_increaseTime", [timeIncreaseInSeconds]);

await wallet.sendTransaction({
to: userWallet.address,
value: ethers.utils.parseEther("0.1"),
});
expectedTimestamp += 2; // New transaction will add two blocks

// Assert
const newBlockTimestamp = (await provider.getBlock("latest")).timestamp;
expect(newBlockTimestamp).to.equal(expectedTimestamp);
});
});

describe("anvil_setNextBlockTimestamp", function () {
it("Should set current timestamp of the node to specific value", async function () {
// Arrange
const timeIncreaseInMS = 123;
let expectedTimestamp: number = await provider.send("config_getCurrentTimestamp", []);
expectedTimestamp += timeIncreaseInMS;
const wallet = new Wallet(RichAccounts[0].PrivateKey, provider);
const userWallet = Wallet.createRandom().connect(provider);

// Act
await provider.send("anvil_setNextBlockTimestamp", [expectedTimestamp]);

await wallet.sendTransaction({
to: userWallet.address,
value: ethers.utils.parseEther("0.1"),
});
expectedTimestamp += 1; // After executing a transaction, the node puts it into a block and increases its current timestamp

// Assert
const newBlockTimestamp = (await provider.getBlock("latest")).timestamp;
expect(newBlockTimestamp).to.equal(expectedTimestamp);
});
});

describe("anvil_setTime", function () {
it("Should set current timestamp of the node to specific value", async function () {
// Arrange
const timeIncreaseInMS = 123;
let expectedTimestamp: number = await provider.send("config_getCurrentTimestamp", []);
expectedTimestamp += timeIncreaseInMS;
const wallet = new Wallet(RichAccounts[0].PrivateKey, provider);
const userWallet = Wallet.createRandom().connect(provider);

// Act
await provider.send("anvil_setTime", [expectedTimestamp]);

await wallet.sendTransaction({
to: userWallet.address,
value: ethers.utils.parseEther("0.1"),
});
expectedTimestamp += 2; // New transaction will add two blocks

// Assert
const newBlockTimestamp = (await provider.getBlock("latest")).timestamp;
expect(newBlockTimestamp).to.equal(expectedTimestamp);
});
});

describe("anvil_setBalance", function () {
it("Should update the balance of an account", async function () {
// Arrange
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/test/evm-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe("evm_setNextBlockTimestamp", function () {
const userWallet = Wallet.createRandom().connect(provider);

// Act
await provider.send("evm_setNextBlockTimestamp", [expectedTimestamp.toString(16)]);
await provider.send("evm_setNextBlockTimestamp", [expectedTimestamp]);

await wallet.sendTransaction({
to: userWallet.address,
Expand Down
33 changes: 33 additions & 0 deletions src/namespaces/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,42 @@ use jsonrpc_derive::rpc;
use zksync_types::{Address, U256, U64};

use super::{ResetRequest, RpcResult};
use crate::utils::Numeric;

#[rpc]
pub trait AnvilNamespaceT {
/// Set the current timestamp for the node.
/// Warning: This will allow you to move backwards in time, which may cause new blocks to appear to be
/// mined before old blocks. This will result in an invalid state.
///
/// # Arguments
///
/// * `time` - The timestamp to set the time to
///
/// # Returns
/// The difference between the current timestamp and the new timestamp.
#[rpc(name = "anvil_setTime")]
fn set_time(&self, timestamp: Numeric) -> RpcResult<i128>;

/// Increase the current timestamp for the node
///
/// # Arguments
///
/// * `seconds` - The number of seconds to increase time by
///
/// # Returns
/// The applied time delta to the current timestamp in seconds.
#[rpc(name = "anvil_increaseTime")]
fn increase_time(&self, seconds: Numeric) -> RpcResult<u64>;

/// Set timestamp for the next block. The timestamp must be in future.
///
/// # Arguments
///
/// * `timestamp` - The timestamp to set the time to
#[rpc(name = "anvil_setNextBlockTimestamp")]
fn set_next_block_timestamp(&self, timestamp: Numeric) -> RpcResult<()>;

/// Sets auto impersonation status.
///
/// # Arguments
Expand Down
10 changes: 4 additions & 6 deletions src/namespaces/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use jsonrpc_derive::rpc;
use zksync_types::{Address, U256, U64};

use crate::namespaces::RpcResult;
use crate::utils::Numeric;

#[rpc]
pub trait EvmNamespaceT {
Expand All @@ -13,7 +14,7 @@ pub trait EvmNamespaceT {
/// # Returns
/// The applied time delta to `current_timestamp` value for the InMemoryNodeInner.
#[rpc(name = "evm_increaseTime")]
fn increase_time(&self, time_delta_seconds: u64) -> RpcResult<u64>;
fn increase_time(&self, time_delta_seconds: Numeric) -> RpcResult<u64>;

/// Modifies an account's nonce by overwriting it.
///
Expand Down Expand Up @@ -41,11 +42,8 @@ pub trait EvmNamespaceT {
///
/// # Parameters
/// - `timestamp`: The timestamp to set the time to
///
/// # Returns
/// The new timestamp value for the InMemoryNodeInner.
#[rpc(name = "evm_setNextBlockTimestamp")]
fn set_next_block_timestamp(&self, timestamp: U64) -> RpcResult<U64>;
fn set_next_block_timestamp(&self, timestamp: Numeric) -> RpcResult<()>;

/// Set the current timestamp for the node.
/// Warning: This will allow you to move backwards in time, which may cause new blocks to appear to be
Expand All @@ -57,7 +55,7 @@ pub trait EvmNamespaceT {
/// # Returns
/// The difference between the `current_timestamp` and the new timestamp for the InMemoryNodeInner.
#[rpc(name = "evm_setTime")]
fn set_time(&self, time: u64) -> RpcResult<i128>;
fn set_time(&self, time: Numeric) -> RpcResult<i128>;

/// Snapshot the state of the blockchain at the current block. Takes no parameters. Returns the id of the snapshot
/// that was created. A snapshot can only be reverted once. After a successful evm_revert, the same snapshot id cannot
Expand Down
28 changes: 28 additions & 0 deletions src/node/anvil.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use zksync_types::{Address, U256, U64};
use zksync_web3_decl::error::Web3Error;

use crate::utils::Numeric;
use crate::{
fork::ForkSource,
namespaces::{AnvilNamespaceT, ResetRequest, RpcResult},
Expand All @@ -11,6 +12,33 @@ use crate::{
impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> AnvilNamespaceT
for InMemoryNode<S>
{
fn set_time(&self, timestamp: Numeric) -> RpcResult<i128> {
self.set_time(timestamp)
.map_err(|err| {
tracing::error!("failed setting time: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn increase_time(&self, seconds: Numeric) -> RpcResult<u64> {
self.increase_time(seconds)
.map_err(|err| {
tracing::error!("failed increasing time: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn set_next_block_timestamp(&self, timestamp: Numeric) -> RpcResult<()> {
self.set_next_block_timestamp(timestamp)
.map_err(|err| {
tracing::error!("failed setting time for next timestamp: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn auto_impersonate_account(&self, enabled: bool) -> RpcResult<()> {
self.auto_impersonate_account(enabled);
Ok(()).into_boxed_future()
Expand Down
9 changes: 5 additions & 4 deletions src/node/evm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use zksync_types::{Address, U256, U64};
use zksync_web3_decl::error::Web3Error;

use crate::utils::Numeric;
use crate::{
fork::ForkSource,
namespaces::{EvmNamespaceT, RpcResult},
Expand All @@ -11,7 +12,7 @@ use crate::{
impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EvmNamespaceT
for InMemoryNode<S>
{
fn increase_time(&self, time_delta_seconds: u64) -> RpcResult<u64> {
fn increase_time(&self, time_delta_seconds: Numeric) -> RpcResult<u64> {
self.increase_time(time_delta_seconds)
.map_err(|err| {
tracing::error!("failed increasing time: {:?}", err);
Expand All @@ -38,7 +39,7 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EvmNamespa
.into_boxed_future()
}

fn set_next_block_timestamp(&self, timestamp: U64) -> RpcResult<U64> {
fn set_next_block_timestamp(&self, timestamp: Numeric) -> RpcResult<()> {
self.set_next_block_timestamp(timestamp)
.map_err(|err| {
tracing::error!("failed setting time for next timestamp: {:?}", err);
Expand All @@ -47,8 +48,8 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EvmNamespa
.into_boxed_future()
}

fn set_time(&self, time: u64) -> RpcResult<i128> {
self.set_time(time)
fn set_time(&self, timestamp: Numeric) -> RpcResult<i128> {
self.set_time(timestamp)
.map_err(|err| {
tracing::error!("failed setting time: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
Expand Down
Loading

0 comments on commit 9818ee5

Please sign in to comment.