diff --git a/SUPPORTED_APIS.md b/SUPPORTED_APIS.md
index 905d4f0e..1b4878e2 100644
--- a/SUPPORTED_APIS.md
+++ b/SUPPORTED_APIS.md
@@ -14,6 +14,12 @@ The `status` options are:
| Namespace | API |
Status
| Description |
| --- | --- | --- | --- |
+| `ANVIL` | `anvil_dropTransaction` | `SUPPORTED` | Removes a transaction from the pool |
+| `ANVIL` | `anvil_dropAllTransactions` | `SUPPORTED` | Remove all transactions from the pool |
+| `ANVIL` | `anvil_removePoolTransactions` | `SUPPORTED` | Remove all transactions from the pool by sender address |
+| `ANVIL` | `anvil_getAutomine` | `SUPPORTED` | Get node's auto mining status |
+| `ANVIL` | `anvil_setAutomine` | `SUPPORTED` | Enable or disables auto mining of new blocks |
+| `ANVIL` | `anvil_setIntervalMining` | `SUPPORTED` | Set the mining behavior to interval with the given interval |
| `ANVIL` | `anvil_setBlockTimestampInterval` | `SUPPORTED` | Sets the block timestamp interval |
| `ANVIL` | `anvil_removeBlockTimestampInterval` | `SUPPORTED` | Removes the block timestamp interval |
| `ANVIL` | `anvil_setMinGasPrice` | `NOT IMPLEMENTED` | Set the minimum gas price for the node. Unsupported for ZKsync as it is only relevant for pre-EIP1559 chains |
diff --git a/e2e-tests-rust/src/lib.rs b/e2e-tests-rust/src/lib.rs
index 7ee02df6..94fd15ff 100644
--- a/e2e-tests-rust/src/lib.rs
+++ b/e2e-tests-rust/src/lib.rs
@@ -1,3 +1,4 @@
+use alloy::primitives::{Address, TxHash};
use alloy::providers::{Provider, ProviderCall};
use alloy::rpc::client::NoParams;
use alloy::transports::Transport;
@@ -22,6 +23,24 @@ where
.request("anvil_setIntervalMining", (seconds,))
.into()
}
+
+ fn drop_transaction(&self, hash: TxHash) -> ProviderCall> {
+ self.client()
+ .request("anvil_dropTransaction", (hash,))
+ .into()
+ }
+
+ fn drop_all_transactions(&self) -> ProviderCall {
+ self.client()
+ .request_noparams("anvil_dropAllTransactions")
+ .into()
+ }
+
+ fn remove_pool_transactions(&self, address: Address) -> ProviderCall {
+ self.client()
+ .request("anvil_removePoolTransactions", (address,))
+ .into()
+ }
}
impl
EraTestNodeApiProvider for P
diff --git a/e2e-tests-rust/tests/lib.rs b/e2e-tests-rust/tests/lib.rs
index 1563e06b..dea3e988 100644
--- a/e2e-tests-rust/tests/lib.rs
+++ b/e2e-tests-rust/tests/lib.rs
@@ -176,3 +176,102 @@ async fn dynamic_sealing_mode() -> anyhow::Result<()> {
Ok(())
}
+
+#[tokio::test]
+async fn drop_transaction() -> anyhow::Result<()> {
+ // Test that we can submit two transactions and then remove one from the pool before it gets
+ // finalized. 3 seconds should be long enough for the entire flow to execute before the first
+ // block is produced.
+ let provider = init(|node| node.block_time(3)).await?;
+
+ // Submit two transactions
+ let tx0 = TransactionRequest::default()
+ .with_from(RICH_WALLET0)
+ .with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"))
+ .with_value(U256::from(100));
+ let pending_tx0 = provider.send_transaction(tx0).await?.register().await?;
+ let tx1 = TransactionRequest::default()
+ .with_from(RICH_WALLET1)
+ .with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"))
+ .with_value(U256::from(100));
+ let pending_tx1 = provider.send_transaction(tx1).await?.register().await?;
+
+ // Drop first
+ provider.drop_transaction(*pending_tx0.tx_hash()).await?;
+
+ // Assert first never gets finalized but the second one does
+ let finalization_result = tokio::time::timeout(Duration::from_secs(4), pending_tx0).await;
+ assert!(finalization_result.is_err());
+ let receipt = provider
+ .get_transaction_receipt(pending_tx1.await?)
+ .await?
+ .unwrap();
+ assert!(receipt.status());
+
+ Ok(())
+}
+
+#[tokio::test]
+async fn drop_all_transactions() -> anyhow::Result<()> {
+ // Test that we can submit two transactions and then remove them from the pool before the get
+ // finalized. 3 seconds should be long enough for the entire flow to execute before the first
+ // block is produced.
+ let provider = init(|node| node.block_time(3)).await?;
+
+ // Submit two transactions
+ let tx0 = TransactionRequest::default()
+ .with_from(RICH_WALLET0)
+ .with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"))
+ .with_value(U256::from(100));
+ let pending_tx0 = provider.send_transaction(tx0).await?.register().await?;
+ let tx1 = TransactionRequest::default()
+ .with_from(RICH_WALLET1)
+ .with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"))
+ .with_value(U256::from(100));
+ let pending_tx1 = provider.send_transaction(tx1).await?.register().await?;
+
+ // Drop all transactions
+ provider.drop_all_transactions().await?;
+
+ // Neither transaction gets finalized
+ let finalization_result = tokio::time::timeout(Duration::from_secs(4), pending_tx0).await;
+ assert!(finalization_result.is_err());
+ let finalization_result = tokio::time::timeout(Duration::from_secs(4), pending_tx1).await;
+ assert!(finalization_result.is_err());
+
+ Ok(())
+}
+
+#[tokio::test]
+async fn remove_pool_transactions() -> anyhow::Result<()> {
+ // Test that we can submit two transactions from two senders and then remove first sender's
+ // transaction from the pool before it gets finalized. 3 seconds should be long enough for the
+ // entire flow to execute before the first block is produced.
+ let provider = init(|node| node.block_time(3)).await?;
+
+ // Submit two transactions
+ let tx0 = TransactionRequest::default()
+ .with_from(RICH_WALLET0)
+ .with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"))
+ .with_value(U256::from(100));
+ let pending_tx0 = provider.send_transaction(tx0).await?.register().await?;
+ let tx1 = TransactionRequest::default()
+ .with_from(RICH_WALLET1)
+ .with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"))
+ .with_value(U256::from(100));
+ let pending_tx1 = provider.send_transaction(tx1).await?.register().await?;
+
+ // Drop first
+ provider.remove_pool_transactions(RICH_WALLET0).await?;
+
+ // Assert first never gets finalized but the second one does
+ let finalization_result = tokio::time::timeout(Duration::from_secs(4), pending_tx0).await;
+ assert!(finalization_result.is_err());
+ let receipt = provider
+ .get_transaction_receipt(pending_tx1.await?)
+ .await?
+ .unwrap();
+ assert!(receipt.status());
+
+ Ok(())
+}
diff --git a/spec-tests/Cargo.lock b/spec-tests/Cargo.lock
index ed8edc00..c4ad9b70 100644
--- a/spec-tests/Cargo.lock
+++ b/spec-tests/Cargo.lock
@@ -4530,7 +4530,7 @@ dependencies = [
[[package]]
name = "zksync_basic_types"
version = "0.1.0"
-source = "git+https://github.com/matter-labs/zksync-era.git?rev=ad5d42e0c7b50068ff0fd501c2b2ee549410adf8#ad5d42e0c7b50068ff0fd501c2b2ee549410adf8"
+source = "git+https://github.com/matter-labs/zksync-era.git?rev=6c034f6e180cc92e99766f14c8840c90efa56cec#6c034f6e180cc92e99766f14c8840c90efa56cec"
dependencies = [
"anyhow",
"chrono",
diff --git a/spec-tests/Cargo.toml b/spec-tests/Cargo.toml
index db35a622..d486a236 100644
--- a/spec-tests/Cargo.toml
+++ b/spec-tests/Cargo.toml
@@ -24,7 +24,7 @@ itertools = "0.13"
tracing = "0.1"
chrono = "0.4"
-zksync_basic_types = { git = "https://github.com/matter-labs/zksync-era.git", rev = "ad5d42e0c7b50068ff0fd501c2b2ee549410adf8" }
+zksync_basic_types = { git = "https://github.com/matter-labs/zksync-era.git", rev = "6c034f6e180cc92e99766f14c8840c90efa56cec" }
[dev-dependencies]
test-log = "0.2.16"
diff --git a/src/namespaces/anvil.rs b/src/namespaces/anvil.rs
index b55f1c7c..ec94c25c 100644
--- a/src/namespaces/anvil.rs
+++ b/src/namespaces/anvil.rs
@@ -1,11 +1,34 @@
use jsonrpc_derive::rpc;
-use zksync_types::{Address, U256, U64};
+use zksync_types::{Address, H256, U256, U64};
use super::{ResetRequest, RpcResult};
use crate::utils::Numeric;
#[rpc]
pub trait AnvilNamespaceT {
+ /// Removes a transaction from the pool.
+ ///
+ /// # Arguments
+ ///
+ /// * `hash` - Hash of the transaction to be removed from the pool
+ ///
+ /// # Returns
+ /// `Some(hash)` if transaction was in the pool before being removed, `None` otherwise
+ #[rpc(name = "anvil_dropTransaction")]
+ fn drop_transaction(&self, hash: H256) -> RpcResult