Skip to content

Commit

Permalink
feat: add anvil_snapshot/anvil_revert (#432)
Browse files Browse the repository at this point in the history
  • Loading branch information
itegulov authored Nov 26, 2024
1 parent 76bdf23 commit ede79a3
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 0 deletions.
2 changes: 2 additions & 0 deletions SUPPORTED_APIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ The `status` options are:

| Namespace | API | <div style="width:130px">Status</div> | Description |
| --- | --- | --- | --- |
| `ANVIL` | `anvil_snapshot` | `SUPPORTED` | Snapshot the state of the blockchain at the current block |
| `ANVIL` | `anvil_revert` | `SUPPORTED` | Revert the state of the blockchain to a previous snapshot |
| `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 |
Expand Down
37 changes: 37 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,43 @@ import { BigNumber } from "ethers";

const provider = getTestProvider();

describe("anvil_snapshot", function () {
it("Should return incrementing snapshot ids", async function () {
const wallet = new Wallet(RichAccounts[6].PrivateKey);
const deployer = new Deployer(hre, wallet);
const greeter = await deployContract(deployer, "Greeter", ["Hi"]);
expect(await greeter.greet()).to.eq("Hi");

// Act
const snapshotId1: string = await provider.send("anvil_snapshot", []);
const snapshotId2: string = await provider.send("anvil_snapshot", []);

// Assert
expect(await greeter.greet()).to.eq("Hi");
expect(BigNumber.from(snapshotId2).toString()).to.eq(BigNumber.from(snapshotId1).add(1).toString());
});
});

describe("anvil_revert", function () {
it("Should revert with correct snapshot id", async function () {
const wallet = new Wallet(RichAccounts[6].PrivateKey);
const deployer = new Deployer(hre, wallet);
const greeter = await deployContract(deployer, "Greeter", ["Hi"]);
expect(await greeter.greet()).to.eq("Hi");
const snapshotId = await provider.send("anvil_snapshot", []);
const setGreetingTx = await greeter.setGreeting("Hola, mundo!");
await setGreetingTx.wait();
expect(await greeter.greet()).to.equal("Hola, mundo!");

// Act
const reverted: boolean = await provider.send("anvil_revert", [snapshotId]);

// Assert
expect(await greeter.greet()).to.eq("Hi");
expect(reverted).to.be.true;
});
});

describe("anvil_increaseTime", function () {
it("Should increase current timestamp of the node", async function () {
// Arrange
Expand Down
23 changes: 23 additions & 0 deletions src/namespaces/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@ use crate::utils::Numeric;

#[rpc]
pub trait AnvilNamespaceT {
/// 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 `anvil_revert`, the same snapshot id cannot
/// be used again. Consider creating a new snapshot after each `anvil_revert` if you need to revert to the same
/// point multiple times.
///
/// # Returns
/// The `U64` identifier for this snapshot.
#[rpc(name = "anvil_snapshot")]
fn snapshot(&self) -> RpcResult<U64>;

/// Revert the state of the blockchain to a previous snapshot. Takes a single parameter,
/// which is the snapshot id to revert to. This deletes the given snapshot, as well as any snapshots
/// taken after (e.g.: reverting to id 0x1 will delete snapshots with ids 0x1, 0x2, etc.)
///
/// # Arguments
///
/// * `id` - The snapshot id to revert
///
/// # Returns
/// `true` if a snapshot was reverted, otherwise `false`.
#[rpc(name = "anvil_revert")]
fn revert(&self, id: U64) -> RpcResult<bool>;

/// 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.
Expand Down
18 changes: 18 additions & 0 deletions src/node/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ use crate::{
impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> AnvilNamespaceT
for InMemoryNode<S>
{
fn snapshot(&self) -> RpcResult<U64> {
self.snapshot()
.map_err(|err| {
tracing::error!("failed creating snapshot: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn revert(&self, id: U64) -> RpcResult<bool> {
self.revert_snapshot(id)
.map_err(|err| {
tracing::error!("failed reverting snapshot: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn set_time(&self, timestamp: Numeric) -> RpcResult<i128> {
self.set_time(timestamp)
.map_err(|err| {
Expand Down

0 comments on commit ede79a3

Please sign in to comment.