From d1d20e8d49454b61907abb668f64c8197de8ea17 Mon Sep 17 00:00:00 2001 From: Will Cory Date: Mon, 1 Jul 2024 19:56:06 -0700 Subject: [PATCH] :test_tube: Test: Add more procedures tests (#1247) ## Description _Concise description of proposed changes_ ## Testing Explain the quality checks that have been done on the code changes ## Additional Information - [ ] I read the [contributing docs](../docs/contributing.md) (if this is your first contribution) Your ENS/address: ## Summary by CodeRabbit - **New Features** - Introduced `getBlockFromRpc` function in the blockchain package, enabling retrieval of blocks from an RPC server. - **Documentation** - Updated blockchain package documentation to include details on the new `getBlockFromRpc` function. --------- Co-authored-by: William Cory --- .../docs/functions/getBlockFromRpc.md | 29 ++ packages/blockchain/docs/globals.md | 1 + packages/blockchain/src/index.ts | 1 + packages/procedures/package.json | 3 + .../__snapshots__/createHandlers.spec.ts.snap | 137 +++++ .../__snapshots__/callProcedure.spec.ts.snap | 53 ++ .../procedures/src/call/callProcedure.spec.ts | 105 ++++ .../procedures/src/createHandlers.spec.ts | 142 ++++++ .../dumpStateProcedure.spec.ts.snap | 95 ++++ .../src/dumpstate/dumpStateProcedure.spec.ts | 40 ++ .../blockNumberProcedure.spec.ts.snap | 5 + .../chainIdProcedure.spec.ts.snap | 5 + .../ethBlobBaseFeeProcedure.spec.ts.snap | 7 + .../ethCallProcedure.spec.ts.snap | 37 ++ .../ethCoinbaseProcedure.spec.ts.snap | 5 + .../ethEstimateGasProcedure.spec.ts.snap | 27 + .../ethGetBlockByHashProcedure.spec.ts.snap | 124 +++++ .../ethGetBlockByNumberProcedure.spec.ts.snap | 14 + ...ansactionCountByHashProcedure.spec.ts.snap | 5 + .../ethGetFilterChangesProcedure.spec.ts.snap | 40 ++ .../gasPriceProcedure.spec.ts.snap | 5 + .../getBalanceProcedure.spec.ts.snap | 3 + .../src/eth/blockNumberProcedure.spec.ts | 43 ++ .../src/eth/chainIdProcedure.spec.ts | 43 ++ .../src/eth/ethBlobBaseFeeProcedure.spec.ts | 58 +++ .../src/eth/ethCallProcedure.spec.ts | 79 +++ .../src/eth/ethCoinbaseProcedure.spec.ts | 43 ++ .../src/eth/ethEstimateGasProcedure.spec.ts | 77 +++ .../eth/ethGetBlockByHashProcedure.spec.ts | 81 +++ .../eth/ethGetBlockByNumberProcedure.spec.ts | 75 +++ ...ockTransactionCountByHashProcedure.spec.ts | 111 ++++ ...kTransactionCountByNumberProcedure.spec.ts | 122 +++++ .../eth/ethGetFilterChangesProcedure.spec.ts | 190 +++++++ .../eth/ethUninstallFilterProcedure.spec.ts | 72 +++ .../src/eth/gasPriceProcedure.spec.ts | 51 ++ .../src/eth/getBalanceProcedure.spec.ts | 63 +++ .../src/eth/getCodeProcedure.spec.ts | 84 +++ .../src/eth/getStorageAtProcedure.spec.ts | 84 +++ .../getAccountProcedure.spec.ts.snap | 61 +++ .../getaccount/getAccountProcedure.spec.ts | 106 ++++ .../src/loadstate/loadStateProcedure.spec.ts | 70 +++ .../__snapshots__/mineProcedure.spec.ts.snap | 13 + .../procedures/src/mine/mineProcedure.spec.ts | 71 +++ .../setAccountProcedure.spec.ts.snap | 47 ++ .../setaccount/setAccountProcedure.spec.ts | 110 ++++ .../blockToJsonRpcBlock.spec.ts.snap | 479 ++++++++++++++++++ .../__snapshots__/txToJsonRpcTx.spec.ts.snap | 26 + .../src/utils/blockToJsonRpcBlock.spec.ts | 20 + .../src/utils/generateRandomId.spec.ts | 15 + .../src/utils/parseBlockTag.spec.ts | 43 ++ .../src/utils/txToJsonRpcTx.spec.ts | 32 ++ pnpm-lock.yaml | 15 +- tevm/docs/blockchain/README.md | 1 + .../blockchain/functions/getBlockFromRpc.md | 29 ++ 54 files changed, 3294 insertions(+), 3 deletions(-) create mode 100644 packages/blockchain/docs/functions/getBlockFromRpc.md create mode 100644 packages/procedures/src/__snapshots__/createHandlers.spec.ts.snap create mode 100644 packages/procedures/src/call/__snapshots__/callProcedure.spec.ts.snap create mode 100644 packages/procedures/src/call/callProcedure.spec.ts create mode 100644 packages/procedures/src/createHandlers.spec.ts create mode 100644 packages/procedures/src/dumpstate/__snapshots__/dumpStateProcedure.spec.ts.snap create mode 100644 packages/procedures/src/dumpstate/dumpStateProcedure.spec.ts create mode 100644 packages/procedures/src/eth/__snapshots__/blockNumberProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/chainIdProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/ethBlobBaseFeeProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/ethCallProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/ethCoinbaseProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/ethEstimateGasProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/ethGetBlockByHashProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/ethGetBlockByNumberProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/ethGetBlockTransactionCountByHashProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/ethGetFilterChangesProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/gasPriceProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/__snapshots__/getBalanceProcedure.spec.ts.snap create mode 100644 packages/procedures/src/eth/blockNumberProcedure.spec.ts create mode 100644 packages/procedures/src/eth/chainIdProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethBlobBaseFeeProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethCallProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethCoinbaseProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethEstimateGasProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethGetBlockByHashProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethGetBlockByNumberProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethGetBlockTransactionCountByHashProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethGetBlockTransactionCountByNumberProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethGetFilterChangesProcedure.spec.ts create mode 100644 packages/procedures/src/eth/ethUninstallFilterProcedure.spec.ts create mode 100644 packages/procedures/src/eth/gasPriceProcedure.spec.ts create mode 100644 packages/procedures/src/eth/getBalanceProcedure.spec.ts create mode 100644 packages/procedures/src/eth/getCodeProcedure.spec.ts create mode 100644 packages/procedures/src/eth/getStorageAtProcedure.spec.ts create mode 100644 packages/procedures/src/getaccount/__snapshots__/getAccountProcedure.spec.ts.snap create mode 100644 packages/procedures/src/getaccount/getAccountProcedure.spec.ts create mode 100644 packages/procedures/src/loadstate/loadStateProcedure.spec.ts create mode 100644 packages/procedures/src/mine/__snapshots__/mineProcedure.spec.ts.snap create mode 100644 packages/procedures/src/mine/mineProcedure.spec.ts create mode 100644 packages/procedures/src/setaccount/__snapshots__/setAccountProcedure.spec.ts.snap create mode 100644 packages/procedures/src/setaccount/setAccountProcedure.spec.ts create mode 100644 packages/procedures/src/utils/__snapshots__/blockToJsonRpcBlock.spec.ts.snap create mode 100644 packages/procedures/src/utils/__snapshots__/txToJsonRpcTx.spec.ts.snap create mode 100644 packages/procedures/src/utils/blockToJsonRpcBlock.spec.ts create mode 100644 packages/procedures/src/utils/generateRandomId.spec.ts create mode 100644 packages/procedures/src/utils/parseBlockTag.spec.ts create mode 100644 packages/procedures/src/utils/txToJsonRpcTx.spec.ts create mode 100644 tevm/docs/blockchain/functions/getBlockFromRpc.md diff --git a/packages/blockchain/docs/functions/getBlockFromRpc.md b/packages/blockchain/docs/functions/getBlockFromRpc.md new file mode 100644 index 0000000000..37c6b4edfe --- /dev/null +++ b/packages/blockchain/docs/functions/getBlockFromRpc.md @@ -0,0 +1,29 @@ +[**@tevm/blockchain**](../README.md) • **Docs** + +*** + +[@tevm/blockchain](../globals.md) / getBlockFromRpc + +# Function: getBlockFromRpc() + +> **getBlockFromRpc**(`params`, `common`): `Promise`\<`Block`\> + +## Parameters + +• **params** + +• **params.blockTag**: `undefined` \| `bigint` \| `BlockTag` \| \`0x$\{string\}\` = `'latest'` + +• **params.transport** + +• **params.transport.request**: `EIP1193RequestFn`\<`undefined`\> + +• **common**: `Common` + +## Returns + +`Promise`\<`Block`\> + +## Defined in + +[utils/getBlockFromRpc.js:37](https://github.com/evmts/tevm-monorepo/blob/main/packages/blockchain/src/utils/getBlockFromRpc.js#L37) diff --git a/packages/blockchain/docs/globals.md b/packages/blockchain/docs/globals.md index 4b56352cad..186d98eef2 100644 --- a/packages/blockchain/docs/globals.md +++ b/packages/blockchain/docs/globals.md @@ -16,6 +16,7 @@ - [deepCopy](functions/deepCopy.md) - [delBlock](functions/delBlock.md) - [getBlock](functions/getBlock.md) +- [getBlockFromRpc](functions/getBlockFromRpc.md) - [getCanonicalHeadBlock](functions/getCanonicalHeadBlock.md) - [getIteratorHead](functions/getIteratorHead.md) - [putBlock](functions/putBlock.md) diff --git a/packages/blockchain/src/index.ts b/packages/blockchain/src/index.ts index 84a52631b9..c9ff18120b 100644 --- a/packages/blockchain/src/index.ts +++ b/packages/blockchain/src/index.ts @@ -11,5 +11,6 @@ export { setIteratorHead, getCanonicalHeadBlock, } from './actions/index.js' +export { getBlockFromRpc } from './utils/getBlockFromRpc.js' export type { Chain } from './Chain.js' export type { ChainOptions } from './ChainOptions.js' diff --git a/packages/procedures/package.json b/packages/procedures/package.json index de31dda220..c952f26677 100644 --- a/packages/procedures/package.json +++ b/packages/procedures/package.json @@ -67,11 +67,14 @@ "@tevm/actions": "workspace:^", "@tevm/base-client": "workspace:^", "@tevm/block": "workspace:^", + "@tevm/blockchain": "workspace:^", + "@tevm/common": "workspace:^", "@tevm/contract": "workspace:^", "@tevm/errors": "workspace:^", "@tevm/evm": "workspace:^", "@tevm/jsonrpc": "workspace:^", "@tevm/state": "workspace:^", + "@tevm/test-utils": "workspace:^", "@tevm/tx": "workspace:^", "@tevm/utils": "workspace:^", "@tevm/vm": "workspace:^" diff --git a/packages/procedures/src/__snapshots__/createHandlers.spec.ts.snap b/packages/procedures/src/__snapshots__/createHandlers.spec.ts.snap new file mode 100644 index 0000000000..d6974ec607 --- /dev/null +++ b/packages/procedures/src/__snapshots__/createHandlers.spec.ts.snap @@ -0,0 +1,137 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`createHandlers should handle tevm_mine 1`] = ` +{ + "id": 1, + "jsonrpc": "2.0", + "method": "tevm_mine", + "result": { + "blockHashes": [ + "0x3201277b6823bbcd45e1a1c4a334c31b083676c70cebb1f386139f6091ae8e49", + ], + }, +} +`; + +exports[`createHandlers should handle eth_chainId 1`] = ` +{ + "id": 1, + "jsonrpc": "2.0", + "method": "eth_chainId", + "result": "0x384", +} +`; + +exports[`createHandlers should handle eth_call 1`] = ` +{ + "error": { + "code": -32000, + "data": { + "errors": [ + +"sender doesn't have enough funds to send tx. The upfront cost is: 210000000 and the sender's account (0x6969696969696969696969696969696969696969) only has: 0 -> block number=0 hash=0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454 hf=cancun baseFeePerGas=7 txs=0 uncles=0 -> tx type=2 hash=0x96171020921a6736a4e62d65c40b749609224fe43187d437cb37d19b3c011005 nonce=0 value=0 signed=true hf=cancun maxFeePerGas=7 maxPriorityFeePerGas=0) + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73 + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Details: /reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73" +, + ], + }, + "message": +"sender doesn't have enough funds to send tx. The upfront cost is: 210000000 and the sender's account (0x6969696969696969696969696969696969696969) only has: 0 -> block number=0 hash=0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454 hf=cancun baseFeePerGas=7 txs=0 uncles=0 -> tx type=2 hash=0x96171020921a6736a4e62d65c40b749609224fe43187d437cb37d19b3c011005 nonce=0 value=0 signed=true hf=cancun maxFeePerGas=7 maxPriorityFeePerGas=0) + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73 + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Details: /reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73" +, + }, + "id": 1, + "jsonrpc": "2.0", + "method": "eth_call", +} +`; + +exports[`createHandlers should handle eth_getBalance 1`] = ` +{ + "id": 1, + "jsonrpc": "2.0", + "method": "eth_getBalance", + "result": "0x0", +} +`; + +exports[`createHandlers should handle eth_estimateGas 1`] = ` +{ + "error": { + "code": -32000, + "data": { + "errors": [ + +"sender doesn't have enough funds to send tx. The upfront cost is: 210000000 and the sender's account (0x6969696969696969696969696969696969696969) only has: 0 -> block number=0 hash=0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454 hf=cancun baseFeePerGas=7 txs=0 uncles=0 -> tx type=2 hash=0x96171020921a6736a4e62d65c40b749609224fe43187d437cb37d19b3c011005 nonce=0 value=0 signed=true hf=cancun maxFeePerGas=7 maxPriorityFeePerGas=0) + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73 + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Details: /reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73" +, + ], + }, + "message": +"sender doesn't have enough funds to send tx. The upfront cost is: 210000000 and the sender's account (0x6969696969696969696969696969696969696969) only has: 0 -> block number=0 hash=0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454 hf=cancun baseFeePerGas=7 txs=0 uncles=0 -> tx type=2 hash=0x96171020921a6736a4e62d65c40b749609224fe43187d437cb37d19b3c011005 nonce=0 value=0 signed=true hf=cancun maxFeePerGas=7 maxPriorityFeePerGas=0) + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73 + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Details: /reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73" +, + }, + "id": 1, + "jsonrpc": "2.0", + "method": "eth_estimateGas", +} +`; + +exports[`createHandlers should handle tevm_contract 1`] = ` +{ + "error": { + "code": "MethodNotSupported", + "message": +"UnsupportedMethodError: tevm_contract is not supported. Encode the contract arguments and use tevm_call instead. + +Docs: https://tevm.sh/reference/tevm/errors/classes/methodnotsupportederror/ +Version: 1.1.0.next-73" +, + }, + "id": 1, + "jsonrpc": "2.0", + "method": "tevm_contract", +} +`; + +exports[`createHandlers should handle eth_mining 1`] = ` +{ + "id": 1, + "jsonrpc": "2.0", + "method": "eth_mining", + "result": false, +} +`; + +exports[`createHandlers should handle eth_syncing 1`] = ` +{ + "id": 1, + "jsonrpc": "2.0", + "method": "eth_syncing", + "result": false, +} +`; diff --git a/packages/procedures/src/call/__snapshots__/callProcedure.spec.ts.snap b/packages/procedures/src/call/__snapshots__/callProcedure.spec.ts.snap new file mode 100644 index 0000000000..0a821a885e --- /dev/null +++ b/packages/procedures/src/call/__snapshots__/callProcedure.spec.ts.snap @@ -0,0 +1,53 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`callProcedure should handle a basic call 1`] = ` +{ + "amountSpent": "0x296a6", + "createdAddresses": [], + "executionGasUsed": "0xc62", + "gas": "0x1c964d6", + "logs": [], + "rawData": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", + "selfdestruct": [], + "totalGasSpent": "0x5eaa", +} +`; + +exports[`callProcedure should handle a call with state override 1`] = ` +{ + "amountSpent": "0x23e38", + "executionGasUsed": "0x0", + "rawData": "0x", + "totalGasSpent": "0x5208", +} +`; + +exports[`callProcedure should handle errors from callHandler 1`] = ` +{ + "code": -32000, + "data": { + "errors": [ + +"sender doesn't have enough funds to send tx. The upfront cost is: 210000500 and the sender's account (0x4242424242424242424242424242424242424242) only has: 0 -> block number=0 hash=0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454 hf=cancun baseFeePerGas=7 txs=0 uncles=0 -> tx type=2 hash=0x66575cf759d7c97e42f103bfc8de6f88f70ea97a785d0c1f558496abf977cae1 nonce=0 value=500 signed=true hf=cancun maxFeePerGas=7 maxPriorityFeePerGas=0) + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73 + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Details: /reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73" +, + ], + }, + "message": +"sender doesn't have enough funds to send tx. The upfront cost is: 210000500 and the sender's account (0x4242424242424242424242424242424242424242) only has: 0 -> block number=0 hash=0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454 hf=cancun baseFeePerGas=7 txs=0 uncles=0 -> tx type=2 hash=0x66575cf759d7c97e42f103bfc8de6f88f70ea97a785d0c1f558496abf977cae1 nonce=0 value=500 signed=true hf=cancun maxFeePerGas=7 maxPriorityFeePerGas=0) + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73 + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Details: /reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73" +, +} +`; diff --git a/packages/procedures/src/call/callProcedure.spec.ts b/packages/procedures/src/call/callProcedure.spec.ts new file mode 100644 index 0000000000..b833b0b9aa --- /dev/null +++ b/packages/procedures/src/call/callProcedure.spec.ts @@ -0,0 +1,105 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { setAccountHandler } from '@tevm/actions' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { ERC20 } from '@tevm/contract' +import { encodeFunctionData, numberToHex, parseEther } from '@tevm/utils' +import type { CallJsonRpcRequest } from './CallJsonRpcRequest.js' +import { callProcedure } from './callProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('callProcedure', () => { + it('should handle a basic call', async () => { + const address = `0x${'69'.repeat(20)}` as const + + await setAccountHandler(client)({ + address, + balance: 420n, + nonce: 69n, + deployedBytecode: ERC20.deployedBytecode, + state: { + '0x0': '0x01', + }, + }) + + const request: CallJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_call', + id: 1, + params: [ + { + to: address, + data: encodeFunctionData(ERC20.read.name()), + gas: numberToHex(21000n), + gasPrice: numberToHex(1n), + }, + ], + } + + const response = await callProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_call') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle a call with state override', async () => { + const to = `0x${'69'.repeat(20)}` as const + const from = `0x${'42'.repeat(20)}` as const + const request: CallJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_call', + id: 1, + params: [ + { + to, + from, + value: numberToHex(parseEther('.9')), + }, + { + [from]: { + balance: numberToHex(parseEther('1')), + }, + }, + ], + } + + const response = await callProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_call') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it.todo('should handle a call with block override', async () => {}) + + it('should handle errors from callHandler', async () => { + const request: CallJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_call', + id: 1, + params: [ + { + to: `0x${'00'.repeat(20)}` as const, // Invalid address + from: `0x${'42'.repeat(20)}` as const, + data: '0x0', + value: numberToHex(500n), + gas: numberToHex(21000n), + gasPrice: numberToHex(1n), + }, + ], + } + + const response = await callProcedure(client)(request) + expect(response.error).toBeDefined() + expect(response.error).toMatchSnapshot() + expect(response.method).toBe('tevm_call') + expect(response.id).toBe(request.id as any) + }) +}) diff --git a/packages/procedures/src/createHandlers.spec.ts b/packages/procedures/src/createHandlers.spec.ts new file mode 100644 index 0000000000..75bdf1808d --- /dev/null +++ b/packages/procedures/src/createHandlers.spec.ts @@ -0,0 +1,142 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { createBaseClient } from '@tevm/base-client' +import { ERC20 } from '@tevm/contract' +import { numberToHex } from '@tevm/utils' +import { type RequestHandlers, createHandlers } from './createHandlers.js' + +const ERC20_ADDRESS = `0x${'69'.repeat(20)}` as const + +describe('createHandlers', () => { + let client: any + let handlers: RequestHandlers + + beforeEach(() => { + client = createBaseClient() + handlers = createHandlers(client) + }) + + it('should handle eth_chainId', async () => { + const res = await handlers.eth_chainId({ + jsonrpc: '2.0', + method: 'eth_chainId', + id: 1, + }) + expect(res).toMatchSnapshot() + }) + + it('should handle eth_call', async () => { + const to = `0x${'69'.repeat(20)}` as const + const res = await handlers.eth_call({ + jsonrpc: '2.0', + method: 'eth_call', + id: 1, + params: [ + { + from: to, + to, + data: '0x', + }, + 'latest', + ], + }) + expect(res).toMatchSnapshot() + }) + + it('should handle eth_getBalance', async () => { + const address = `0x${'69'.repeat(20)}` as const + const res = await handlers.eth_getBalance({ + jsonrpc: '2.0', + method: 'eth_getBalance', + id: 1, + params: [address, 'latest'], + }) + expect(res).toMatchSnapshot() + }) + + it('should handle tevm_getAccount', async () => { + await handlers.tevm_setAccount({ + jsonrpc: '2.0', + method: 'tevm_setAccount', + id: 1, + params: [ + { + address: ERC20_ADDRESS, + deployedBytecode: ERC20.deployedBytecode, + balance: numberToHex(420n), + nonce: numberToHex(69n), + }, + ], + }) + const res = await handlers.tevm_getAccount({ + jsonrpc: '2.0', + method: 'tevm_getAccount', + id: 1, + params: [ + { + address: ERC20_ADDRESS, + }, + ], + }) + expect(res.error).toBeUndefined() + expect(res).toMatchSnapshot + }) + + it('should handle eth_estimateGas', async () => { + const to = `0x${'69'.repeat(20)}` as const + const res = await handlers.eth_estimateGas({ + jsonrpc: '2.0', + method: 'eth_estimateGas', + id: 1, + params: [ + { + from: to, + to, + data: '0x', + }, + ], + }) + expect(res).toMatchSnapshot() + }) + + it('should handle tevm_mine', async () => { + const res = await handlers.tevm_miner({ + jsonrpc: '2.0', + method: 'tevm_mine', + id: 1, + params: ['0x1', '0x1'], + }) + expect(res.id).toBe(1) + expect(res.error).toBeUndefined() + expect(res.method).toBe('tevm_mine') + expect(res.result?.blockHashes).toHaveLength(1) + }) + + it('should handle tevm_contract', async () => { + const res = await handlers.tevm_contract({ + jsonrpc: '2.0', + method: 'tevm_contract', + id: 1, + }) + expect(res).toMatchSnapshot() + }) + + // Add more tests for other handlers as needed... + + it('should handle eth_mining', async () => { + const res = handlers.eth_mining({ + jsonrpc: '2.0', + method: 'eth_mining', + id: 1, + }) + expect(res).toMatchSnapshot() + }) + + it('should handle eth_syncing', async () => { + const res = handlers.eth_syncing({ + jsonrpc: '2.0', + method: 'eth_syncing', + id: 1, + }) + expect(res).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/dumpstate/__snapshots__/dumpStateProcedure.spec.ts.snap b/packages/procedures/src/dumpstate/__snapshots__/dumpStateProcedure.spec.ts.snap new file mode 100644 index 0000000000..a590b2aea3 --- /dev/null +++ b/packages/procedures/src/dumpstate/__snapshots__/dumpStateProcedure.spec.ts.snap @@ -0,0 +1,95 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`dumpStateProcedure should dump the state successfully 1`] = ` +{ + "state": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0x6969696969696969696969696969696969696969": { + "balance": "0x1a4", + "codeHash": "0x56570de287d73cd1cb6092bb8fdee6173974955fdef345ae579ee9f475ea7432", + "deployedBytecode": "0x1234", + "nonce": "0x45", + "storage": { + "0000000000000000000000000000000000000000000000000000000000000000": "0x01", + }, + "storageRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + }, + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0x90F79bf6EB2c4f870365E785982E1f101E93b906": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0x976EA74026E726554dB657fA54763abd0C3a0aa9": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": { + "balance": "0x3635c9adc5dea00000", + "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "storage": {}, + "storageRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + }, +} +`; diff --git a/packages/procedures/src/dumpstate/dumpStateProcedure.spec.ts b/packages/procedures/src/dumpstate/dumpStateProcedure.spec.ts new file mode 100644 index 0000000000..8fddc63d74 --- /dev/null +++ b/packages/procedures/src/dumpstate/dumpStateProcedure.spec.ts @@ -0,0 +1,40 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { setAccountHandler } from '@tevm/actions' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { DumpStateJsonRpcRequest } from './DumpStateJsonRpcRequest.js' +import { dumpStateProcedure } from './dumpStateProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('dumpStateProcedure', () => { + it('should dump the state successfully', async () => { + const address = `0x${'69'.repeat(20)}` as const + + await setAccountHandler(client)({ + address, + balance: 420n, + nonce: 69n, + deployedBytecode: '0x1234', + state: { + '0x0': '0x01', + }, + }) + + const request: DumpStateJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_dumpState', + id: 1, + } + + const response = await dumpStateProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_dumpState') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/__snapshots__/blockNumberProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/blockNumberProcedure.spec.ts.snap new file mode 100644 index 0000000000..46abb478ab --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/blockNumberProcedure.spec.ts.snap @@ -0,0 +1,5 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`blockNumberProcedure should return the current block number 1`] = `"0x0"`; + +exports[`blockNumberProcedure should handle requests without an id 1`] = `"0x0"`; diff --git a/packages/procedures/src/eth/__snapshots__/chainIdProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/chainIdProcedure.spec.ts.snap new file mode 100644 index 0000000000..326a632b36 --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/chainIdProcedure.spec.ts.snap @@ -0,0 +1,5 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`chainIdProcedure should return the current chain ID 1`] = `"0x384"`; + +exports[`chainIdProcedure should handle requests without an id 1`] = `"0x384"`; diff --git a/packages/procedures/src/eth/__snapshots__/ethBlobBaseFeeProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/ethBlobBaseFeeProcedure.spec.ts.snap new file mode 100644 index 0000000000..810f8b5d2e --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/ethBlobBaseFeeProcedure.spec.ts.snap @@ -0,0 +1,7 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`ethBlobBaseFeeJsonRpcProcedure should return the blob base fee 1`] = `"0x1"`; + +exports[`ethBlobBaseFeeJsonRpcProcedure should handle requests without an id 1`] = `"0x1"`; + +exports[`ethBlobBaseFeeJsonRpcProcedure should handle requests with additional parameters 1`] = `"0x1"`; diff --git a/packages/procedures/src/eth/__snapshots__/ethCallProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/ethCallProcedure.spec.ts.snap new file mode 100644 index 0000000000..22432b3684 --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/ethCallProcedure.spec.ts.snap @@ -0,0 +1,37 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`ethCallProcedure should return error when call fails 1`] = ` +{ + "code": -32000, + "data": { + "errors": [ + +"sender doesn't have enough funds to send tx. The upfront cost is: 210000000 and the sender's account (0x6969696969696969696969696969696969696969) only has: 0 -> block number=0 hash=0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454 hf=cancun baseFeePerGas=7 txs=0 uncles=0 -> tx type=2 hash=0xd3e22d4c76284ef1cd4b984bc3e20308dbcb13a96239f9e550ca74b03d67f779 nonce=0 value=0 signed=true hf=cancun maxFeePerGas=7 maxPriorityFeePerGas=0) + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73 + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Details: /reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73" +, + ], + }, + "message": +"sender doesn't have enough funds to send tx. The upfront cost is: 210000000 and the sender's account (0x6969696969696969696969696969696969696969) only has: 0 -> block number=0 hash=0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454 hf=cancun baseFeePerGas=7 txs=0 uncles=0 -> tx type=2 hash=0xd3e22d4c76284ef1cd4b984bc3e20308dbcb13a96239f9e550ca74b03d67f779 nonce=0 value=0 signed=true hf=cancun maxFeePerGas=7 maxPriorityFeePerGas=0) + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73 + +Docs: https://tevm.sh/reference/tevm/errors/classes/insufficientfundserror/ +Details: /reference/tevm/errors/classes/insufficientfundserror/ +Version: 1.1.0.next-73" +, +} +`; + +exports[`ethCallProcedure should handle missing optional fields in the request 1`] = `"0x"`; + +exports[`ethCallProcedure should handle requests without an id 1`] = `"0x"`; + +exports[`ethCallProcedure should execute a message call successfully 1`] = `"0x"`; diff --git a/packages/procedures/src/eth/__snapshots__/ethCoinbaseProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/ethCoinbaseProcedure.spec.ts.snap new file mode 100644 index 0000000000..8f3e041954 --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/ethCoinbaseProcedure.spec.ts.snap @@ -0,0 +1,5 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`ethCoinbaseJsonRpcProcedure should return the coinbase address 1`] = `"0x0000000000000000000000000000000000000000"`; + +exports[`ethCoinbaseJsonRpcProcedure should handle requests without an id 1`] = `"0x0000000000000000000000000000000000000000"`; diff --git a/packages/procedures/src/eth/__snapshots__/ethEstimateGasProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/ethEstimateGasProcedure.spec.ts.snap new file mode 100644 index 0000000000..ff6391ce55 --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/ethEstimateGasProcedure.spec.ts.snap @@ -0,0 +1,27 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`ethEstimateGasJsonRpcProcedure should estimate gas successfully 1`] = `"0x5208"`; + +exports[`ethEstimateGasJsonRpcProcedure should handle errors from callProcedure 1`] = ` +{ + "code": -32602, + "data": { + "errors": [ + +"value must be a hex string + +Docs: https://tevm.sh/reference/tevm/errors/classes/invaliddataerror/ +Version: 1.1.0.next-73" +, + ], + }, + "message": +"value must be a hex string + +Docs: https://tevm.sh/reference/tevm/errors/classes/invaliddataerror/ +Version: 1.1.0.next-73" +, +} +`; + +exports[`ethEstimateGasJsonRpcProcedure should handle requests without an id 1`] = `"0x5208"`; diff --git a/packages/procedures/src/eth/__snapshots__/ethGetBlockByHashProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/ethGetBlockByHashProcedure.spec.ts.snap new file mode 100644 index 0000000000..6b89dab1b4 --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/ethGetBlockByHashProcedure.spec.ts.snap @@ -0,0 +1,124 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`ethGetBlockByHashJsonRpcProcedure should return block details by hash 1`] = ` +{ + "baseFeePerGas": "0x7", + "blobGasUsed": "0x0", + "difficulty": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x", + "gasLimit": "0x1c9c380", + "gasUsed": "0x5208", + "hash": "0xd5c6fb8547f642937fac28ed08bac378b24fa000017029cf7e52996e2af217d6", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "number": "0x1", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash": "0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454", + "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", + "requests": undefined, + "requestsRoot": undefined, + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x6d1", + "stateRoot": "0xd417e9ddb1cd7255747c7a5ea9b703fea4851b04aa2917ca5d4e8b12ec0efe0f", + "timestamp": "0x6683628e", + "totalDifficulty": "0x0", + "transactions": [ + "0x53f34b8aec96f115c92c8e49f3ec2abe1cb606d59e07732e3be76408628991f5", + ], + "transactionsRoot": "0x728f001e8385a75fdb87571fa02a8ac4ec71211f4e292e48d11c2aa36a7aca29", + "uncles": [], + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", +} +`; + +exports[`ethGetBlockByHashJsonRpcProcedure should include transactions if requested 1`] = ` +{ + "baseFeePerGas": "0x7", + "blobGasUsed": "0x0", + "difficulty": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x", + "gasLimit": "0x1c9c380", + "gasUsed": "0x5208", + "hash": "0xd5c6fb8547f642937fac28ed08bac378b24fa000017029cf7e52996e2af217d6", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "number": "0x1", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash": "0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454", + "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", + "requests": undefined, + "requestsRoot": undefined, + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x6d1", + "stateRoot": "0xd417e9ddb1cd7255747c7a5ea9b703fea4851b04aa2917ca5d4e8b12ec0efe0f", + "timestamp": "0x6683628e", + "totalDifficulty": "0x0", + "transactions": [ + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xd5c6fb8547f642937fac28ed08bac378b24fa000017029cf7e52996e2af217d6", + "blockNumber": "0x1", + "data": "0x", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "gas": "0x5a3c", + "gasPrice": "0x7", + "hash": "0x53f34b8aec96f115c92c8e49f3ec2abe1cb606d59e07732e3be76408628991f5", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x7", + "maxPriorityFeePerGas": "0x0", + "nonce": "0x0", + "to": "0x0101010101010101010101010101010101010101", + "transactionIndex": "0x0", + "type": "0x2", + "value": "0x1a4", + }, + ], + "transactionsRoot": "0x728f001e8385a75fdb87571fa02a8ac4ec71211f4e292e48d11c2aa36a7aca29", + "uncles": [], + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", +} +`; + +exports[`ethGetBlockByHashJsonRpcProcedure should handle requests without an id 1`] = ` +{ + "baseFeePerGas": "0x7", + "blobGasUsed": "0x0", + "difficulty": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x", + "gasLimit": "0x1c9c380", + "gasUsed": "0x5208", + "hash": "0xd5c6fb8547f642937fac28ed08bac378b24fa000017029cf7e52996e2af217d6", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "number": "0x1", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash": "0x63b5bacdb65c8c9d10e77d77d40bda5b4dc010a4f7e02a10bdea21a2b1b0a454", + "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", + "requests": undefined, + "requestsRoot": undefined, + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x6d1", + "stateRoot": "0xd417e9ddb1cd7255747c7a5ea9b703fea4851b04aa2917ca5d4e8b12ec0efe0f", + "timestamp": "0x6683628e", + "totalDifficulty": "0x0", + "transactions": [ + "0x53f34b8aec96f115c92c8e49f3ec2abe1cb606d59e07732e3be76408628991f5", + ], + "transactionsRoot": "0x728f001e8385a75fdb87571fa02a8ac4ec71211f4e292e48d11c2aa36a7aca29", + "uncles": [], + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", +} +`; diff --git a/packages/procedures/src/eth/__snapshots__/ethGetBlockByNumberProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/ethGetBlockByNumberProcedure.spec.ts.snap new file mode 100644 index 0000000000..61393d31fe --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/ethGetBlockByNumberProcedure.spec.ts.snap @@ -0,0 +1,14 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`ethGetBlockByNumberJsonRpcProcedure should handle invalid block tag 1`] = ` +{ + "code": -32602, + "message": "Invalid block tag invalidTag", +} +`; + +exports[`ethGetBlockByNumberJsonRpcProcedure should return block details by number 1`] = `Promise {}`; + +exports[`ethGetBlockByNumberJsonRpcProcedure should include transactions if requested 1`] = `Promise {}`; + +exports[`ethGetBlockByNumberJsonRpcProcedure should handle requests without an id 1`] = `Promise {}`; diff --git a/packages/procedures/src/eth/__snapshots__/ethGetBlockTransactionCountByHashProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/ethGetBlockTransactionCountByHashProcedure.spec.ts.snap new file mode 100644 index 0000000000..2661afdbb6 --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/ethGetBlockTransactionCountByHashProcedure.spec.ts.snap @@ -0,0 +1,5 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`ethGetBlockTransactionCountByHashJsonRpcProcedure should handle an invalid block hash 1`] = `[BaseError: Invalid byte sequence ("0I" in "0InvalidHash"). + +Version: viem@2.14.2]`; diff --git a/packages/procedures/src/eth/__snapshots__/ethGetFilterChangesProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/ethGetFilterChangesProcedure.spec.ts.snap new file mode 100644 index 0000000000..ba2b5efa17 --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/ethGetFilterChangesProcedure.spec.ts.snap @@ -0,0 +1,40 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`ethGetFilterChangesProcedure should return log changes for Log type filter 1`] = ` +[ + { + "address": "0x0000000000000000000000000000000000000000", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", + "data": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logIndex": "0x0", + "removed": false, + "topics": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + ], + "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0", + }, +] +`; + +exports[`ethGetFilterChangesProcedure should return block changes for Block type filter 1`] = ` +[ + "0x1", +] +`; + +exports[`ethGetFilterChangesProcedure should return an error if the filter is not found 1`] = ` +{ + "code": -32601, + "message": "Method not implemented yet", +} +`; + +exports[`ethGetFilterChangesProcedure should throw an error for an unknown filter type 1`] = `[Error: InternalError: Unknown filter type. This indicates a bug in tevm or potentially a typo in filter type if manually added]`; + +exports[`ethGetFilterChangesProcedure should return transaction changes for PendingTransaction type filter 1`] = ` +[ + "0x9451a27a810f49a49f917da5e6ad3ecc7182e31b0d71560e9bf4af4928ac52fc", +] +`; diff --git a/packages/procedures/src/eth/__snapshots__/gasPriceProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/gasPriceProcedure.spec.ts.snap new file mode 100644 index 0000000000..dfbbfa3bff --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/gasPriceProcedure.spec.ts.snap @@ -0,0 +1,5 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`gasPriceProcedure should return the current gas price 1`] = `"0x3b9aca00"`; + +exports[`gasPriceProcedure should handle requests without an id 1`] = `"0x3b9aca00"`; diff --git a/packages/procedures/src/eth/__snapshots__/getBalanceProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/getBalanceProcedure.spec.ts.snap new file mode 100644 index 0000000000..5b347f29d7 --- /dev/null +++ b/packages/procedures/src/eth/__snapshots__/getBalanceProcedure.spec.ts.snap @@ -0,0 +1,3 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`getBalanceProcedure should return an error if the block parameter is missing 1`] = `[Error: getBalanceProcedure received invalid parameters: Block parameter (req.params[1]) is missing or invalid. Expected a hex string or block tag (e.g., "latest", "earliest").]`; diff --git a/packages/procedures/src/eth/blockNumberProcedure.spec.ts b/packages/procedures/src/eth/blockNumberProcedure.spec.ts new file mode 100644 index 0000000000..a2f0a3be4f --- /dev/null +++ b/packages/procedures/src/eth/blockNumberProcedure.spec.ts @@ -0,0 +1,43 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { EthBlockNumberJsonRpcRequest } from './EthJsonRpcRequest.js' +import { blockNumberProcedure } from './blockNumberProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('blockNumberProcedure', () => { + it('should return the current block number', async () => { + const request: EthBlockNumberJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_blockNumber', + id: 1, + params: [], + } + + const response = await blockNumberProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_blockNumber') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle requests without an id', async () => { + const request: EthBlockNumberJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_blockNumber', + params: [], + } + + const response = await blockNumberProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_blockNumber') + expect(response.id).toBeUndefined() + expect(response.result).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/chainIdProcedure.spec.ts b/packages/procedures/src/eth/chainIdProcedure.spec.ts new file mode 100644 index 0000000000..86d23662b1 --- /dev/null +++ b/packages/procedures/src/eth/chainIdProcedure.spec.ts @@ -0,0 +1,43 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { EthChainIdJsonRpcRequest } from './EthJsonRpcRequest.js' +import { chainIdProcedure } from './chainIdProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('chainIdProcedure', () => { + it('should return the current chain ID', async () => { + const request: EthChainIdJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_chainId', + id: 1, + params: [], + } + + const response = await chainIdProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_chainId') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle requests without an id', async () => { + const request: EthChainIdJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_chainId', + params: [], + } + + const response = await chainIdProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_chainId') + expect(response.id).toBeUndefined() + expect(response.result).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/ethBlobBaseFeeProcedure.spec.ts b/packages/procedures/src/eth/ethBlobBaseFeeProcedure.spec.ts new file mode 100644 index 0000000000..779f377bb2 --- /dev/null +++ b/packages/procedures/src/eth/ethBlobBaseFeeProcedure.spec.ts @@ -0,0 +1,58 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { ethBlobBaseFeeJsonRpcProcedure } from './ethBlobBaseFeeProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('ethBlobBaseFeeJsonRpcProcedure', () => { + it('should return the blob base fee', async () => { + const request: any = { + jsonrpc: '2.0', + method: 'eth_blobBaseFee', + id: 1, + params: [], + } + + const response = await ethBlobBaseFeeJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_blobBaseFee' as any) + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle requests without an id', async () => { + const request: any = { + jsonrpc: '2.0', + method: 'eth_blobBaseFee', + params: [], + } + + const response = await ethBlobBaseFeeJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_blobBaseFee' as any) + expect(response.id).toBeUndefined() + expect(response.result).toMatchSnapshot() + }) + + it('should handle requests with additional parameters', async () => { + const request: any = { + jsonrpc: '2.0', + method: 'eth_blobBaseFee', + id: 1, + params: ['0x1'], // Additional parameters should be ignored + } + + const response = await ethBlobBaseFeeJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_blobBaseFee' as any) + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/ethCallProcedure.spec.ts b/packages/procedures/src/eth/ethCallProcedure.spec.ts new file mode 100644 index 0000000000..17113139df --- /dev/null +++ b/packages/procedures/src/eth/ethCallProcedure.spec.ts @@ -0,0 +1,79 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { EthCallJsonRpcRequest } from './EthJsonRpcRequest.js' +import { ethCallProcedure } from './ethCallProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('ethCallProcedure', () => { + it('should execute a message call successfully', async () => { + const request: EthCallJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_call', + id: 1, + params: [ + { + data: '0x0', + from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + to: `0x${'69'.repeat(20)}`, + value: '0x1', + }, + 'latest', + ], + } + + const response = await ethCallProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_call') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle missing optional fields in the request', async () => { + const request: EthCallJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_call', + id: 1, + params: [ + { + from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + to: `0x${'69'.repeat(20)}`, + }, + 'latest', + ], + } + + const response = await ethCallProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_call') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle requests without an id', async () => { + const request: EthCallJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_call', + params: [ + { + from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + to: `0x${'69'.repeat(20)}`, + }, + 'latest', + ], + } + + const response = await ethCallProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_call') + expect(response.id).toBeUndefined() + expect(response.result).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/ethCoinbaseProcedure.spec.ts b/packages/procedures/src/eth/ethCoinbaseProcedure.spec.ts new file mode 100644 index 0000000000..5b66050a5d --- /dev/null +++ b/packages/procedures/src/eth/ethCoinbaseProcedure.spec.ts @@ -0,0 +1,43 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { EthCoinbaseJsonRpcRequest } from './EthJsonRpcRequest.js' +import { ethCoinbaseJsonRpcProcedure } from './ethCoinbaseProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('ethCoinbaseJsonRpcProcedure', () => { + it('should return the coinbase address', async () => { + const request: EthCoinbaseJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_coinbase', + id: 1, + params: [], + } + + const response = await ethCoinbaseJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_coinbase') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle requests without an id', async () => { + const request: EthCoinbaseJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_coinbase', + params: [], + } + + const response = await ethCoinbaseJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_coinbase') + expect(response.id).toBeUndefined() + expect(response.result).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/ethEstimateGasProcedure.spec.ts b/packages/procedures/src/eth/ethEstimateGasProcedure.spec.ts new file mode 100644 index 0000000000..201a5bb01e --- /dev/null +++ b/packages/procedures/src/eth/ethEstimateGasProcedure.spec.ts @@ -0,0 +1,77 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { EthEstimateGasJsonRpcRequest } from './EthJsonRpcRequest.js' +import { ethEstimateGasJsonRpcProcedure } from './ethEstimateGasProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('ethEstimateGasJsonRpcProcedure', () => { + it('should estimate gas successfully', async () => { + const request: EthEstimateGasJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_estimateGas', + id: 1, + params: [ + { + from: '0x0000000000000000000000000000000000000000', + to: '0x0000000000000000000000000000000000000000', + data: '0x', + }, + ], + } + + const response = await ethEstimateGasJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_estimateGas') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle errors from callProcedure', async () => { + const request: EthEstimateGasJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_estimateGas', + id: 1, + params: [ + { + to: '0x0000000000000000000000000000000000000000', + from: '0x0000000000000000000000000000000000000000', + data: '0xINVALID_DATA', + }, + ], + } + + const response = await ethEstimateGasJsonRpcProcedure(client)(request) + expect(response.error).toBeDefined() + expect(response.result).toBeUndefined() + expect(response.method).toBe('eth_estimateGas') + expect(response.id).toBe(request.id as any) + expect(response.error).toMatchSnapshot() + }) + + it('should handle requests without an id', async () => { + const request: EthEstimateGasJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_estimateGas', + params: [ + { + to: '0x0000000000000000000000000000000000000000', + from: '0x0000000000000000000000000000000000000000', + data: '0x', + }, + ], + } + + const response = await ethEstimateGasJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_estimateGas') + expect(response.id).toBeUndefined() + expect(response.result).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/ethGetBlockByHashProcedure.spec.ts b/packages/procedures/src/eth/ethGetBlockByHashProcedure.spec.ts new file mode 100644 index 0000000000..a93d6d079e --- /dev/null +++ b/packages/procedures/src/eth/ethGetBlockByHashProcedure.spec.ts @@ -0,0 +1,81 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { callHandler, mineHandler } from '@tevm/actions' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { bytesToHex, numberToHex } from '@tevm/utils' +import type { EthGetBlockByHashJsonRpcRequest } from './EthJsonRpcRequest.js' +import { ethGetBlockByHashJsonRpcProcedure } from './ethGetBlockByHashProcedure.js' + +let client: BaseClient + +beforeEach(async () => { + client = createBaseClient() + await callHandler(client)({ createTransaction: true, value: 420n, to: `0x${'01'.repeat(20)}` }) + await mineHandler(client)() +}) + +describe('ethGetBlockByHashJsonRpcProcedure', () => { + it('should return block details by hash', async () => { + const vm = await client.getVm() + const head = await vm.blockchain.getCanonicalHeadBlock() + const request: EthGetBlockByHashJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockByHash', + id: 1, + params: [ + bytesToHex(head.header.hash()), + false, // Do not include transactions + ], + } + + const response = await ethGetBlockByHashJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getBlockByHash') + expect(response.id).toBe(request.id as any) + expect(response.result?.hash).toBe(bytesToHex(head.header.hash())) + expect(response.result?.number).toBe(numberToHex(head.header.number)) + }) + + it('should include transactions if requested', async () => { + const vm = await client.getVm() + const head = await vm.blockchain.getCanonicalHeadBlock() + const request: EthGetBlockByHashJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockByHash', + id: 1, + params: [ + bytesToHex(head.header.hash()), + true, // Include transactions + ], + } + + const response = await ethGetBlockByHashJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getBlockByHash') + expect(response.id).toBe(request.id as any) + expect(response.result?.hash).toBe(bytesToHex(head.header.hash())) + expect(response.result?.number).toBe(numberToHex(head.header.number)) + }) + + it('should handle requests without an id', async () => { + const vm = await client.getVm() + const head = await vm.blockchain.getCanonicalHeadBlock() + const request: EthGetBlockByHashJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockByHash', + params: [ + bytesToHex(head.header.hash()), + false, // Do not include transactions + ], + } + + const response = await ethGetBlockByHashJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getBlockByHash') + expect(response.id).toBeUndefined() + expect(response.result?.hash).toBe(bytesToHex(head.header.hash())) + expect(response.result?.number).toBe(numberToHex(head.header.number)) + }) +}) diff --git a/packages/procedures/src/eth/ethGetBlockByNumberProcedure.spec.ts b/packages/procedures/src/eth/ethGetBlockByNumberProcedure.spec.ts new file mode 100644 index 0000000000..0a94e17c77 --- /dev/null +++ b/packages/procedures/src/eth/ethGetBlockByNumberProcedure.spec.ts @@ -0,0 +1,75 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { mineHandler } from '@tevm/actions' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { EthGetBlockByNumberJsonRpcRequest } from './EthJsonRpcRequest.js' +import { ethGetBlockByNumberJsonRpcProcedure } from './ethGetBlockByNumberProcedure.js' + +let client: BaseClient + +beforeEach(async () => { + client = createBaseClient() + await mineHandler(client)() + await mineHandler(client)() +}) + +describe('ethGetBlockByNumberJsonRpcProcedure', () => { + it('should return block details by number', async () => { + const request: EthGetBlockByNumberJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + id: 1, + params: ['0x1', false], // Block number as hex + } + + const response = await ethGetBlockByNumberJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getBlockByNumber') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should include transactions if requested', async () => { + const request: EthGetBlockByNumberJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + id: 1, + params: ['0x1', true], // Block number as hex + } + + const response = await ethGetBlockByNumberJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getBlockByNumber') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle requests without an id', async () => { + const request: EthGetBlockByNumberJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + params: ['0x1', false], // Block number as hex + } + + const response = await ethGetBlockByNumberJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getBlockByNumber') + expect(response.id).toBeUndefined() + expect(response.result).toMatchSnapshot() + }) + + it('should handle invalid block tag', async () => { + const request: EthGetBlockByNumberJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + id: 1, + params: ['invalidTag' as any, false], // Invalid block tag + } + + const response = await ethGetBlockByNumberJsonRpcProcedure(client)(request) + expect(response.error).toBeDefined() + expect(response.error).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/ethGetBlockTransactionCountByHashProcedure.spec.ts b/packages/procedures/src/eth/ethGetBlockTransactionCountByHashProcedure.spec.ts new file mode 100644 index 0000000000..850109a67a --- /dev/null +++ b/packages/procedures/src/eth/ethGetBlockTransactionCountByHashProcedure.spec.ts @@ -0,0 +1,111 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { Block } from '@tevm/block' +import { bytesToHex, numberToHex } from '@tevm/utils' +import type { Vm } from '@tevm/vm' +import type { EthGetBlockTransactionCountByHashJsonRpcRequest } from './EthJsonRpcRequest.js' +import { ethGetBlockTransactionCountByHashJsonRpcProcedure } from './ethGetBlockTransactionCountByHashProcedure.js' + +let client: BaseClient +let vm: Vm + +beforeEach(async () => { + client = createBaseClient() + vm = await client.getVm() +}) + +describe('ethGetBlockTransactionCountByHashJsonRpcProcedure', () => { + it('should return the transaction count for a given block hash', async () => { + // Prepare mock block with transactions + const mockBlock = Block.fromBlockData( + { + header: { + parentHash: numberToHex(32, { size: 32 }), + coinbase: `0x${'12'.repeat(20)}`, + stateRoot: numberToHex(599, { size: 32 }), + transactionsTrie: numberToHex(999, { size: 32 }), + receiptTrie: numberToHex(10, { size: 32 }), + logsBloom: '0x', + number: 0, + gasLimit: 1, + gasUsed: 0, + timestamp: 0, + extraData: '0x', + mixHash: numberToHex(77, { size: 32 }), + nonce: numberToHex(0, { size: 8 }), + baseFeePerGas: 0, + }, + transactions: [{}, {}, {}], // Mock 3 transactions + }, + { common: vm.common }, + ) + + await vm.blockchain.putBlock(mockBlock) + + const request: EthGetBlockTransactionCountByHashJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockTransactionCountByHash', + id: 1, + params: [bytesToHex(mockBlock.hash())], + } + + const response = await ethGetBlockTransactionCountByHashJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBe(numberToHex(mockBlock.transactions.length)) + expect(response.method).toBe('eth_getBlockTransactionCountByHash') + expect(response.id).toBe(request.id as any) + }) + + it('should handle requests without an id', async () => { + // Prepare mock block with transactions + const mockBlock = Block.fromBlockData( + { + header: { + number: 0, + parentHash: numberToHex(32, { size: 32 }), + coinbase: `0x${'12'.repeat(20)}`, + stateRoot: numberToHex(599, { size: 32 }), + transactionsTrie: numberToHex(999, { size: 32 }), + receiptTrie: numberToHex(10, { size: 32 }), + logsBloom: '0x', + gasLimit: 1, + gasUsed: 0, + timestamp: 0, + extraData: '0x', + mixHash: numberToHex(77, { size: 32 }), + nonce: numberToHex(0, { size: 8 }), + baseFeePerGas: 0, + }, + transactions: [{}, {}, {}], // Mock 3 transactions + }, + { common: vm.common }, + ) + + await vm.blockchain.putBlock(mockBlock) + + const request: EthGetBlockTransactionCountByHashJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockTransactionCountByHash', + params: [bytesToHex(mockBlock.hash())], + } + + const response = await ethGetBlockTransactionCountByHashJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBe(numberToHex(mockBlock.transactions.length)) + expect(response.method).toBe('eth_getBlockTransactionCountByHash') + expect(response.id).toBeUndefined() + }) + + it('should handle an invalid block hash', async () => { + const request: EthGetBlockTransactionCountByHashJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockTransactionCountByHash', + id: 1, + params: ['0xInvalidHash'], + } + + const e = await ethGetBlockTransactionCountByHashJsonRpcProcedure(client)(request).catch((e) => e) + expect(e).toBeDefined() + expect(e).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/ethGetBlockTransactionCountByNumberProcedure.spec.ts b/packages/procedures/src/eth/ethGetBlockTransactionCountByNumberProcedure.spec.ts new file mode 100644 index 0000000000..21f7f47b05 --- /dev/null +++ b/packages/procedures/src/eth/ethGetBlockTransactionCountByNumberProcedure.spec.ts @@ -0,0 +1,122 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { Block } from '@tevm/block' +import { numberToHex } from '@tevm/utils' +import type { Vm } from '@tevm/vm' +import type { EthGetBlockTransactionCountByNumberJsonRpcRequest } from './EthJsonRpcRequest.js' +import { ethGetBlockTransactionCountByNumberJsonRpcProcedure } from './ethGetBlockTransactionCountByNumberProcedure.js' + +let client: BaseClient +let vm: Vm + +beforeEach(async () => { + client = createBaseClient() + vm = await client.getVm() +}) + +describe('ethGetBlockTransactionCountByNumberJsonRpcProcedure', () => { + it('should return the transaction count for a given block number', async () => { + // Prepare mock block with transactions + const mockBlock = Block.fromBlockData( + { + header: { + number: 1, + parentHash: `0x${'0'.repeat(64)}`, + coinbase: `0x${'1'.repeat(40)}`, + stateRoot: `0x${'0'.repeat(64)}`, + transactionsTrie: `0x${'0'.repeat(64)}`, + receiptTrie: `0x${'0'.repeat(64)}`, + logsBloom: `0x${'0'.repeat(512)}`, + gasLimit: 1, + gasUsed: 0, + timestamp: 0, + extraData: '0x', + mixHash: `0x${'0'.repeat(64)}`, + nonce: `0x${'0'.repeat(16)}`, + baseFeePerGas: 0, + }, + transactions: [ + { + to: `0x${'2'.repeat(40)}`, + value: 1, + gasLimit: 1, + gasPrice: 1, + nonce: 0, + data: '0x', + }, + { + to: `0x${'2'.repeat(40)}`, + value: 1, + gasLimit: 1, + gasPrice: 1, + nonce: 1, + data: '0x', + }, + { + to: `0x${'2'.repeat(40)}`, + value: 1, + gasLimit: 1, + gasPrice: 1, + nonce: 2, + data: '0x', + }, + ], + }, + { common: vm.common }, + ) + + await vm.blockchain.putBlock(mockBlock) + + const request: EthGetBlockTransactionCountByNumberJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockTransactionCountByNumber', + id: 1, + params: [numberToHex(mockBlock.header.number)], + } + + const response = await ethGetBlockTransactionCountByNumberJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBe(numberToHex(mockBlock.transactions.length)) + expect(response.method).toBe('eth_getBlockTransactionCountByNumber') + expect(response.id).toBe(request.id as any) + }) + + it('should handle requests without an id', async () => { + const mockBlock = Block.fromBlockData( + { + header: { + number: 1, + parentHash: `0x${'0'.repeat(64)}`, + coinbase: `0x${'0'.repeat(40)}`, + stateRoot: `0x${'0'.repeat(64)}`, + transactionsTrie: `0x${'0'.repeat(64)}`, + receiptTrie: `0x${'0'.repeat(64)}`, + logsBloom: `0x${'0'.repeat(512)}`, + gasLimit: 1, + gasUsed: 0, + timestamp: 0, + extraData: '0x', + mixHash: `0x${'0'.repeat(64)}`, + nonce: `0x${'0'.repeat(16)}`, + baseFeePerGas: 0, + }, + transactions: [{}, {}, {}], // Mock 3 transactions + }, + { common: vm.common }, + ) + + await vm.blockchain.putBlock(mockBlock) + + const request: EthGetBlockTransactionCountByNumberJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBlockTransactionCountByNumber', + params: [numberToHex(mockBlock.header.number)], + } + + const response = await ethGetBlockTransactionCountByNumberJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBe(numberToHex(mockBlock.transactions.length)) + expect(response.method).toBe('eth_getBlockTransactionCountByNumber') + expect(response.id).toBeUndefined() + }) +}) diff --git a/packages/procedures/src/eth/ethGetFilterChangesProcedure.spec.ts b/packages/procedures/src/eth/ethGetFilterChangesProcedure.spec.ts new file mode 100644 index 0000000000..3d03f88348 --- /dev/null +++ b/packages/procedures/src/eth/ethGetFilterChangesProcedure.spec.ts @@ -0,0 +1,190 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, type Filter, createBaseClient } from '@tevm/base-client' +import { Block } from '@tevm/block' +import { createImpersonatedTx } from '@tevm/tx' +import { EthjsAddress } from '@tevm/utils' +import type { EthGetFilterChangesJsonRpcRequest } from './EthJsonRpcRequest.js' +import { ethGetFilterChangesProcedure } from './ethGetFilterChangesProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('ethGetFilterChangesProcedure', () => { + it('should return log changes for Log type filter', async () => { + const filterId = '0x1' as const + + client.setFilter({ + ...{ created: Date.now() }, + id: filterId, + created: Date.now(), + type: 'Log', + logs: [ + { + address: `0x${'0'.repeat(40)}`, + topics: [`0x${'0'.repeat(64)}`], + data: `0x${'0'.repeat(64)}`, + blockNumber: 1n, + transactionHash: `0x${'0'.repeat(64)}`, + transactionIndex: 0, + blockHash: `0x${'0'.repeat(64)}`, + logIndex: 0, + removed: false, + }, + ], + tx: [], + blocks: [], + installed: {}, + err: undefined, + registeredListeners: [], + }) + const request: EthGetFilterChangesJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getFilterChanges', + id: 1, + params: [filterId], + } + + const response = await ethGetFilterChangesProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.method).toBe('eth_getFilterChanges') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should return block changes for Block type filter', async () => { + const filterId = '0x2' as const + const vm = await client.getVm() + const blocks = [ + Block.fromBlockData( + { + header: { + number: 1, + parentHash: `0x${'0'.repeat(64)}`, + coinbase: `0x${'0'.repeat(40)}`, + stateRoot: `0x${'0'.repeat(64)}`, + transactionsTrie: `0x${'0'.repeat(64)}`, + receiptTrie: `0x${'0'.repeat(64)}`, + logsBloom: `0x${'0'.repeat(512)}`, + gasLimit: 1, + gasUsed: 0, + timestamp: 0, + extraData: `0x${'0'.repeat(64)}`, + mixHash: `0x${'0'.repeat(64)}`, + nonce: `0x${'0'.repeat(16)}`, + baseFeePerGas: 0, + }, + }, + { common: vm.common }, + ), + ] + const filter: Filter = { + id: filterId, + type: 'Block', + created: Date.now(), + logs: [], + tx: [], + blocks, + installed: {}, + err: undefined, + registeredListeners: [], + } + + client.setFilter(filter) + + const request: EthGetFilterChangesJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getFilterChanges', + id: 1, + params: [filterId], + } + + const response = await ethGetFilterChangesProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.method).toBe('eth_getFilterChanges') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should return transaction changes for PendingTransaction type filter', async () => { + const filterId = '0x3' as const + const tx = [ + createImpersonatedTx({ + impersonatedAddress: EthjsAddress.fromString(`0x${'23'.repeat(20)}`), + to: `0x${'0'.repeat(40)}`, + data: `0x${'0'.repeat(40)}`, + }), + ] + + const filter: Filter = { + id: filterId, + type: 'PendingTransaction', + created: Date.now(), + logs: [], + tx, + blocks: [], + installed: {}, + err: undefined, + registeredListeners: [], + } + + client.setFilter(filter) + + const request: EthGetFilterChangesJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getFilterChanges', + id: 1, + params: [filterId], + } + + const response = await ethGetFilterChangesProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.method).toBe('eth_getFilterChanges') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should return an error if the filter is not found', async () => { + const filterId = '0x4' as const + const request: EthGetFilterChangesJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getFilterChanges', + id: 1, + params: [filterId], + } + + const response = await ethGetFilterChangesProcedure(client)(request) + expect(response.error).toBeDefined() + expect(response.error).toMatchSnapshot() + expect(response.method).toBe('eth_getFilterChanges') + expect(response.id).toBe(request.id as any) + }) + + it('should throw an error for an unknown filter type', async () => { + const filterId = '0x5' as const + const filter: Filter = { + id: filterId, + type: 'UnknownType' as any, // Invalid type to test error handling + created: Date.now(), + logs: [], + tx: [], + blocks: [], + installed: {}, + err: undefined, + registeredListeners: [], + } + + client.setFilter(filter) + + const request: EthGetFilterChangesJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getFilterChanges', + id: 1, + params: [filterId], + } + + expect(await ethGetFilterChangesProcedure(client)(request).catch((e) => e)).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/ethUninstallFilterProcedure.spec.ts b/packages/procedures/src/eth/ethUninstallFilterProcedure.spec.ts new file mode 100644 index 0000000000..b143f26f5d --- /dev/null +++ b/packages/procedures/src/eth/ethUninstallFilterProcedure.spec.ts @@ -0,0 +1,72 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { type Hex } from '@tevm/utils' +import type { EthUninstallFilterJsonRpcRequest } from './EthJsonRpcRequest.js' +import { ethUninstallFilterJsonRpcProcedure } from './ethUninstallFilterProcedure.js' + +let client: BaseClient +const filterId: Hex = '0x1' + +beforeEach(() => { + client = createBaseClient() + + client.getFilters().set(filterId, { + id: filterId, + type: 'Log', + created: Date.now(), + logs: [], + tx: [], + blocks: [], + installed: {}, + err: undefined, + registeredListeners: [() => {}], + }) +}) + +describe('ethUninstallFilterJsonRpcProcedure', () => { + it('should uninstall an existing filter and return true', async () => { + const request: EthUninstallFilterJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_uninstallFilter', + id: 1, + params: [filterId], + } + + const response = await ethUninstallFilterJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBe(true) + expect(response.method).toBe('eth_uninstallFilter') + expect(response.id).toBe(request.id as any) + expect(client.getFilters().has(filterId)).toBe(false) + }) + + it('should return false for non-existent filter', async () => { + const nonExistentFilterId: Hex = '0x2' + const request: EthUninstallFilterJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_uninstallFilter', + id: 1, + params: [nonExistentFilterId], + } + + const response = await ethUninstallFilterJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBe(false) + expect(response.method).toBe('eth_uninstallFilter') + expect(response.id).toBe(request.id as any) + }) + it('should handle requests without an id', async () => { + const request: EthUninstallFilterJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_uninstallFilter', + params: [filterId], + } + + const response = await ethUninstallFilterJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBe(true) + expect(response.method).toBe('eth_uninstallFilter') + expect(response.id).toBeUndefined() + expect(client.getFilters().has(filterId)).toBe(false) + }) +}) diff --git a/packages/procedures/src/eth/gasPriceProcedure.spec.ts b/packages/procedures/src/eth/gasPriceProcedure.spec.ts new file mode 100644 index 0000000000..838066725c --- /dev/null +++ b/packages/procedures/src/eth/gasPriceProcedure.spec.ts @@ -0,0 +1,51 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { EthGasPriceJsonRpcRequest } from './EthJsonRpcRequest.js' +import { gasPriceProcedure } from './gasPriceProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('gasPriceProcedure', () => { + it('should return the current gas price', async () => { + const request: EthGasPriceJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_gasPrice', + id: 1, + params: [], + } + + const response = await gasPriceProcedure({ + getVm: client.getVm, + forkTransport: client.forkTransport, + } as any)(request) + + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_gasPrice') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) + + it('should handle requests without an id', async () => { + const request: EthGasPriceJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_gasPrice', + params: [], + } + + const response = await gasPriceProcedure({ + getVm: client.getVm, + forkTransport: client.forkTransport, + } as any)(request) + + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_gasPrice') + expect(response.id).toBeUndefined() + expect(response.result).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/getBalanceProcedure.spec.ts b/packages/procedures/src/eth/getBalanceProcedure.spec.ts new file mode 100644 index 0000000000..39adcaacb5 --- /dev/null +++ b/packages/procedures/src/eth/getBalanceProcedure.spec.ts @@ -0,0 +1,63 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { mineHandler, setAccountHandler } from '@tevm/actions' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { type Address, numberToHex } from '@tevm/utils' +import type { EthGetBalanceJsonRpcRequest } from './EthJsonRpcRequest.js' +import { getBalanceProcedure } from './getBalanceProcedure.js' + +let client: BaseClient +let accountAddress: Address + +beforeEach(async () => { + client = createBaseClient() + accountAddress = `0x${'69'.repeat(20)}` as Address + await setAccountHandler(client)({ + address: accountAddress, + balance: 1000n, + }) + await mineHandler(client)() +}) + +describe('getBalanceProcedure', () => { + it('should return the balance of an account', async () => { + const request: EthGetBalanceJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBalance', + id: 1, + params: [accountAddress, 'latest'], + } + + const response = await getBalanceProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getBalance') + expect(response.id).toBe(request.id as any) + expect(response.result).toBe(numberToHex(1000n)) + }) + + it('should handle requests without an id', async () => { + const request: EthGetBalanceJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBalance', + params: [accountAddress, 'latest'], + } + + const response = await getBalanceProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getBalance') + expect(response.id).toBeUndefined() + expect(response.result).toBe(numberToHex(1000n)) + }) + + it('should return an error if the block parameter is missing', async () => { + const request: EthGetBalanceJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getBalance', + id: 1, + params: [accountAddress] as any, + } + + expect(await getBalanceProcedure(client)(request).catch((e) => e)).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/eth/getCodeProcedure.spec.ts b/packages/procedures/src/eth/getCodeProcedure.spec.ts new file mode 100644 index 0000000000..4048848671 --- /dev/null +++ b/packages/procedures/src/eth/getCodeProcedure.spec.ts @@ -0,0 +1,84 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { deployHandler, mineHandler } from '@tevm/actions' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { SimpleContract } from '@tevm/test-utils' +import { type Address } from '@tevm/utils' +import type { EthGetCodeJsonRpcRequest } from './EthJsonRpcRequest.js' +import { getCodeProcedure } from './getCodeProcedure.js' + +let client: BaseClient +let contractAddress: Address + +beforeEach(async () => { + client = createBaseClient() + const tevmDeploy = deployHandler(client) + const { bytecode, abi } = SimpleContract + const deployResult = await tevmDeploy({ + bytecode, + abi, + args: [420n], + }) + if (!deployResult.createdAddress) { + throw new Error('contract never deployed') + } + contractAddress = deployResult.createdAddress as Address + if (!deployResult.txHash) { + throw new Error('txHash not found') + } + await mineHandler(client)() +}) + +describe('getCodeProcedure', () => { + it('should return the code of a contract', async () => { + const request: EthGetCodeJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getCode', + id: 1, + params: [contractAddress, 'latest'], + } + + const response = await getCodeProcedure({ + getVm: client.getVm, + forkClient: { + request: async (req) => { + if (req.method !== 'eth_getCode') { + throw new Error('Invalid method') + } + return SimpleContract.bytecode as any + }, + }, + })(request) + + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getCode') + expect(response.id).toBe(request.id as any) + expect(response.result).toBe(SimpleContract.deployedBytecode) + }) + + it('should handle requests without an id', async () => { + const request: EthGetCodeJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getCode', + params: [contractAddress, 'latest'], + } + + const response = await getCodeProcedure({ + getVm: client.getVm, + forkClient: { + request: async (req) => { + if (req.method !== 'eth_getCode') { + throw new Error('Invalid method') + } + return SimpleContract.bytecode as any + }, + }, + })(request) + + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getCode') + expect(response.id).toBeUndefined() + expect(response.result).toBe(SimpleContract.deployedBytecode) + }) +}) diff --git a/packages/procedures/src/eth/getStorageAtProcedure.spec.ts b/packages/procedures/src/eth/getStorageAtProcedure.spec.ts new file mode 100644 index 0000000000..95dafe712d --- /dev/null +++ b/packages/procedures/src/eth/getStorageAtProcedure.spec.ts @@ -0,0 +1,84 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { deployHandler, mineHandler } from '@tevm/actions' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { SimpleContract } from '@tevm/test-utils' +import { type Address, numberToHex } from '@tevm/utils' +import type { EthGetStorageAtJsonRpcRequest } from './EthJsonRpcRequest.js' +import { getStorageAtProcedure } from './getStorageAtProcedure.js' + +let client: BaseClient +let contractAddress: Address + +beforeEach(async () => { + client = createBaseClient() + const tevmDeploy = deployHandler(client) + const { bytecode, abi } = SimpleContract + const deployResult = await tevmDeploy({ + bytecode, + abi, + args: [420n], + }) + if (!deployResult.createdAddress) { + throw new Error('contract never deployed') + } + contractAddress = deployResult.createdAddress + if (!deployResult.txHash) { + throw new Error('txHash not found') + } + await mineHandler(client)() +}) + +describe('getStorageAtProcedure', () => { + it('should return the storage value at a specific position', async () => { + const request: EthGetStorageAtJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getStorageAt', + id: 1, + params: [contractAddress, numberToHex(0), 'latest'], + } + + const response = await getStorageAtProcedure({ + getVm: client.getVm, + forkClient: { + request: async (req) => { + if (req.method !== 'eth_getStorageAt') { + throw new Error('Invalid method') + } + return numberToHex(420, { size: 2 }) as any + }, + }, + })(request) + + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getStorageAt') + expect(response.id).toBe(request.id as any) + expect(response.result).toBe(numberToHex(420, { size: 2 })) + }) + + it('should handle requests without an id', async () => { + const request: EthGetStorageAtJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_getStorageAt', + params: [contractAddress, numberToHex(0), 'latest'], + } + + const response = await getStorageAtProcedure({ + getVm: client.getVm, + forkClient: { + request: async (req) => { + if (req.method !== 'eth_getStorageAt') { + throw new Error('Invalid method') + } + return numberToHex(420, { size: 2 }) as any + }, + }, + })(request) + + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_getStorageAt') + expect(response.id).toBeUndefined() + expect(response.result).toBe(numberToHex(420, { size: 2 })) + }) +}) diff --git a/packages/procedures/src/getaccount/__snapshots__/getAccountProcedure.spec.ts.snap b/packages/procedures/src/getaccount/__snapshots__/getAccountProcedure.spec.ts.snap new file mode 100644 index 0000000000..d3edd3fb38 --- /dev/null +++ b/packages/procedures/src/getaccount/__snapshots__/getAccountProcedure.spec.ts.snap @@ -0,0 +1,61 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`getAccountProcedure should return storage if returnStorage is true 1`] = ` +{ + "address": "0x6969696969696969696969696969696969696969", + "balance": "0x1a4", + "codeHash": "0x56570de287d73cd1cb6092bb8fdee6173974955fdef345ae579ee9f475ea7432", + "deployedBytecode": "0x1234", + "isContract": true, + "isEmpty": false, + "nonce": "0x45", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x01", + }, + "storageRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", +} +`; + +exports[`getAccountProcedure should handle account not found 1`] = ` +{ + "code": -32001, + "data": { + "errors": [ + +"account 0x6969696969696969696969696969696969696969 not found + +Docs: https://tevm.sh/reference/tevm/errors/classes/resourcenotfounderror/ +Version: 1.1.0.next-73" +, + ], + }, + "message": +"account 0x6969696969696969696969696969696969696969 not found + +Docs: https://tevm.sh/reference/tevm/errors/classes/resourcenotfounderror/ +Version: 1.1.0.next-73" +, +} +`; + +exports[`getAccountProcedure should handle errors from getAccountHandler 1`] = ` +{ + "code": -32001, + "data": { + "errors": [ + +"account 0x6969696969696969696969696969696969696969 not found + +Docs: https://tevm.sh/reference/tevm/errors/classes/resourcenotfounderror/ +Version: 1.1.0.next-73" +, + ], + }, + "message": +"account 0x6969696969696969696969696969696969696969 not found + +Docs: https://tevm.sh/reference/tevm/errors/classes/resourcenotfounderror/ +Version: 1.1.0.next-73" +, +} +`; diff --git a/packages/procedures/src/getaccount/getAccountProcedure.spec.ts b/packages/procedures/src/getaccount/getAccountProcedure.spec.ts new file mode 100644 index 0000000000..5451ec33dd --- /dev/null +++ b/packages/procedures/src/getaccount/getAccountProcedure.spec.ts @@ -0,0 +1,106 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { setAccountHandler } from '@tevm/actions' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { GetAccountJsonRpcRequest } from './GetAccountJsonRpcRequest.js' +import { getAccountProcedure } from './getAccountProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('getAccountProcedure', () => { + it('should get account successfully', async () => { + const address = `0x${'69'.repeat(20)}` as const + + await setAccountHandler(client)({ + address, + balance: 420n, + nonce: 69n, + deployedBytecode: '0x1234', + }) + + const request: GetAccountJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_getAccount', + id: 1, + params: [{ address }], + } + + const response = await getAccountProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_getAccount') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchObject({ + address, + balance: '0x1a4', + deployedBytecode: '0x1234', + nonce: '0x45', + storageRoot: expect.any(String), + isContract: true, + isEmpty: false, + codeHash: expect.any(String), + storage: undefined, + }) + }) + + it('should handle account not found', async () => { + const address = `0x${'69'.repeat(20)}` as const + + const request: GetAccountJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_getAccount', + id: 1, + params: [{ address }], + } + + const response = await getAccountProcedure(client)(request) + expect(response.error).toBeDefined() + expect(response.error).toMatchSnapshot() + }) + + it('should handle errors from getAccountHandler', async () => { + const address = `0x${'69'.repeat(20)}` as const + + const request: GetAccountJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_getAccount', + id: 1, + params: [{ address }], + } + const response = await getAccountProcedure(client)(request) + expect(response.error).toBeDefined() + expect(response.error).toMatchSnapshot() + expect(response.method).toBe('tevm_getAccount') + expect(response.id).toBe(request.id as any) + }) + + it('should return storage if returnStorage is true', async () => { + const address = `0x${'69'.repeat(20)}` as const + await setAccountHandler(client)({ + address, + balance: 420n, + nonce: 69n, + deployedBytecode: '0x1234', + state: { + '0x0': '0x01', + }, + }) + + const request: GetAccountJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_getAccount', + id: 1, + params: [{ address, returnStorage: true }], + } + + const response = await getAccountProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_getAccount') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/loadstate/loadStateProcedure.spec.ts b/packages/procedures/src/loadstate/loadStateProcedure.spec.ts new file mode 100644 index 0000000000..8b0cf00f80 --- /dev/null +++ b/packages/procedures/src/loadstate/loadStateProcedure.spec.ts @@ -0,0 +1,70 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { LoadStateJsonRpcRequest } from './LoadStateJsonRpcRequest.js' +import { loadStateProcedure } from './loadStateProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('loadStateProcedure', () => { + it('should load state successfully', async () => { + const request: LoadStateJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_loadState', + id: 1, + params: [ + { + state: { + '0x1234567890abcdef1234567890abcdef12345678': { + nonce: '0x1', + balance: '0x10', + storageRoot: '0x1234', + codeHash: '0x5678', + }, + }, + }, + ], + } + + const response = await loadStateProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_loadState') + expect(response.id).toBe(request.id as any) + }) + + it('should handle state loading with multiple accounts', async () => { + const request: LoadStateJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_loadState', + id: 1, + params: [ + { + state: { + '0x1234567890abcdef1234567890abcdef12345678': { + nonce: '0x1', + balance: '0x10', + storageRoot: '0x1234', + codeHash: '0x5678', + }, + '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd': { + nonce: '0x2', + balance: '0x20', + storageRoot: '0x5678', + codeHash: '0x1234', + }, + }, + }, + ], + } + + const response = await loadStateProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_loadState') + expect(response.id).toBe(request.id as any) + }) +}) diff --git a/packages/procedures/src/mine/__snapshots__/mineProcedure.spec.ts.snap b/packages/procedures/src/mine/__snapshots__/mineProcedure.spec.ts.snap new file mode 100644 index 0000000000..35a529329e --- /dev/null +++ b/packages/procedures/src/mine/__snapshots__/mineProcedure.spec.ts.snap @@ -0,0 +1,13 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`mineProcedure should handle errors from mineHandler 1`] = ` +{ + "code": -32603, + "message": +"No blocks were mined + +Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/ +Version: 1.1.0.next-73" +, +} +`; diff --git a/packages/procedures/src/mine/mineProcedure.spec.ts b/packages/procedures/src/mine/mineProcedure.spec.ts new file mode 100644 index 0000000000..4d94a3cc52 --- /dev/null +++ b/packages/procedures/src/mine/mineProcedure.spec.ts @@ -0,0 +1,71 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import type { MineJsonRpcRequest } from './MineJsonRpcRequest.js' +import { mineProcedure } from './mineProcedure.js' + +let client: BaseClient + +beforeEach(() => { + client = createBaseClient() +}) + +describe('mineProcedure', () => { + it('should mine a block successfully', async () => { + const request: MineJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_mine', + id: 1, + params: ['0x1', '0x0'], + } + + const response = await mineProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_mine') + expect(response.id).toBe(request.id as any) + }) + + it('should handle mining multiple blocks', async () => { + const request: MineJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_mine', + id: 1, + params: ['0x5', '0x0'], + } + + const response = await mineProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_mine') + expect(response.id).toBe(request.id as any) + }) + + it('should handle mining with interval', async () => { + const request: MineJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_mine', + id: 1, + params: ['0x1', '0x3E8'], + } + + const response = await mineProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('tevm_mine') + expect(response.id).toBe(request.id as any) + }) + + it('should handle errors from mineHandler', async () => { + // Simulate an error in mineHandler by providing invalid parameters or mocking mineHandler + const request: MineJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_mine', + id: 1, + params: ['0x0', '0x0'], + } + + const response = await mineProcedure(client)(request) + expect(response.error).toBeDefined() + expect(response.error).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/setaccount/__snapshots__/setAccountProcedure.spec.ts.snap b/packages/procedures/src/setaccount/__snapshots__/setAccountProcedure.spec.ts.snap new file mode 100644 index 0000000000..ffcb40dd77 --- /dev/null +++ b/packages/procedures/src/setaccount/__snapshots__/setAccountProcedure.spec.ts.snap @@ -0,0 +1,47 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`setAccountProcedure should handle errors correctly 1`] = ` +{ + "code": -32013, + "data": { + "errors": [ + +"Invalid Address 0xInvalidAddress + +Docs: https://tevm.sh/reference/tevm/errors/classes/invalidaddresserror/ +Version: 1.1.0.next-73" +, + ], + }, + "message": +"Invalid Address 0xInvalidAddress + +Docs: https://tevm.sh/reference/tevm/errors/classes/invalidaddresserror/ +Version: 1.1.0.next-73" +, +} +`; + +exports[`setAccountProcedure should return an error when stateManager fails 1`] = ` +{ + "code": -32603, + "data": { + "errors": [ + +"Unexpected error setting account + +Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/ +Details: unexpected error +Version: 1.1.0.next-73" +, + ], + }, + "message": +"Unexpected error setting account + +Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/ +Details: unexpected error +Version: 1.1.0.next-73" +, +} +`; diff --git a/packages/procedures/src/setaccount/setAccountProcedure.spec.ts b/packages/procedures/src/setaccount/setAccountProcedure.spec.ts new file mode 100644 index 0000000000..e3e1fa6927 --- /dev/null +++ b/packages/procedures/src/setaccount/setAccountProcedure.spec.ts @@ -0,0 +1,110 @@ +import { beforeEach, describe, expect, it } from 'bun:test' +import { type BaseClient, createBaseClient } from '@tevm/base-client' +import { ERC20 } from '@tevm/contract' +import { EthjsAddress, bytesToHex, keccak256, numberToHex } from '@tevm/utils' +import type { SetAccountJsonRpcRequest } from './SetAccountJsonRpcRequest.js' +import { setAccountProcedure } from './setAccountProcedure.js' + +let client: BaseClient +const ERC20_ADDRESS = `0x${'69'.repeat(20)}` as const + +beforeEach(() => { + client = createBaseClient() +}) + +describe('setAccountProcedure', () => { + it('should set account with provided parameters', async () => { + const request: SetAccountJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_setAccount', + id: 1, + params: [ + { + address: ERC20_ADDRESS, + deployedBytecode: ERC20.deployedBytecode, + balance: numberToHex(420n), + nonce: numberToHex(69n), + }, + ], + } + const response = await setAccountProcedure(client)(request) + expect(response.error).toBeUndefined() + + const account = await (await client.getVm()).stateManager.getAccount(EthjsAddress.fromString(ERC20_ADDRESS)) + + if (!account) throw new Error('Account not found') + + expect(bytesToHex(account.codeHash)).toBe(keccak256(ERC20.deployedBytecode)) + expect(account.balance).toBe(420n) + expect(account.nonce).toBe(69n) + }) + + it('should handle errors correctly', async () => { + const request: SetAccountJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_setAccount', + id: 1, + params: [ + { + address: '0xInvalidAddress', + deployedBytecode: ERC20.deployedBytecode, + balance: numberToHex(420n), + nonce: numberToHex(69n), + }, + ], + } + const response = await setAccountProcedure(client)(request) + expect(response.error).toBeDefined() + if (!response.error) throw new Error('Expected error') + expect(response.error).toMatchSnapshot() + expect(response.method).toBe('tevm_setAccount') + expect(response.id).toBe(request.id as any) + }) + + it('should set account without optional parameters', async () => { + const request: SetAccountJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_setAccount', + id: 1, + params: [ + { + address: ERC20_ADDRESS, + }, + ], + } + const response = await setAccountProcedure(client)(request) + expect(response.error).toBeUndefined() + + const account = await (await client.getVm()).stateManager.getAccount(EthjsAddress.fromString(ERC20_ADDRESS)) + + if (!account) throw new Error('Account not found') + + expect(account.balance).toBe(0n) + expect(account.nonce).toBe(0n) + expect(bytesToHex(account.codeHash)).toBe(keccak256(new Uint8Array())) + }) + + it('should return an error when stateManager fails', async () => { + const vm = await client.getVm() + vm.stateManager.putAccount = () => { + throw new Error('unexpected error') + } + + const request: SetAccountJsonRpcRequest = { + jsonrpc: '2.0', + method: 'tevm_setAccount', + id: 1, + params: [ + { + address: ERC20_ADDRESS, + deployedBytecode: ERC20.deployedBytecode, + balance: numberToHex(420n), + nonce: numberToHex(69n), + }, + ], + } + const response = await setAccountProcedure(client)(request) + expect(response.error).toBeDefined() + expect(response.error).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/utils/__snapshots__/blockToJsonRpcBlock.spec.ts.snap b/packages/procedures/src/utils/__snapshots__/blockToJsonRpcBlock.spec.ts.snap new file mode 100644 index 0000000000..0541a5e48f --- /dev/null +++ b/packages/procedures/src/utils/__snapshots__/blockToJsonRpcBlock.spec.ts.snap @@ -0,0 +1,479 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`blockToJsonRpcBlock should convert block to JSON-RPC block format with transactions 1`] = ` +{ + "baseFeePerGas": "0x836f9", + "blobGasUsed": "0x0", + "difficulty": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x", + "gasLimit": "0x1c9c380", + "gasUsed": "0x572821", + "hash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "logsBloom": "0x27806004c206102500c1228040107a8990090148034019c8840c0948044302008080101080100214da000093100080400400884c1401c000882086c0c42000d226009200000402042012802c80248b0160071813226c04280023606424040d440202a18012000010254004c020112d8004204030102804042323001120088000c010030c30218502010800010020804442016020a02e0060422a40084224c4081e130002000082200018268280c106a00318000908a021022842502108200100e000000b118209410040c0420410144028c0000a0020604280200082050422310071280a002414800590f0c8cc0842226c8002d0101d06b90e0c85030609140c", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0x845b54e141b5614790a68e6b7d2020fd08500b318e79a0054b52e63984082a89", + "nonce": "0x0000000000000000", + "number": "0x744f93e", + "parentBeaconBlockRoot": "0x28ed5fb7d646679dc167f7fa6267f3796707caf19ba80c7d1e89d5436495969c", + "parentHash": "0x81fa6aafd3f57685aaf367381471ab15ad294450472455503f71395b1f6272a5", + "receiptsRoot": "0x60c9f088c98590a865d6ff54163d09f61c9e24927922ab8310375aa62ad9abc8", + "requests": undefined, + "requestsRoot": undefined, + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x120d6", + "stateRoot": "0x2f49385f03516638a9df2aec8f280441d6e2ac206a671be95cda6c8bd4a7caa1", + "timestamp": "0x667dcc35", + "totalDifficulty": "0x0", + "transactions": [ + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x", + "from": "0xc7c413d201902895a727e336522a17d820941a49", + "gas": "0x5208", + "gasPrice": "0xa313b4", + "hash": "0xf45a5dd2622427c446b41e784bc5a6223fedef39313786169142c74bd8caa1ea", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0xa313b4", + "maxPriorityFeePerGas": "0x989680", + "nonce": "0xbd85", + "r": "0x984f49a0a0d0d3e7162de9d606236e588077b272884bfe9dd5d0cfa46838f0cc", + "s": "0x42ded60f2512746ed3bac43de7109e39e6a35dc069a542e421b0dc909b6df419", + "to": "0xb6524430e06a60c8a6e33b4a2a03100f5bb91be2", + "transactionIndex": "0x0", + "type": "0x2", + "v": "0x0", + "value": "0x12309ce54000", + }, + { + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x64778c1f00000000000000000000000000000000000000000000000000000fa2f70cd41d3533343335320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c3ad000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000040236e2dd05d9d6c7707d4e96efa56375b45da9c", + "from": "0x00051d55999c7cd91b17af7276cbecd647dbc000", + "gas": "0x25488", + "gasPrice": "0x19c6ff", + "hash": "0x39fe544fde06be0de3793d0816dbab06d81c15eeffe7aa3b66f332c9d38bc508", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "nonce": "0xb702", + "r": "0xe018a0ed0e3eff4a58b6fe986c191045f19d93ae6c5c18dc22b564cad406a906", + "s": "0x10ba466809dd076fbda3a08a12133c55c8d5463f616535575ae4c63f63c430c7", + "to": "0x8201c02d4ab2214471e8c3ad6475c8b0cd9f2d06", + "transactionIndex": "0x1", + "type": "0x0", + "v": "0x38", + "value": "0xfa2f70cd41d", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0xb1dc65a40001cc80e588de14e30f262975c1c65d9408ec7b34e157f43bd4bac65030b7500000000000000000000000000000000000000000000000000000000000a84301bd9ab49adc684fcdc47cacf7bbfa08061f1b217d565fe59df9ead7a0a9d3210c00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000001b60000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000013a0000000000000000000000000000000000000000000000000000000000000194000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006e0000000000000000000000000000000000000000000000000dda641cfe44aff82000000000000000000000000e1c14b9f065dead2e89ee35382f8bd42bdb87a04000000000000000000000000e1c14b9f065dead2e89ee35382f8bd42bdb87a040000000000000000000000000000000000000000000000000000000000005297000000000000000000000000000000000000000000000000000000000005573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b5c0000000000000000000000004200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000043e3c7de748900000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000000000000000000000000000000000000000600436024b4f51b02434485531177fba70f6ccfc801724a444df8207a5f4a51210000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000130805f7214fa21b1abaee76728ad896434be7c30000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff8500000000000000000000000000000000000000000000000000000000000ee43d00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000006423b872dd000000000000000000000000e1c14b9f065dead2e89ee35382f8bd42bdb87a040000000000000000000000006d7bd5c0c207cba1377f58048aa5f8c5cd93361300000000000000000000000000000000000000000000000000000000000ee43d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000130805f7214fa21b1abaee76728ad896434be7c300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff8500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000ee43d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000033aec0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000dda641cfe44aff82000000000000000000000000e1c14b9f065dead2e89ee35382f8bd42bdb87a04000000000000000000000000e1c14b9f065dead2e89ee35382f8bd42bdb87a040000000000000000000000000000000000000000000000000000000000005298000000000000000000000000000000000000000000000000000000000005573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b5d0000000000000000000000004200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000044121ff2268b00000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000b8007828d285760686a2d6b31fc0a8fa39e88f3c694aacacd4c4caf01e14b5abcae000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000000200000000000000000000000008e015145235e49f60e36376b84a8872688daf099000000000000000000000000da10009cbd5d07dd0cecc66161fc93d7c9000da100000000000000000000000000000000000000000000002bdc394fd023afc48300000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000006423b872dd000000000000000000000000e1c14b9f065dead2e89ee35382f8bd42bdb87a040000000000000000000000006d7bd5c0c207cba1377f58048aa5f8c5cd93361300000000000000000000000000000000000000000000000000000000302ac75100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000111111125421ca6dc452d289314280a0f8842a6500000000000000000000000000000000000000000000000000000000302ac7510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111125421ca6dc452d289314280a0f8842a65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000030807ed2379000000000000000000000000b63aae6c353636d66df13b89ba4425cfe13d10ba0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000da10009cbd5d07dd0cecc66161fc93d7c9000da1000000000000000000000000b63aae6c353636d66df13b89ba4425cfe13d10ba0000000000000000000000006d7bd5c0c207cba1377f58048aa5f8c5cd93361300000000000000000000000000000000000000000000000000000000302ac75100000000000000000000000000000000000000000000002b33ccee9da845737b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001b300000000000000000000000000000000000000000000000000019500016700a0c9e75c48000000000000000008020000000000000000000000000000000000000000000000000001390000d600a007e5c0d20000000000000000000000000000000000000000000000000000b200004f02a0000000000000000000000000000000000000000000000008b4c3d804b95e4a6eee63c1e501e2101d19b78e695982975cc1a27b6a9117e02b090b2c639c533813f4aa9d7837caf62653d097ff8502a0000000000000000000000000000000000000000000000008a40ee963e68e3625ee63c1e5819438a9d1bdeece02ed4431ac59613a128201e0b98c6f28f2f1a3c87f0f938b96d27520d9751ec8d9111111125421ca6dc452d289314280a0f8842a6502a00000000000000000000000000000000000000000000000228fbe0539c1b73d56ee63c1e581d28f71e383e93c570d3edfe82ebbceb35ec6c4120b2c639c533813f4aa9d7837caf62653d097ff85111111125421ca6dc452d289314280a0f8842a650020d6bdbf78da10009cbd5d07dd0cecc66161fc93d7c9000da1111111125421ca6dc452d289314280a0f8842a650000000000000000000000000066e0bbe300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000da10009cbd5d07dd0cecc66161fc93d7c9000da1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000008e015145235e49f60e36376b84a8872688daf0990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000da10009cbd5d07dd0cecc66161fc93d7c9000da100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000302ac7510000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000033aee00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000f80000000000000006000000020000000000033aec0000000000000000000000001682ae6375c4e4a97e4b583bc394c861a46d89620000000000000000000000002b4069517957735be00cee0fadae88a26365528f00000000000000000000000004336603557feb138b36075156e92f9e551dfc5d00000000000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000e1c14b9f065dead2e89ee35382f8bd42bdb87a0400000000000000000000000000000000000000000000000000000000000ee43d00000000000000000000000055a5786ca51c31623f3efb8bbfcc8df9a4c61ba900000000000000000000000000000000000000000000000000000000000000000000000000000082877cf59fc5408d8fff1034dee9d8595f67f9b902b1f6a1d6e234f1902951c83e542ab900c569f1ee2c9835650f9deefcb00b058198293c89044c850def1c70921c95e4028d24abe2bc0d5617922b66e387cfd9947955426864c7939e1aef62e7411da05833f93aa01a12da708e35573d79c6caba4dfe584ba483d7d493be4d4e591b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000f80000000000000006000000020000000000033aee0000000000000000000000001682ae6375c4e4a97e4b583bc394c861a46d89620000000000000000000000002b4069517957735be00cee0fadae88a26365528f00000000000000000000000004336603557feb138b36075156e92f9e551dfc5d00000000000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000e1c14b9f065dead2e89ee35382f8bd42bdb87a0400000000000000000000000000000000000000000000000000000000302ac75100000000000000000000000055a5786ca51c31623f3efb8bbfcc8df9a4c61ba9000000000000000000000000000000000000000000000000000000000000000000000000000000827539a2d6092430451cb14e624ad251da564c13eec1842c16ec26414aca8acada08ee516d229381214f05df8b9a65854ed74823f34284fe2a347d545e15cf720b1b4ad11726a6cb285cfb51259aaf2e6b5ff2ec2cd2a1bd764cfbdf02b7c59388d273e5b1699e372a1378b6f5d60fe553f56878745fa1abc9b1f0e398620ddedbc11b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065ae42512d32acb5595449375b73a63f09261b70e2576f7ca2818de375796cb54070e9cc43aaee429344c58740607a90a2b702aceaad72f87929dec6e55fda62a34e7d744b33118c01794332690915eb19eb7129b3ed6b5b071b434a63f4f000b39fcaf2a9b6d0cbe247f106c3c59f3d94b974ec037853345b5f4799e4bc8a3c237597d51d8f9ce160cdab08feff7ffdad44cfd4d7fe2f46ba20d80acfc1ca0a23adfd6b7a26d9725b2c1c37f619060d3081f8e1a3c33947c0e83e9df3fb3641e000000000000000000000000000000000000000000000000000000000000000608fbddc85129ef6410b1ab538e43087fe916e21f801c97f547096a86f79220813ba68f75864a0e41fd8a5ebf96055b09b446ef61a6dbc8b0753ba15e71bd0196070eb4eeee7e654ae1b473112a0aa37cd7d55fab0d7ab3d4b024f13f3f56b615010711700aec4ac924d155163d272bd2baf72b3b9cf3a1a6eb980bb66c3c073c749d6694030d3dea25ca38c14def87d69a81bfb8e1519da760a8b8fe13bda42d4901fbb250cebf257792de0fe8566756f63b2f9c06538b04cfd6ecc2a561c2d4", + "from": "0xfcc955232d26348385237a0ba2b5428920b0abec", + "gas": "0x7a1200", + "gasPrice": "0x18bdde", + "hash": "0x5a8e7264d069cda182116f2c9972374de50bdd6f2b240e4039b2ec824b8224b5", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x18bdde", + "maxPriorityFeePerGas": "0x10942d", + "nonce": "0x8e9e", + "r": "0x1800af107deab44fb74bbe44cf9f5f10d5df2293797d2895bd86ea5485307fe8", + "s": "0x319259a5e875256bf5b17a4641addd7407764f5040882ef66fac0152f40c5959", + "to": "0xbae6560eca9b77cb047158c783e36f7735c86037", + "transactionIndex": "0x2", + "type": "0x2", + "v": "0x0", + "value": "0x0", + }, + { + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x94b918de051200667dce78000000000006a94d74f4300000000000000000000007308e8e", + "from": "0x1d3286a3348fa99852d147c57a79045b41c4f713", + "gas": "0x7a120", + "gasPrice": "0x17ab14", + "hash": "0x5410ce3440e83894b2f6d5f31b17926833986212e79295edb35e6340aa071a67", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "nonce": "0x7c274", + "r": "0xbbcca1332b1ab1b1a3e35f9d79fb9b4c3b8a36f21bcbf86300c58185ffe8e2e6", + "s": "0x3fe80eb40ce628c1f459e930663082fe2129b5904a471ce88388793f994b33df", + "to": "0xfe8b32439bb2f222d031a42c6e7c8964e20e0a33", + "transactionIndex": "0x3", + "type": "0x0", + "v": "0x37", + "value": "0x0", + }, + { + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0xd123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000209067188e0c46419ec3e06242f60ed1135f372759d597fe73bc1c73384d9e4f01f15ab9eea6fcdf81260e97412cb07e000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000006deae4c132f4a0343eed2518eba79c969272721d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d594c725136765367633376536f6a6552767a69463474574247714a4e735a6d77424269434a375a793452763700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041436db5a081a19b7a8eb9f53b2e0077499c0d7fb5a89dc071e965cb5a7d5c49ed1e84a0a7ff2a7ff1a5594ecb34bfa9c33122e5e4643396d8b33153b4dee49ced1c00000000000000000000000000000000000000000000000000000000000000", + "from": "0x6deae4c132f4a0343eed2518eba79c969272721d", + "gas": "0x48452", + "gasPrice": "0x17a6b0", + "hash": "0x0cd45ed7f3a1f9a4d2928e52d102de7514692b401eb126abd1629bf8221a0e3f", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "nonce": "0x25", + "r": "0x70a8967123f2204a8b07e254112aa89cd51a984471c284d1f8185edb63876970", + "s": "0x2d353dc11c6a5e415b222d30424f680c594bbbd7c6c01c8b0e63709ba3f1680d", + "to": "0x1195cf65f83b3a5768f3c496d3a05ad6412c64b7", + "transactionIndex": "0x4", + "type": "0x0", + "v": "0x38", + "value": "0x44364c5bb000", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x01617fab00000000000000000000000000000000000000000000000000000000000187a08000000000000000000000000000000000000000000000000106f8cc3e464000", + "from": "0x6ed30aa3a90aacbf754c6b733efe4f1c3f56a284", + "gas": "0x27768", + "gasPrice": "0x1f7764", + "hash": "0xcaa08692ca02e911c8418907b8fda455e4b9d08561284b2443b6f43e4eeacd02", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x1f7764", + "maxPriorityFeePerGas": "0xf4240", + "nonce": "0x2", + "r": "0xb1fcace0de4a697c6fcf56d4d611cffd36806bf306b1fd3631a5b133058f27c6", + "s": "0x6f123519f4fb4139f8d8045af90c8f1bb9dd04e4495a554ec330f8ec6b95f670", + "to": "0xf332761c673b59b21ff6dfa8ada44d78c12def09", + "transactionIndex": "0x5", + "type": "0x2", + "v": "0x1", + "value": "0x0", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000000000000017adb406", + "from": "0xb1feb9a7c2d170d506f1d0642a43c5d379a7e08b", + "gas": "0x1499a", + "gasPrice": "0x1b8582", + "hash": "0x4e0a2f705ac50c846edf1ac7eaabc59f5e80b9d8f1754cded0a42f3b3bd4b45e", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x1b8582", + "maxPriorityFeePerGas": "0xf4240", + "nonce": "0x7", + "r": "0x71d5f1c58a2f7a99b934f7c985a8ba675b835331fbcc89287e0293b055fb26c1", + "s": "0x666153b6fd5ee7cdbbb973f0ef86e266aec7cd4b0c1d21c5902e9790efc450e4", + "to": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", + "transactionIndex": "0x6", + "type": "0x2", + "v": "0x1", + "value": "0x0", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0xcf514dd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000008000000000000000000000000e254240867a8358b5da3acb51303b973f72fdaed00000000000000000000000041448012b6302721ee660e3fa6e0cb761b0c61e9000000000000000000000000239903ccfb6affbff410fef2e6581426e878c3f30000000000000000000000007f66c77bd4efc0f1f9e57a66db14c8d5a4ea09e20000000000000000000000007bf30630c8a084e72ad29d761f41ab23eebdc697000000000000000000000000f123340932f53bb85b1e92db88b2a0192a869c9900000000000000000000000040b5f28d20ff22a6ff6fbf1a3e24d763a1eafa30000000000000000000000000643c56a88caf07171b529024571b438c4558f517000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000987224", + "from": "0xc97240c92596276b8b9366064123fd76a1207164", + "gas": "0x2ab980", + "gasPrice": "0x18c4f6", + "hash": "0xaa1e0c2e4c92ce05d83eb94560a150de5aeeb5e33d6cc476d6b47bf140c4c7c1", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x18c4f6", + "maxPriorityFeePerGas": "0x888c0", + "nonce": "0xa98e3", + "r": "0x72533c2f707bebeb4f276a7aa1cdf570e1f756dab093c57fb83080b17217d5fd", + "s": "0x12d4d6dd73f8f59da787e09f621dcac58c5e4534e2e791234be04fbb74ba2b95", + "to": "0xc0edd4902879a7e85b4bd2dfe293dbec4d838c2d", + "transactionIndex": "0x7", + "type": "0x2", + "v": "0x1", + "value": "0x0", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x82ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d00000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000007600000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000d20000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000012e000000000000000000000000000000000000000000000000000000000000015c000000000000000000000000000000000000000000000000000000000000018a00000000000000000000000000000000000000000000000000000000000001b800000000000000000000000000000000000000000000000000000000000001e600000000000000000000000000000000000000000000000000000000000002140000000000000000000000000000000000000000000000000000000000000242000000000000000000000000097fb709f3a935d7d30fc3b1e154002eccc8d4ed00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000001054b6de99d3ef61243c759a8e89971e4179fb4000000000000000000000000000000000000000000000000b469471f80140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041afbb155be75d0d02f5fb13309f6aca3098caf0fe0e772af827d3533692ab687541c501103831409c8e231d25752b6c71e90c785c9cd26ed8c234219fae99ba3e1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074e5e3578dce8836b043d7bb5562f2ce65e0343a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000939c338abe920b4d5fa4a0a2fb394e398e2783ac00000000000000000000000000000000000000000000000029b3e7942e0c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a49b9bc9b34fc81d540efa10d92c363263d954fb3eadfc0eca87fda9a8bd090a017c6fa0227e86b5b6c1ecfdf0759e95bfda8fbca0f96574e82bc497a8f9d12b1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b190539c6fcf317ebbb0f413ab599ad203b716c50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000003687017c6cb44f794abfd6396fa86d2c22c89da600000000000000000000000000000000000000000000000029a2241af62c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414b8df31256f2db82602fe2a2c94956af97dff2dd4d80303f97eb9974af2cea9228fae3a18e8918cc44e673bdf64eac00ef10cc76d026fb1f44026ef4cfb695d41b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000368dfd404069ccf324084e0a805ae7e43a77c8540000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000000a650b4fbe69867f6e9403ce37f5654c1e26f05f0000000000000000000000000000000000000000000000000005b677accc80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417654a228172d08f98e4a094bf37f985723f1a2ae0f7b8aef8018e4c0fd2e629a09463a379e9ca18070b22035ff6ab14197bb03ae5cef04f82f6a6eb62cbc1ba31b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008e807b6d81e61cd04c8939fbd4c18a281cb807fd0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000080ce3de69efa9049f851f8edaffbdc0fe8274b4f00000000000000000000000000000000000000000000000029a15c0438524000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041e8a235c6d42ecb572c036c5075679f8445c0c175136d10789eda6678c755084b54fd3e8848c2ec04738342106197c986feb6e108394a5aa868da40726566a92b1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000565451717eb507f822d539833048a2861cd03bf0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000072a45291e748a660d81ccc88ce2a9588900f7f72000000000000000000000000000000000000000000000000000d59e18f10e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004193db00abc8648bd1b47a45592f98fb3227531c34d5783dcf45cc1a4701a7fa88357cc627133fee89ad47c2c33e0ea93fa4fa84ab10ed68680b9bbc070e0d6dda1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044d886d33c597ad54f7695ab28ad18192b5480150000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000005a13ec194649b4c416996f4a46dffd649faf404f00000000000000000000000000000000000000000000000029a2241af62c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041ddb86b078e6fa418d17d6407bbb54b1b62f7f8d9b9b9476b6993fec52efe78e4340287dd0ea82ae5bfd477b8d0d0320b5de57a24e80a133aee50efa1dc0d7d1a1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096d06d3e5e36809ea27c05931212e653614f23a80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000311097175d1687780c8a08d108feb0e85ddc7e5300000000000000000000000000000000000000000000000029a24fc2d51f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417536d7886786a0389bb401ef33dbf3df7c9b16f7a71f71ac7ee4c49d9bfb0f7e533c2ba011f132a4a89f2d39c8aaa8b5185bf86c74fe087fe515b631cbeffe0e1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0d854a03e8f7ab6f146684a2611614bfc2d15a90000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000635d5bbf389c218b0d07a960c677c9a62f715bc600000000000000000000000000000000000000000000000029a2241af62c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000416b01a73e3b1c804519a0ccb3f13f7332516f098d1e5b7c9576abc157222e4e334f72cd67b406369bc1a53b49e8d80d0a210ba8d23ab82451bd33a6b3d34c3f481b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000807b3739d32a32fbe1293ff69e6adcd7101083510000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000074570048267800aafc2e33bc59ab49f9454babcc00000000000000000000000000000000000000000000000029aae25f2cf6d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041e76a41e4556673bdf09e6a64bec5b142b5e3e54cd5db63d65f2874fb9f0781ea06a142a49e20cc0d6bd33773697d282b98fbc046ad1562d07aa328cb2eee258d1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001aab339e2fbb0958cf6265be0c7108628b2d4c310000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000005a13ec194649b4c416996f4a46dffd649faf404f0000000000000000000000000000000000000000000000007c9b9889de41a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417e2e1a65d9f491e41f867b5e585d969952c1b06f990f7c30016b35ee32afff6777521a386e7fc2d3f14222a9b8c2f18110b3f23f6cd02e252b4b8ccd2db037021b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0a907802bbd3a4ff731767302d611a93de81c1b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000001449e2de409729ec20076a0143fc6040be8e0aa900000000000000000000000000000000000000000000000053444835ec580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b9fb4cc328c4bddd5dfebd379b8db82bfa1534847e51b08b73aa419a5a96368e4ac0028e8624846080b752900955f25b63399753376dc769328884007eafb62a1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074aa81989fc71f20dccb9d8d554f090110f638520000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a7612020000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c316070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000c703796525a55e12ebe3672ddbbc624d950e3cc800000000000000000000000000000000000000000000000000000000007f0b390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417234a639b9c3f1167696117774f718889df77eb017dd5494585b7701c7cd747969f25e9f8cdf0ed92c53d3f3741f5a9157d70790498154ed3b915f7cba5bdb1b1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "from": "0xe168f61309fb6bd28ff05a24df28da856a53b587", + "gas": "0xba548", + "gasPrice": "0x183d84", + "hash": "0x9402b9dd885ec6ee9b3f0955fc7c18eea975310d077bfca1a9397bae2672fe5b", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x183d84", + "maxPriorityFeePerGas": "0x7f19e", + "nonce": "0x67d7d", + "r": "0x22e5651bc0f57549b3c9533b847c74aacb962355ffc256b3fc305419906dddd7", + "s": "0x7750dbeb193be9a245de4edd82f9fe2659e55de2a8333be0f53a0bfac1e95f80", + "to": "0x087000a300de7200382b55d40045000000e5d60e", + "transactionIndex": "0x8", + "type": "0x2", + "v": "0x0", + "value": "0x0", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x170dce2f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000400000000000000000000000042389c7c9f955ca8bea45a699dfd43d384306d120000000000000000000000000000000000000000000000008ac7230489e800000000000000000000000000007a7b6fc862decb0f781f3863c46291d3800e02bc0000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000e78d20c3d096573cc48fa5df58018d4b11dd2f450000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000cc14a5f6ea075dbe43c440e6eac2f23e8bf305c10000000000000000000000000000000000000000000000008ac7230489e80000", + "from": "0xcac1a7ce337957bd737aba084fe0441392e97b21", + "gas": "0xf4240", + "gasPrice": "0x183d84", + "hash": "0x87bc00715ecb845746b0b26477f6aef712e54d56b7dc0c3e437f30a64eeca0b1", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x183d84", + "maxPriorityFeePerGas": "0x7f19e", + "nonce": "0x71aba", + "r": "0xa01e251b6a70c80462b12e04723f4f69d374861c8a3d60eef3721d9b59e8b56e", + "s": "0x62912f7c4b9050cebef85326054777789484b09cd834a781fe9884dc86073fce", + "to": "0x6b3872e786db187c311479ed3d1513a244e31b68", + "transactionIndex": "0x9", + "type": "0x2", + "v": "0x1", + "value": "0x0", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x82ad56cb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000000000000000000000000000000000000000009c00000000000000000000000000000000000000000000000000000000000000ca00000000000000000000000000000000000000000000000000000000000000f800000000000000000000000000000000000000000000000000000000000001260000000000000000000000000000000000000000000000000000000000000154000000000000000000000000000000000000000000000000000000000000018200000000000000000000000000def067d861a988dce345dee8d0824f8c499a91d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000000fc7474b5981beea7593941608ddad7c7c431b8500000000000000000000000000000000000000000000000053444835ec5800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415d8d6108ce954be3020a7dd8b550e6c242050034fd8f27442eb5c399a4ccbd2c5a3372eedfddf3de5117dbd610f88b51106ead29794a273b0ec1ebc49a6af6d51c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f6e5e0ab88ab8b31fbfc1556ae413079202cb490000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000006f258145180a00b7b6f86a2815c02e09c63d50220000000000000000000000000000000000000000000000004563918244f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004177fce869d7c72760ff8ea610c51a4ebce6622ebbaf55969641b0b726be7661cb6715a038313bc9e5b41fa14f029b6b03bbd1b606d05e7aed92497b73b8357d1a1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eb9d53ba8e31778ecf7c6153fdb78c2d5c114a490000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000007e8d6942d9f3094a0ee531c4cf6a4fba4126a2b80000000000000000000000000000000000000000000000005344a06e7ee31000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041913834e70f52cd771c833923920225203812ad420eccb83844d37e1ea8f500e01dff9bf33c633bfcc20501a0baced8b39ac9dd27a1a695bfd1cfa2a10eb043ec1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004dbeca976e484d807bd9c207c9299fdb6ab9fcdc0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000083157bdcc1b4acf67cdff87f42f9ca2b64f8fc4000000000000000000000000000000000000000000000000053439f0b6c6a60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415e61db26c75800c672b1577d91535cca6757970cf34783b41ea56ee0f122e04310ac49ea562b98aeb3b650fd0f2eeee6c5c181b634ae0eb3da83dfdf62da795d1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b175ab8424aad0bae554c71534062c6ae43208990000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000000dcb7416d12506ea1b6988b945409751c69b20d600000000000000000000000000000000000000000000000029a27080bc554000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a36a1acb5c90ea5fd4e316178e824d8ffa1a07e6859afb374c1699714a5d42b76544cf2cd4222e51ef15e5d494c0eff7ed898ebfd4dae4219f6f56a546fa638a1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090b6713f8d6d7c3ee55bf43f8a1a331aaadbe8980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000001dbcac0ae8ecf9a1fba1cdec65f8d47ea5ec1a2700000000000000000000000000000000000000000000000029a2241af62c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412202274d5e5651877760c9b04ca4b26797ca8dc6433f3abbfacb027bc4c63bd7687864909a91628fcd6b90424c661fea4ee5c13134c9ffca6048468d16d047cb1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003441992c9d65925a4dc9af2d9da9e7b375800d6b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000004a1e66a859ad4379354c5156766c26fb3cfe91b60000000000000000000000000000000000000000000000004564eec13c8c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412e39ce66daed0ce90e7ed2dddfd6e77c1519ba9ea9ec153ece4064057988f55937a31ead2a89b399516c9e1a5dbc8d4755f034bc7a053c3a154d64a89991e4ce1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f20c07401880660b13e5679e4a96e94967edfb2e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a761202000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000007ced2ffb4b2237c5c1bc340394a8e079120e614900000000000000000000000000000000000000000000000025637b27fa2c13fc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041aa80d0ea3777d6fd5aeafa72320eb4887c99dc44d8949dc6a818f46c2212fb7760acc8aef2a32e45aeefd794e8afdf373cc250d724b9c553a260584aef9646381b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070b5aac7fb146f680db1ab1c6c9429ccf56c9a4d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002446a7612020000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c316070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000ee4e48c7c320b8b85f2fdaff44b6683c1b57cee7000000000000000000000000000000000000000000000000000000000082f65e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417235d06ddbd83849bec8baf6c023f23e0b0d8613083587a9f3ebb3dc2c1cd63b1cafac6c9ea46f7eb67e6c2825e0b7c291fcc610578a5bd4ad21320f4ac47f601b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "from": "0x6809501ef4df4a4c78024bd50ba4bef3d5af17a9", + "gas": "0x8053b", + "gasPrice": "0x183d84", + "hash": "0x7d384bba0916da82117d02c98357c0575877632f2cb58d596874b53bbc6499df", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x183d84", + "maxPriorityFeePerGas": "0x7f19e", + "nonce": "0x158ced", + "r": "0xace37b119f045e37f36e3d446b6578b5aaafcf361b53ced6e0d74828cf73bf44", + "s": "0x10e8899dc6e585ff97830d2cac411b78915ad132226ba6e6defc4d3923dda385", + "to": "0x087000a300de7200382b55d40045000000e5d60e", + "transactionIndex": "0xa", + "type": "0x2", + "v": "0x0", + "value": "0x0", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0xd123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000998835d383c24f2ab854cd398acda64280a1af1f1914f2dadab4a8311d51f240a9ba400d3f9c593c98ad360169caa714000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000040a3f053ee9339ab067b539fcf731b548286ea5e0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000004200000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007d00000000000000000000000005e2b297016d41055876ff1078100c548adc3a49800000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5732594574747a706355354e3678745967754a69576b586d3331456e484e43635a7450394b324863706e37750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004161f89b152d892aee49b1e841f43fcbe0809d7b1d9f5e8a707ebafb4c691510e008438e105111a7f5d39436d95160049402a95ca07e21a183b7572c5dfd3463341b00000000000000000000000000000000000000000000000000000000000000", + "from": "0x40a3f053ee9339ab067b539fcf731b548286ea5e", + "gas": "0x4898e", + "gasPrice": "0x201770", + "hash": "0xa12044d708dfd32d61977e92a4ae0ba7d59a54208ff28a69078dd896a9ae8106", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x201770", + "maxPriorityFeePerGas": "0x760b8", + "nonce": "0x61", + "r": "0x5afb51f2439a40be72354bf4dcca72b18938e985d5a9eb2c2927c56c1e99d006", + "s": "0x5f96699990bb53a6e0c7a7f7c097892930d6b08c21dfbc4293329aaacb8b405d", + "to": "0x1195cf65f83b3a5768f3c496d3a05ad6412c64b7", + "transactionIndex": "0xb", + "type": "0x2", + "v": "0x0", + "value": "0x44364c5bb000", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0xd123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000998835d383c24f2ab854cd398acda642469c84d4a2e73322ce3486380aed7c7c6e613299e111769d7b55c6c941793f15000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000212bce405a4f0b593d511616bb48881449492f210000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000004200000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007d00000000000000000000000005e2b297016d41055876ff1078100c548adc3a49800000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d535747506154515667506e796f487274673232464d793161466b313270636f43354444334b7a77694269513800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041ecd196d983fd40b1d4aebc42452a21f4a86b7d5a4c11735c50a56ee2ed0d337058c577389a2f530634c3a73b207b99eedcdae160f0de65b6bf8114db7e716e1a1b00000000000000000000000000000000000000000000000000000000000000", + "from": "0x212bce405a4f0b593d511616bb48881449492f21", + "gas": "0x43125", + "gasPrice": "0x201770", + "hash": "0x9e2f238a7f7e1d9014363209b19e9957fdf201c2d82fe9dbfb70c3571053221d", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x201770", + "maxPriorityFeePerGas": "0x760b8", + "nonce": "0x78", + "r": "0x5d964fbe36168193dce66df1fd44fe1388704ddb2776596f0753060dd5217bc0", + "s": "0x65a00a53828b4f5addab2151151fd5da1b53327da91d275883d12105bfd72e4c", + "to": "0x1195cf65f83b3a5768f3c496d3a05ad6412c64b7", + "transactionIndex": "0xc", + "type": "0x2", + "v": "0x0", + "value": "0x44364c5bb000", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x608060405234801561001057600080fd5b506040518060400160405280600b81526020016a10dc9e5c1d1bd09d5a5b1960aa1b8152506040518060400160405280600481526020016321a9213d60e11b81525081600390816100619190610210565b50600461006e8282610210565b5050506100a0336100836100a560201b60201c565b61008e90600a6103cd565b61009b90620f42406103e3565b6100aa565b61040d565b601290565b6001600160a01b0382166101045760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b806002600082825461011691906103fa565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b505050565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061019c57607f821691505b6020821081036101bc57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561016d57806000526020600020601f840160051c810160208510156101e95750805b601f840160051c820191505b8181101561020957600081556001016101f5565b5050505050565b81516001600160401b0381111561022957610229610172565b61023d816102378454610188565b846101c2565b6020601f82116001811461027157600083156102595750848201515b600019600385901b1c1916600184901b178455610209565b600084815260208120601f198516915b828110156102a15787850151825560209485019460019092019101610281565b50848210156102bf5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b634e487b7160e01b600052601160045260246000fd5b6001815b600184111561031f57808504811115610303576103036102ce565b600184161561031157908102905b60019390931c9280026102e8565b935093915050565b600082610336575060016103c7565b81610343575060006103c7565b816001811461035957600281146103635761037f565b60019150506103c7565b60ff841115610374576103746102ce565b50506001821b6103c7565b5060208310610133831016604e8410600b84101617156103a2575081810a6103c7565b6103af60001984846102e4565b80600019048211156103c3576103c36102ce565b0290505b92915050565b60006103dc60ff841683610327565b9392505050565b80820281158282048414176103c7576103c76102ce565b808201808211156103c7576103c76102ce565b6108538061041c6000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461012357806370a082311461013657806395d89b411461015f578063a457c2d714610167578063a9059cbb1461017a578063dd62ed3e1461018d57600080fd5b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100ef57806323b872dd14610101578063313ce56714610114575b600080fd5b6100b66101a0565b6040516100c3919061069c565b60405180910390f35b6100df6100da366004610706565b610232565b60405190151581526020016100c3565b6002545b6040519081526020016100c3565b6100df61010f366004610730565b61024c565b604051601281526020016100c3565b6100df610131366004610706565b610270565b6100f361014436600461076d565b6001600160a01b031660009081526020819052604090205490565b6100b6610292565b6100df610175366004610706565b6102a1565b6100df610188366004610706565b610321565b6100f361019b36600461078f565b61032f565b6060600380546101af906107c2565b80601f01602080910402602001604051908101604052809291908181526020018280546101db906107c2565b80156102285780601f106101fd57610100808354040283529160200191610228565b820191906000526020600020905b81548152906001019060200180831161020b57829003601f168201915b5050505050905090565b60003361024081858561035a565b60019150505b92915050565b60003361025a85828561047e565b6102658585856104f8565b506001949350505050565b600033610240818585610283838361032f565b61028d91906107fc565b61035a565b6060600480546101af906107c2565b600033816102af828661032f565b9050838110156103145760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b610265828686840361035a565b6000336102408185856104f8565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103bc5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b606482015260840161030b565b6001600160a01b03821661041d5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161030b565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b600061048a848461032f565b905060001981146104f257818110156104e55760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604482015260640161030b565b6104f2848484840361035a565b50505050565b6001600160a01b03831661055c5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161030b565b6001600160a01b0382166105be5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161030b565b6001600160a01b038316600090815260208190526040902054818110156106365760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161030b565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36104f2565b602081526000825180602084015260005b818110156106ca57602081860181015160408684010152016106ad565b506000604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b038116811461070157600080fd5b919050565b6000806040838503121561071957600080fd5b610722836106ea565b946020939093013593505050565b60008060006060848603121561074557600080fd5b61074e846106ea565b925061075c602085016106ea565b929592945050506040919091013590565b60006020828403121561077f57600080fd5b610788826106ea565b9392505050565b600080604083850312156107a257600080fd5b6107ab836106ea565b91506107b9602084016106ea565b90509250929050565b600181811c908216806107d657607f821691505b6020821081036107f657634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561024657634e487b7160e01b600052601160045260246000fdfea26469706673582212200495336e386fa3f71d6739523de419c1ac49242999ec24f023f5d34cf9a07b6064736f6c634300081a0033", + "from": "0x4b947941e0c443001c7ae69291aacfcbb2526403", + "gas": "0x993a7", + "gasPrice": "0x201770", + "hash": "0x72fa1ede439955cfd37c096716a9688c1358bc1f96c7d4b183763e9632d606d1", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x201770", + "maxPriorityFeePerGas": "0x760b8", + "nonce": "0x45c", + "r": "0x18bbfce715e50d8b2cf0f94c8e0ea4ce316b72551431d41f2b87f24d55f66ced", + "s": "0x2a345c706f75d0959f417c6a699cd60a4ec5d2c61523763150da9984f7b06683", + "transactionIndex": "0xd", + "type": "0x2", + "v": "0x0", + "value": "0x0", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0xd123b4d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000005bb26690d38447acb992fff61dccf3d994e85bd258f838bc305edae4932f2f06bb6567d24c5781a6128bebb4b79af86b000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000207a981f2a08559d44b5159be47b05fcfde662d20000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d6632666568355550334e643363324267656a72326237466551446337646b6f586f52565961325146465253530000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000042307866353630363038313939353863383166333664653836336231323732386430616562333134626539643730643165613130396430386266316165313439333966000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000665766d3a3130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041610d751df1d4df6389151995046cc13c475ddaa25547c57a26f79d31728156e86f10603fda68b69e138fb63b24d2c12d4a0c8c00c3ff9bc0a2e9ff129904ae2f1c00000000000000000000000000000000000000000000000000000000000000", + "from": "0x207a981f2a08559d44b5159be47b05fcfde662d2", + "gas": "0x323c4", + "gasPrice": "0x201770", + "hash": "0xcc2031d9ee76e5fb67bf3d8de896473e8a28af202e4da7bcfb512a7e7efb5a13", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x201770", + "maxPriorityFeePerGas": "0x760b8", + "nonce": "0x45", + "r": "0x5beef200328c77e579919a99b8bd4a8051d369a3c6c8a98e0d059f11879f825f", + "s": "0x6a9a5e7222c37e2fa973cd27757cc7d2a4aabd0db517141c9df091bd45a7a8af", + "to": "0x1195cf65f83b3a5768f3c496d3a05ad6412c64b7", + "transactionIndex": "0xe", + "type": "0x2", + "v": "0x1", + "value": "0x44364c5bb000", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x", + "from": "0x1e2716d7c420468b720cc0aa45deaf48ba7b62f3", + "gas": "0x5208", + "gasPrice": "0x201770", + "hash": "0xa40c069fb029a7102b9261d2323012c32c32d0baaaa56f2f24304507ec2e5d36", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x201770", + "maxPriorityFeePerGas": "0x760b8", + "nonce": "0x1c", + "r": "0x4dbdd1778022cb0b2ce37fcba3eb73bba949be53907055999f5b728d168ae55a", + "s": "0x3bacc2c193f72a48a08ea7fdab59a06ceb01e8b2fe03dba65a66530f601fd26a", + "to": "0xd3693c384aa66aadedaea646eda9c3ba0db40f2b", + "transactionIndex": "0xf", + "type": "0x2", + "v": "0x0", + "value": "0x145b6796902000", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x", + "from": "0x6421aafd4011cac3a0bc493dfd48930bbd90a109", + "gas": "0x5a3c", + "gasPrice": "0xce6c0", + "hash": "0x716fe751a2ade6618ee2608eda89d33a843486a7c4a8fdb574ec3846bc571938", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0xce6c0", + "maxPriorityFeePerGas": "0x2b751", + "nonce": "0xe", + "r": "0x7512613d482fcbaf63b1a5007a6c734e175aac5b98f17fc4497acf60ff096806", + "s": "0x551333ba8bf472cfe6f07b226b4bd14e0ebfe6af51f14f29905b7527f9cd3111", + "to": "0xe639c752066b50afd3cf3ecabcff0f9158cf8177", + "transactionIndex": "0x10", + "type": "0x2", + "v": "0x0", + "value": "0x2c3610fa939cac", + }, + { + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x", + "from": "0xda1fcac981bd86af0d5f40f0abb779f90e1e4ed5", + "gas": "0x5a3c", + "gasPrice": "0xce6c0", + "hash": "0x64572046b0f54ac42f35d004e0d242a5031e824bfbbabb85f94996e3bbbba44f", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0xce6c0", + "maxPriorityFeePerGas": "0x2b751", + "nonce": "0x9", + "r": "0xa1b21a46210ef2aa23cf34ce10219ef6690294162ba342c9fb31fa9fdefdbd12", + "s": "0x7daf98f689a006badb14e73a4eaa61eb2eb32e387a198e406c5cea6c5225041a", + "to": "0xd048e563fef7e0ddb51dd7ad55cade66ac10ba27", + "transactionIndex": "0x11", + "type": "0x2", + "v": "0x1", + "value": "0x3a4ca97f9e4d1", + }, + ], + "transactionsRoot": "0xa466f8998f529307985c2410b1e726069d799a832365af8cb43cf26fbe6369c0", + "uncles": [], + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", +} +`; + +exports[`blockToJsonRpcBlock should convert block to JSON-RPC block format without transactions 1`] = ` +{ + "baseFeePerGas": "0x836f9", + "blobGasUsed": "0x0", + "difficulty": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x", + "gasLimit": "0x1c9c380", + "gasUsed": "0x572821", + "hash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "logsBloom": "0x27806004c206102500c1228040107a8990090148034019c8840c0948044302008080101080100214da000093100080400400884c1401c000882086c0c42000d226009200000402042012802c80248b0160071813226c04280023606424040d440202a18012000010254004c020112d8004204030102804042323001120088000c010030c30218502010800010020804442016020a02e0060422a40084224c4081e130002000082200018268280c106a00318000908a021022842502108200100e000000b118209410040c0420410144028c0000a0020604280200082050422310071280a002414800590f0c8cc0842226c8002d0101d06b90e0c85030609140c", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0x845b54e141b5614790a68e6b7d2020fd08500b318e79a0054b52e63984082a89", + "nonce": "0x0000000000000000", + "number": "0x744f93e", + "parentBeaconBlockRoot": "0x28ed5fb7d646679dc167f7fa6267f3796707caf19ba80c7d1e89d5436495969c", + "parentHash": "0x81fa6aafd3f57685aaf367381471ab15ad294450472455503f71395b1f6272a5", + "receiptsRoot": "0x60c9f088c98590a865d6ff54163d09f61c9e24927922ab8310375aa62ad9abc8", + "requests": undefined, + "requestsRoot": undefined, + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x120d6", + "stateRoot": "0x2f49385f03516638a9df2aec8f280441d6e2ac206a671be95cda6c8bd4a7caa1", + "timestamp": "0x667dcc35", + "totalDifficulty": "0x0", + "transactions": [ + "0xf45a5dd2622427c446b41e784bc5a6223fedef39313786169142c74bd8caa1ea", + "0x39fe544fde06be0de3793d0816dbab06d81c15eeffe7aa3b66f332c9d38bc508", + "0x5a8e7264d069cda182116f2c9972374de50bdd6f2b240e4039b2ec824b8224b5", + "0x5410ce3440e83894b2f6d5f31b17926833986212e79295edb35e6340aa071a67", + "0x0cd45ed7f3a1f9a4d2928e52d102de7514692b401eb126abd1629bf8221a0e3f", + "0xcaa08692ca02e911c8418907b8fda455e4b9d08561284b2443b6f43e4eeacd02", + "0x4e0a2f705ac50c846edf1ac7eaabc59f5e80b9d8f1754cded0a42f3b3bd4b45e", + "0xaa1e0c2e4c92ce05d83eb94560a150de5aeeb5e33d6cc476d6b47bf140c4c7c1", + "0x9402b9dd885ec6ee9b3f0955fc7c18eea975310d077bfca1a9397bae2672fe5b", + "0x87bc00715ecb845746b0b26477f6aef712e54d56b7dc0c3e437f30a64eeca0b1", + "0x7d384bba0916da82117d02c98357c0575877632f2cb58d596874b53bbc6499df", + "0xa12044d708dfd32d61977e92a4ae0ba7d59a54208ff28a69078dd896a9ae8106", + "0x9e2f238a7f7e1d9014363209b19e9957fdf201c2d82fe9dbfb70c3571053221d", + "0x72fa1ede439955cfd37c096716a9688c1358bc1f96c7d4b183763e9632d606d1", + "0xcc2031d9ee76e5fb67bf3d8de896473e8a28af202e4da7bcfb512a7e7efb5a13", + "0xa40c069fb029a7102b9261d2323012c32c32d0baaaa56f2f24304507ec2e5d36", + "0x716fe751a2ade6618ee2608eda89d33a843486a7c4a8fdb574ec3846bc571938", + "0x64572046b0f54ac42f35d004e0d242a5031e824bfbbabb85f94996e3bbbba44f", + ], + "transactionsRoot": "0xa466f8998f529307985c2410b1e726069d799a832365af8cb43cf26fbe6369c0", + "uncles": [], + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", +} +`; diff --git a/packages/procedures/src/utils/__snapshots__/txToJsonRpcTx.spec.ts.snap b/packages/procedures/src/utils/__snapshots__/txToJsonRpcTx.spec.ts.snap new file mode 100644 index 0000000000..07ba72af8c --- /dev/null +++ b/packages/procedures/src/utils/__snapshots__/txToJsonRpcTx.spec.ts.snap @@ -0,0 +1,26 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`txToJsonRpcTx should work 1`] = ` +{ + "accessList": [], + "blobVersionedHashes": undefined, + "blockHash": "0xdbdfda9fdfdbd5b589ad1d93152701880b99a87cfde9d2940eba8a0a7f7120ab", + "blockNumber": "0x744f93e", + "data": "0x010203", + "from": "0xf0d793fd3c1410b43c40d17f3dbbab5d92285c1d", + "gas": "0x64", + "gasPrice": "0x64", + "hash": "0x7924071b4864d96724032c7b84b88712dc73e9d39369d2ac32dec5a34ddb6874", + "maxFeePerBlobGas": undefined, + "maxFeePerGas": "0x64", + "maxPriorityFeePerGas": "0x64", + "nonce": "0x1", + "r": "0x2", + "s": "0x3", + "to": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "transactionIndex": "0x0", + "type": "0x2", + "v": "0x1", + "value": "0x64", +} +`; diff --git a/packages/procedures/src/utils/blockToJsonRpcBlock.spec.ts b/packages/procedures/src/utils/blockToJsonRpcBlock.spec.ts new file mode 100644 index 0000000000..a28cc4815d --- /dev/null +++ b/packages/procedures/src/utils/blockToJsonRpcBlock.spec.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from 'bun:test' +import { createBaseClient } from '@tevm/base-client' +import { getBlockFromRpc } from '@tevm/blockchain' +import { optimism } from '@tevm/common' +import { transports } from '@tevm/test-utils' +import { blockToJsonRpcBlock } from './blockToJsonRpcBlock.js' + +describe('blockToJsonRpcBlock', async () => { + const client = createBaseClient({ common: optimism }) + const vm = await client.getVm() + const block = await getBlockFromRpc({ blockTag: 121960766n, transport: transports.optimism }, vm.common) + + it('should convert block to JSON-RPC block format with transactions', async () => { + expect(await blockToJsonRpcBlock(block, true)).toMatchSnapshot() + }) + + it('should convert block to JSON-RPC block format without transactions', async () => { + expect(await blockToJsonRpcBlock(block, false)).toMatchSnapshot() + }) +}) diff --git a/packages/procedures/src/utils/generateRandomId.spec.ts b/packages/procedures/src/utils/generateRandomId.spec.ts new file mode 100644 index 0000000000..e94fb9eb79 --- /dev/null +++ b/packages/procedures/src/utils/generateRandomId.spec.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from 'bun:test' +import { generateRandomId } from './generateRandomId.js' + +describe('generateRandomId', () => { + it('should generate a valid hex string of length 34', () => { + const id = generateRandomId() + expect(id).toMatch(/^0x[a-f0-9]{32}$/) + }) + + it('should generate different ids on multiple calls', () => { + const id1 = generateRandomId() + const id2 = generateRandomId() + expect(id1).not.toBe(id2) + }) +}) diff --git a/packages/procedures/src/utils/parseBlockTag.spec.ts b/packages/procedures/src/utils/parseBlockTag.spec.ts new file mode 100644 index 0000000000..450a5b2e8a --- /dev/null +++ b/packages/procedures/src/utils/parseBlockTag.spec.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from 'bun:test' +import { hexToBigInt } from '@tevm/utils' +import { parseBlockTag } from './parseBlockTag.js' + +describe('parseBlockTag', () => { + it('should parse hex block numbers to bigint', () => { + const blockTag = '0x10' + const result = parseBlockTag(blockTag) + expect(result).toBe(hexToBigInt(blockTag)) + }) + + it('should return block hash as is', () => { + const blockHash = `0x${'a'.repeat(64)}` as const + const result = parseBlockTag(blockHash) + expect(result).toBe(blockHash) + }) + + it('should return special block tags as is', () => { + const tags = ['latest', 'earliest', 'pending'] as const + tags.forEach((tag) => { + const result = parseBlockTag(tag) + expect(result).toBe(tag) + }) + }) + + it('should return block number as bigint for valid hex strings', () => { + const blockTag = '0x1a' + const result = parseBlockTag(blockTag) + expect(result).toBe(26n) + }) + + it('should handle block tag as a number string correctly', () => { + const blockTag = '0x10' + const result = parseBlockTag(blockTag) + expect(result).toBe(16n) + }) + + it('should return blockTag unchanged if it is a non-hex string', () => { + const blockTag = 'pending' + const result = parseBlockTag(blockTag) + expect(result).toBe(blockTag) + }) +}) diff --git a/packages/procedures/src/utils/txToJsonRpcTx.spec.ts b/packages/procedures/src/utils/txToJsonRpcTx.spec.ts new file mode 100644 index 0000000000..94a06d7b3d --- /dev/null +++ b/packages/procedures/src/utils/txToJsonRpcTx.spec.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'bun:test' +import { createBaseClient } from '@tevm/base-client' +import { getBlockFromRpc } from '@tevm/blockchain' +import { optimism } from '@tevm/common' +import { transports } from '@tevm/test-utils' +import { FeeMarketEIP1559Transaction } from '@tevm/tx' +import { EthjsAddress } from '@tevm/utils' +import { txToJsonRpcTx } from './txToJsonRpcTx.js' + +describe(txToJsonRpcTx.name, () => { + it('should work', async () => { + const tx = new FeeMarketEIP1559Transaction({ + to: EthjsAddress.fromString(`0x${'a'.repeat(40)}`), + data: Uint8Array.from([1, 2, 3]), + value: 100n, + nonce: 1n, + maxFeePerGas: 100n, + chainId: 1n, + v: 1n, + r: 2n, + s: 3n, + gasLimit: 100n, + maxPriorityFeePerGas: 100n, + }) + const client = createBaseClient({ + common: optimism, + }) + const vm = await client.getVm() + const block = await getBlockFromRpc({ blockTag: 121960766n, transport: transports.optimism }, vm.common) + expect(txToJsonRpcTx(tx, block, 0)).toMatchSnapshot() + }) +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36db54fc92..04f731cf87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1863,6 +1863,12 @@ importers: '@tevm/block': specifier: workspace:^ version: link:../block + '@tevm/blockchain': + specifier: workspace:^ + version: link:../blockchain + '@tevm/common': + specifier: workspace:^ + version: link:../common '@tevm/contract': specifier: workspace:^ version: link:../contract @@ -1878,6 +1884,9 @@ importers: '@tevm/state': specifier: workspace:^ version: link:../state + '@tevm/test-utils': + specifier: workspace:^ + version: link:../../test/test-utils '@tevm/tx': specifier: workspace:^ version: link:../tx @@ -24468,7 +24477,7 @@ snapshots: dependencies: ws: 8.13.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) - isows@1.0.4(ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)): + isows@1.0.4(ws@8.17.1(bufferutil@4.0.8)): dependencies: ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) @@ -29386,7 +29395,7 @@ snapshots: '@scure/bip32': 1.3.2 '@scure/bip39': 1.2.1 abitype: 1.0.0(typescript@5.5.2)(zod@3.23.8) - isows: 1.0.4(ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)) + isows: 1.0.4(ws@8.17.1(bufferutil@4.0.8)) ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) optionalDependencies: typescript: 5.5.2 @@ -29403,7 +29412,7 @@ snapshots: '@scure/bip32': 1.3.2 '@scure/bip39': 1.2.1 abitype: 1.0.4(typescript@5.5.2)(zod@3.23.8) - isows: 1.0.4(ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)) + isows: 1.0.4(ws@8.17.1(bufferutil@4.0.8)) ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) optionalDependencies: typescript: 5.5.2 diff --git a/tevm/docs/blockchain/README.md b/tevm/docs/blockchain/README.md index dead920177..4e85bbf15b 100644 --- a/tevm/docs/blockchain/README.md +++ b/tevm/docs/blockchain/README.md @@ -20,6 +20,7 @@ - [deepCopy](functions/deepCopy.md) - [delBlock](functions/delBlock.md) - [getBlock](functions/getBlock.md) +- [getBlockFromRpc](functions/getBlockFromRpc.md) - [getCanonicalHeadBlock](functions/getCanonicalHeadBlock.md) - [getIteratorHead](functions/getIteratorHead.md) - [putBlock](functions/putBlock.md) diff --git a/tevm/docs/blockchain/functions/getBlockFromRpc.md b/tevm/docs/blockchain/functions/getBlockFromRpc.md new file mode 100644 index 0000000000..8200de5a21 --- /dev/null +++ b/tevm/docs/blockchain/functions/getBlockFromRpc.md @@ -0,0 +1,29 @@ +[**tevm**](../../README.md) • **Docs** + +*** + +[tevm](../../modules.md) / [blockchain](../README.md) / getBlockFromRpc + +# Function: getBlockFromRpc() + +> **getBlockFromRpc**(`__namedParameters`, `common`): `Promise`\<[`Block`](../../block/classes/Block.md)\> + +## Parameters + +• **\_\_namedParameters** + +• **\_\_namedParameters.blockTag?**: `bigint` \| \`0x$\{string\}\` \| [`BlockTag`](../../index/type-aliases/BlockTag.md) + +• **\_\_namedParameters.transport** + +• **\_\_namedParameters.transport.request**: `EIP1193RequestFn`\<`undefined`\> + +• **common**: [`Common`](../../common/type-aliases/Common.md) + +## Returns + +`Promise`\<[`Block`](../../block/classes/Block.md)\> + +## Defined in + +packages/blockchain/types/utils/getBlockFromRpc.d.ts:1