Skip to content

Commit

Permalink
✅ Test: Add eth_getTransactionCount tests
Browse files Browse the repository at this point in the history
  • Loading branch information
William Cory authored and William Cory committed Sep 13, 2024
1 parent 838631e commit 99fe333
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 12 deletions.
66 changes: 54 additions & 12 deletions packages/procedures/src/eth/ethGetTransactionCountProcedure.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { createAddress } from '@tevm/address'
import { bytesToHex, getAddress, hexToBigInt, hexToBytes, numberToHex } from '@tevm/utils'
import { ForkError, InternalEvmError } from '@tevm/errors'
import { hexToBigInt, hexToBytes, numberToHex } from '@tevm/utils'

/**
* Request handler for eth_getFilterLogs JSON-RPC requests.
* @param {import('@tevm/node').TevmNode} client
* @param {import('@tevm/node').TevmNode} node
* @returns {import('./EthProcedure.js').EthGetTransactionCountJsonRpcProcedure}
*/
export const ethGetTransactionCountProcedure = (client) => {
export const ethGetTransactionCountProcedure = (node) => {
return async (request) => {
const [address, tag] = request.params

const block = await (async () => {
const vm = await client.getVm()
const vm = await node.getVm()
if (tag.startsWith('0x') && tag.length === 66) {
return vm.blockchain.getBlock(hexToBytes(/** @type {import('@tevm/utils').Hex}*/ (tag)))
}
Expand Down Expand Up @@ -42,23 +43,64 @@ export const ethGetTransactionCountProcedure = (client) => {
const pendingCount =
tag === 'pending'
? await (async () => {
const txPool = await client.getTxPool()
const txPool = await node.getTxPool()
const pendingTx = await txPool.getBySenderAddress(createAddress(address))
return BigInt(pendingTx.length)
})()
: 0n

const includedCount = await (async () => {
const vm = await client.getVm()
// TODO we can optimize this by not deep copying once we are more confident it's safe
const root = vm.stateManager._baseState.stateRoots.get(bytesToHex(block.header.stateRoot))
if (!root) {
// todo we might want to throw an error hre
return 0n
const vm = await node.getVm()
if (!(await vm.stateManager.hasStateRoot(block.header.stateRoot))) {
return undefined
}
return root[getAddress(address)]?.nonce ?? 0n
const stateCopy = await vm.stateManager.deepCopy()
stateCopy.setStateRoot(block.header.stateRoot)
const account = await stateCopy.getAccount(createAddress(address))
return account?.nonce ?? 0n
})()

if (includedCount === undefined && node.forkTransport) {
try {
/**
* @type {import('@tevm/utils').Hex}
*/
const result = await node.forkTransport.request(request)
return {
...(request.id ? { id: request.id } : {}),
method: request.method,
jsonrpc: request.jsonrpc,
result: numberToHex(hexToBigInt(result) + pendingCount),
}
} catch (e) {
const err = new ForkError('Unable to resolve eth_getTransactionCount with fork', {
cause: /** @type {any}*/ (e),
})
return {
...(request.id ? { id: request.id } : {}),
method: request.method,
jsonrpc: request.jsonrpc,
error: {
code: err.code,
message: err.message,
},
}
}
}
if (includedCount === undefined) {
const err = new InternalEvmError(`No state root found for block tag ${tag} in eth_getTransactionCountProcedure`)
node.logger.error(err)
return {
...(request.id ? { id: request.id } : {}),
method: request.method,
jsonrpc: request.jsonrpc,
error: {
code: err.code,
message: err.message,
},
}
}

return {
...(request.id ? { id: request.id } : {}),
method: request.method,
Expand Down
102 changes: 102 additions & 0 deletions packages/procedures/src/eth/ethGetTransactionCountProcedure.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { callHandler } from '@tevm/actions'
import { createAddress } from '@tevm/address'
import { createTevmNode } from '@tevm/node'
import { transports } from '@tevm/test-utils'
import { numberToHex, parseEther } from '@tevm/utils'
import { describe, expect, it } from 'vitest'
import { ethGetTransactionCountProcedure } from './ethGetTransactionCountProcedure.js'

const address = '0xb5d85CBf7cB3EE0D56b3bB207D5Fc4B82f43F511' as const

describe(ethGetTransactionCountProcedure.name, () => {
it('should work', async () => {
const node = createTevmNode({
fork: {
transport: transports.mainnet,
blockTag: 20743493n,
},
})
expect(
await ethGetTransactionCountProcedure(node)({
jsonrpc: '2.0',
id: 1,
method: 'eth_getTransactionCount',
params: [address, 'latest'],
}),
).toMatchInlineSnapshot(`
{
"id": 1,
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"result": "0x8e96b4",
}
`)
})
it('should work with past block tags', async () => {
const node = createTevmNode({
fork: {
transport: transports.mainnet,
blockTag: 20743493n,
},
})
expect(
await ethGetTransactionCountProcedure(node)({
jsonrpc: '2.0',
id: 1,
method: 'eth_getTransactionCount',
params: [address, numberToHex(20700000n)],
}),
).toMatchInlineSnapshot(`
{
"id": 1,
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"result": "0x8df90f",
}
`)
})
it('should work with pending tx', async () => {
const node = createTevmNode({
fork: {
transport: transports.mainnet,
blockTag: 20743493n,
},
})
await callHandler(node)({
from: address,
to: createAddress(5).toString(),
value: parseEther('0.1'),
createTransaction: true,
})
expect(
await ethGetTransactionCountProcedure(node)({
jsonrpc: '2.0',
id: 1,
method: 'eth_getTransactionCount',
params: [address, 'latest'],
}),
).toMatchInlineSnapshot(`
{
"id": 1,
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"result": "0x8e96b4",
}
`)
expect(
await ethGetTransactionCountProcedure(node)({
jsonrpc: '2.0',
id: 1,
method: 'eth_getTransactionCount',
params: [address, 'pending'],
}),
).toMatchInlineSnapshot(`
{
"id": 1,
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"result": "0x8e96b5",
}
`)
})
})

0 comments on commit 99fe333

Please sign in to comment.