diff --git a/SUPPORTED_APIS.md b/SUPPORTED_APIS.md
index 7110dcdd..a76d569d 100644
--- a/SUPPORTED_APIS.md
+++ b/SUPPORTED_APIS.md
@@ -14,6 +14,8 @@ The `status` options are:
| Namespace | API |
Status
| 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 |
diff --git a/e2e-tests/test/anvil-apis.test.ts b/e2e-tests/test/anvil-apis.test.ts
index 72d24148..648ff80b 100644
--- a/e2e-tests/test/anvil-apis.test.ts
+++ b/e2e-tests/test/anvil-apis.test.ts
@@ -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
diff --git a/src/namespaces/anvil.rs b/src/namespaces/anvil.rs
index 4cf69ee9..39c802a0 100644
--- a/src/namespaces/anvil.rs
+++ b/src/namespaces/anvil.rs
@@ -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;
+
+ /// 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;
+
/// 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.
diff --git a/src/node/anvil.rs b/src/node/anvil.rs
index c8f473bb..a3251f5a 100644
--- a/src/node/anvil.rs
+++ b/src/node/anvil.rs
@@ -12,6 +12,24 @@ use crate::{
impl AnvilNamespaceT
for InMemoryNode
{
+ fn snapshot(&self) -> RpcResult {
+ 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 {
+ 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 {
self.set_time(timestamp)
.map_err(|err| {