diff --git a/SUPPORTED_APIS.md b/SUPPORTED_APIS.md index c3bec4c3..1969a403 100644 --- a/SUPPORTED_APIS.md +++ b/SUPPORTED_APIS.md @@ -102,7 +102,7 @@ The `status` options are: | `HARDHAT` | `hardhat_setMinGasPrice` | `NOT IMPLEMENTED` | Sets the minimum gas price | | `HARDHAT` | `hardhat_setNextBlockBaseFeePerGas` | `NOT IMPLEMENTED` | Sets the base fee per gas for the next block | | `HARDHAT` | `hardhat_setPrevRandao` | `NOT IMPLEMENTED` | Sets the PREVRANDAO value of the next block | -| `HARDHAT` | `hardhat_setNonce` | `NOT IMPLEMENTED`
[GitHub Issue #77](https://github.com/matter-labs/era-test-node/issues/77) | Sets the nonce of a given account | +| [`HARDHAT`](#hardhat-namespace) | [`hardhat_setNonce`](#hardhat_setnonce) | `SUPPORTED` | Sets the nonce of a given account | | `HARDHAT` | `hardhat_setStorageAt` | `NOT IMPLEMENTED` | Sets the storage value at a given key for a given account | | `HARDHAT` | `hardhat_stopImpersonatingAccount` | `NOT IMPLEMENTED`
[GitHub Issue #74](https://github.com/matter-labs/era-test-node/issues/74) | Stop impersonating an account after having previously used `hardhat_impersonateAccount` | | [`NETWORK`](#network-namespace) | [`net_version`](#net_version) | `SUPPORTED` | Returns the current network id
_(default is `260`)_ | @@ -717,6 +717,39 @@ curl --request POST \ }' ``` +### `hardhat_setNonce` + +[source](src/hardhat.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 + +#### Status + +`SUPPORTED` + +#### Example + +```bash +curl --request POST \ + --url http://localhost:8011/ \ + --header 'content-type: application/json' \ + --data '{ + "jsonrpc": "2.0", + "id": "1", + "method": "hardhat_setNonce", + "params": [ + "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + "0x1337" + ] + }' +``` + ## `ZKS NAMESPACE` ### `zks_estimateFee` diff --git a/src/hardhat.rs b/src/hardhat.rs index 0cb206f9..83b9aecf 100644 --- a/src/hardhat.rs +++ b/src/hardhat.rs @@ -5,8 +5,12 @@ use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; use zksync_basic_types::{Address, U256}; use zksync_core::api_server::web3::backend_jsonrpc::error::into_jsrpc_error; -use zksync_types::utils::storage_key_for_eth_balance; -use zksync_utils::u256_to_h256; +use zksync_state::ReadStorage; +use zksync_types::{ + get_nonce_key, + utils::{decompose_full_nonce, nonces_to_full_nonce, storage_key_for_eth_balance}, +}; +use zksync_utils::{h256_to_u256, u256_to_h256}; use zksync_web3_decl::error::Web3Error; /// Implementation of HardhatNamespaceImpl @@ -35,6 +39,19 @@ pub trait HardhatNamespaceT { /// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation. #[rpc(name = "hardhat_setBalance")] fn set_balance(&self, address: Address, balance: U256) -> BoxFuture>; + + /// 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 = "hardhat_setNonce")] + fn set_nonce(&self, address: Address, balance: U256) -> BoxFuture>; } impl HardhatNamespaceT @@ -67,6 +84,48 @@ impl HardhatNamespaceT } }) } + + fn set_nonce( + &self, + address: Address, + nonce: U256, + ) -> jsonrpc_core::BoxFuture> { + let inner = Arc::clone(&self.node); + Box::pin(async move { + match inner.write() { + Ok(mut inner_guard) => { + let nonce_key = get_nonce_key(&address); + let full_nonce = inner_guard.fork_storage.read_value(&nonce_key); + let (mut account_nonce, mut deployment_nonce) = + decompose_full_nonce(h256_to_u256(full_nonce)); + if account_nonce >= nonce { + return Err(jsonrpc_core::Error::invalid_params(format!( + "Account Nonce is already set to a higher value ({}, requested {})", + account_nonce, nonce + ))); + } + account_nonce = nonce; + if deployment_nonce >= nonce { + return Err(jsonrpc_core::Error::invalid_params(format!( + "Deployment Nonce is already set to a higher value ({}, requested {})", + deployment_nonce, nonce + ))); + } + deployment_nonce = nonce; + let enforced_full_nonce = nonces_to_full_nonce(account_nonce, deployment_nonce); + println!( + "👷 Nonces for address {:?} have been set to {}", + address, nonce + ); + inner_guard + .fork_storage + .set_value(nonce_key, u256_to_h256(enforced_full_nonce)); + Ok(true) + } + Err(_) => Err(into_jsrpc_error(Web3Error::InternalError)), + } + }) + } } #[cfg(test)] @@ -94,4 +153,24 @@ mod tests { assert_eq!(balance_after, U256::from(1337)); assert_ne!(balance_before, balance_after); } + + #[tokio::test] + async fn test_set_nonce() { + let address = Address::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap(); + let node = InMemoryNode::::default(); + let hardhat = HardhatNamespaceImpl::new(node.get_inner()); + + let nonce_before = node.get_transaction_count(address, None).await.unwrap(); + + let result = hardhat.set_nonce(address, U256::from(1337)).await.unwrap(); + assert!(result); + + let nonce_after = node.get_transaction_count(address, None).await.unwrap(); + assert_eq!(nonce_after, U256::from(1337)); + assert_ne!(nonce_before, nonce_after); + + // setting nonce lower than the current one should fail + let result = hardhat.set_nonce(address, U256::from(1336)).await; + assert!(result.is_err()); + } } diff --git a/test_endpoints.http b/test_endpoints.http index 2d2572dc..584afa46 100644 --- a/test_endpoints.http +++ b/test_endpoints.http @@ -279,6 +279,20 @@ content-type: application/json POST http://localhost:8011 content-type: application/json +{ + "jsonrpc": "2.0", + "id": "2", + "method": "hardhat_setNonce", + "params": [ + "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", + "0x56" + ] +} + +### +POST http://localhost:8011 +content-type: application/json + { "jsonrpc": "2.0", "id": "1",