diff --git a/SUPPORTED_APIS.md b/SUPPORTED_APIS.md
index 62397aa2..024f30d0 100644
--- a/SUPPORTED_APIS.md
+++ b/SUPPORTED_APIS.md
@@ -121,7 +121,7 @@ The `status` options are:
| `ZKS` | `zks_getL2ToL1LogProof` | `NOT IMPLEMENTED` | Given a transaction hash, and an index of the L2 to L1 log produced within the transaction, it returns the proof for the corresponding L2 to L1 log |
| `ZKS` | `zks_getL2ToL1MsgProof` | `NOT IMPLEMENTED` | Given a block, a sender, a message, and an optional message log index in the block containing the L1->L2 message, it returns the proof for the message sent via the L1Messenger system contract |
| `ZKS` | `zks_getMainContract` | `NOT IMPLEMENTED` | Returns the address of the zkSync Era contract |
-| `ZKS` | `zks_getRawBlockTransactions` | `NOT IMPLEMENTED` | Returns data of transactions in a block |
+| [`ZKS`](#zks-namespace) | [`zks_getRawBlockTransactions`](#zks_getrawblocktransactions) | `SUPPORTED` | Returns data of transactions in a block |
| `ZKS` | `zks_getTestnetPaymaster` | `NOT IMPLEMENTED` | Returns the address of the testnet paymaster |
| [`ZKS`](#zks-namespace) | [`zks_getTokenPrice`](#zks_getTokenPrice) | `SUPPORTED` | Gets the USD price of a token
_(`ETH` is hard-coded to `1_500`, while some others are `1`)_ |
| [`ZKS`](#zks-namespace) | [`zks_getTransactionDetails`](#zks_gettransactiondetails) | `SUPPORTED` | Returns data from a specific transaction given by the transaction hash |
@@ -1791,4 +1791,27 @@ curl --request POST \
"id": "2",
"method": "zks_getBridgeContracts"
}'
-```
\ No newline at end of file
+```
+
+### `zks_getRawBlockTransactions`
+
+[source](src/zks.rs)
+
+Returns data of transactions in a block.
+
+#### Arguments
+
++ `block: u32` - The number of the block
+
+#### Status
+
+`SUPPORTED`
+
+#### Example
+
+```bash
+curl --request POST \
+ --url http://localhost:8011/ \
+ --header 'content-type: application/json' \
+ --data '{"jsonrpc": "2.0", "id": 1, "method": "zks_getRawBlockTransactions", "params": [ 140599 ]}'
+```
diff --git a/e2e-tests/test/zks-apis.test.ts b/e2e-tests/test/zks-apis.test.ts
index eef4adcb..35770486 100644
--- a/e2e-tests/test/zks-apis.test.ts
+++ b/e2e-tests/test/zks-apis.test.ts
@@ -136,3 +136,19 @@ describe("zks_getBytecodeByHash", function () {
expect(ethers.utils.hexlify(bytecode)).to.equal(artifact.deployedBytecode);
});
});
+
+describe("zks_getRawBlockTransactions", function () {
+ it("Should return transactions for locally-produced blocks", async function () {
+ const wallet = new Wallet(RichAccounts[0].PrivateKey);
+ const deployer = new Deployer(hre, wallet);
+
+ const greeter = await deployContract(deployer, "Greeter", ["Hi"]);
+ const receipt = await greeter.setGreeting("Luke Skywalker");
+
+ const latestBlock = await provider.getBlock("latest");
+ const txns = await provider.send("zks_getRawBlockTransactions", [latestBlock.number - 1]);
+
+ expect(txns.length).to.equal(1);
+ expect(txns[0]["execute"]["calldata"]).to.equal(receipt.data);
+ });
+});
diff --git a/src/zks.rs b/src/zks.rs
index 9fc2bd4f..96606432 100644
--- a/src/zks.rs
+++ b/src/zks.rs
@@ -4,16 +4,17 @@ use bigdecimal::BigDecimal;
use futures::FutureExt;
use zksync_basic_types::{Address, L1BatchNumber, MiniblockNumber, U256};
use zksync_core::api_server::web3::backend_jsonrpc::{
- error::into_jsrpc_error, namespaces::zks::ZksNamespaceT,
+ error::{internal_error, into_jsrpc_error},
+ namespaces::zks::ZksNamespaceT,
};
use zksync_state::ReadStorage;
use zksync_types::{
api::{
BlockDetails, BlockDetailsBase, BlockStatus, BridgeAddresses, ProtocolVersion,
- TransactionDetails, TransactionStatus,
+ TransactionDetails, TransactionStatus, TransactionVariant,
},
fee::Fee,
- ProtocolVersionId,
+ ExecuteTransactionCommon, ProtocolVersionId, Transaction,
};
use zksync_web3_decl::{
error::Web3Error,
@@ -69,11 +70,74 @@ impl ZksNamespaceT
}
}
+ /// Returns data of transactions in a block.
+ ///
+ /// # Arguments
+ ///
+ /// * `block` - Block number
+ ///
+ /// # Returns
+ ///
+ /// A `BoxFuture` containing a `Result` with a `Vec` of `Transaction`s representing the transactions in the block.
fn get_raw_block_transactions(
&self,
- _block_number: MiniblockNumber,
+ block_number: MiniblockNumber,
) -> jsonrpc_core::BoxFuture>> {
- not_implemented("zks_getRawBlockTransactions")
+ let inner = self.node.clone();
+ Box::pin(async move {
+ let reader = inner
+ .read()
+ .map_err(|_err| into_jsrpc_error(Web3Error::InternalError))?;
+
+ let maybe_transactions = reader
+ .block_hashes
+ .get(&(block_number.0 as u64))
+ .and_then(|hash| reader.blocks.get(hash))
+ .map(|block| {
+ block
+ .transactions
+ .iter()
+ .map(|tx| match tx {
+ TransactionVariant::Full(tx) => &tx.hash,
+ TransactionVariant::Hash(hash) => hash,
+ })
+ .flat_map(|tx_hash| {
+ reader.tx_results.get(tx_hash).map(
+ |TransactionResult { info, .. }| Transaction {
+ common_data: ExecuteTransactionCommon::L2(
+ info.tx.common_data.clone(),
+ ),
+ execute: info.tx.execute.clone(),
+ received_timestamp_ms: info.tx.received_timestamp_ms,
+ raw_bytes: info.tx.raw_bytes.clone(),
+ },
+ )
+ })
+ .collect()
+ });
+
+ let transactions = match maybe_transactions {
+ Some(txns) => Ok(txns),
+ None => {
+ let fork_storage_read = reader
+ .fork_storage
+ .inner
+ .read()
+ .expect("failed reading fork storage");
+
+ match fork_storage_read.fork.as_ref() {
+ Some(fork) => fork
+ .fork_source
+ .get_raw_block_transactions(block_number)
+ .map_err(|e| internal_error("get_raw_block_transactions", e)),
+ None => Ok(vec![]),
+ }
+ }
+ }
+ .map_err(into_jsrpc_error)?;
+
+ Ok(transactions)
+ })
}
fn estimate_gas_l1_to_l2(
@@ -442,7 +506,7 @@ mod tests {
use super::*;
use zksync_basic_types::{Address, H160, H256};
- use zksync_types::api::{Block, TransactionReceipt, TransactionVariant};
+ use zksync_types::api::{self, Block, TransactionReceipt, TransactionVariant};
use zksync_types::transaction_request::CallRequest;
#[tokio::test]
@@ -865,4 +929,129 @@ mod tests {
// Assert
assert_eq!(input_bytecode, actual);
}
+
+ #[tokio::test]
+ async fn test_get_raw_block_transactions_local() {
+ // Arrange
+ let node = InMemoryNode::::default();
+ let namespace = ZkMockNamespaceImpl::new(node.get_inner());
+ let inner = node.get_inner();
+ {
+ let mut writer = inner.write().unwrap();
+ let mut block = Block::::default();
+ let txn = api::Transaction::default();
+ writer.tx_results.insert(
+ txn.hash,
+ TransactionResult {
+ info: testing::default_tx_execution_info(),
+ receipt: TransactionReceipt {
+ logs: vec![],
+ gas_used: Some(U256::from(10_000)),
+ effective_gas_price: Some(U256::from(1_000_000_000)),
+ ..Default::default()
+ },
+ debug: testing::default_tx_debug_info(),
+ },
+ );
+ block.transactions.push(TransactionVariant::Full(txn));
+ writer.blocks.insert(H256::repeat_byte(0x1), block);
+ writer.block_hashes.insert(0, H256::repeat_byte(0x1));
+ }
+
+ // Act
+ let txns = namespace
+ .get_raw_block_transactions(MiniblockNumber(0))
+ .await
+ .expect("get transaction details");
+
+ // Assert
+ assert_eq!(txns.len(), 1);
+ }
+
+ #[tokio::test]
+ async fn test_get_raw_block_transactions_fork() {
+ let mock_server = MockServer::run_with_config(ForkBlockConfig {
+ number: 10,
+ transaction_count: 0,
+ hash: H256::repeat_byte(0xab),
+ });
+ let miniblock = MiniblockNumber::from(16474138);
+ mock_server.expect(
+ serde_json::json!({
+ "jsonrpc": "2.0",
+ "id": 0,
+ "method": "zks_getRawBlockTransactions",
+ "params": [miniblock.0]
+ }),
+ serde_json::json!({
+ "jsonrpc": "2.0",
+ "result": [
+ {
+ "common_data": {
+ "L2": {
+ "nonce": 86,
+ "fee": {
+ "gas_limit": "0xcc626",
+ "max_fee_per_gas": "0x141dd760",
+ "max_priority_fee_per_gas": "0x0",
+ "gas_per_pubdata_limit": "0x4e20"
+ },
+ "initiatorAddress": "0x840bd73f903ba7dbb501be8326fe521dadcae1a5",
+ "signature": [
+ 135,
+ 163,
+ 2,
+ 78,
+ 118,
+ 14,
+ 209
+ ],
+ "transactionType": "EIP1559Transaction",
+ "input": {
+ "hash": "0xc1f625f55d186ad0b439054adfe3317ae703c5f588f4fa1896215e8810a141e0",
+ "data": [
+ 2,
+ 249,
+ 1,
+ 110,
+ 130
+ ]
+ },
+ "paymasterParams": {
+ "paymaster": "0x0000000000000000000000000000000000000000",
+ "paymasterInput": []
+ }
+ }
+ },
+ "execute": {
+ "contractAddress": "0xbe7d1fd1f6748bbdefc4fbacafbb11c6fc506d1d",
+ "calldata": "0x38ed173900000000000000000000000000000000000000000000000000000000002c34cc00000000000000000000000000000000000000000000000000000000002c9a2500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000840bd73f903ba7dbb501be8326fe521dadcae1a500000000000000000000000000000000000000000000000000000000652c5d1900000000000000000000000000000000000000000000000000000000000000020000000000000000000000008e86e46278518efc1c5ced245cba2c7e3ef115570000000000000000000000003355df6d4c9c3035724fd0e3914de96a5a83aaf4",
+ "value": "0x0",
+ "factoryDeps": null
+ },
+ "received_timestamp_ms": 1697405097873u64,
+ "raw_bytes": "0x02f9016e820144568084141dd760830cc62694be7d1fd1f6748bbdefc4fbacafbb11c6fc506d1d80b9010438ed173900000000000000000000000000000000000000000000000000000000002c34cc00000000000000000000000000000000000000000000000000000000002c9a2500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000840bd73f903ba7dbb501be8326fe521dadcae1a500000000000000000000000000000000000000000000000000000000652c5d1900000000000000000000000000000000000000000000000000000000000000020000000000000000000000008e86e46278518efc1c5ced245cba2c7e3ef115570000000000000000000000003355df6d4c9c3035724fd0e3914de96a5a83aaf4c080a087a3024e760ed14134ef541608bf308e083c899a89dba3c02bf3040f07c8b91b9fc3a7eeb6b3b8b36bb03ea4352415e7815dda4954f4898d255bd7660736285e"
+ }
+ ],
+ "id": 0
+ }),
+ );
+
+ let node = InMemoryNode::::new(
+ Some(ForkDetails::from_network(&mock_server.url(), None, CacheConfig::None).await),
+ crate::node::ShowCalls::None,
+ ShowStorageLogs::None,
+ ShowVMDetails::None,
+ ShowGasDetails::None,
+ false,
+ &system_contracts::Options::BuiltIn,
+ );
+
+ let namespace = ZkMockNamespaceImpl::new(node.get_inner());
+ let txns = namespace
+ .get_raw_block_transactions(miniblock)
+ .await
+ .expect("get transaction details");
+ assert_eq!(txns.len(), 1);
+ }
}
diff --git a/test_endpoints.http b/test_endpoints.http
index 90ef7e79..d7554850 100644
--- a/test_endpoints.http
+++ b/test_endpoints.http
@@ -718,6 +718,17 @@ content-type: application/json
POST http://localhost:8011
content-type: application/json
+{
+ "jsonrpc": "2.0",
+ "id": "1",
+ "method": "zks_getRawBlockTransactions",
+ "params": [16474138]
+}
+
+###
+POST http://localhost:8011
+content-type: application/json
+
{
"jsonrpc": "2.0",
"id": "1",