Skip to content

Commit

Permalink
🧪 Test: Add more test coverage to tevm actions
Browse files Browse the repository at this point in the history
  • Loading branch information
roninjin10 committed Oct 10, 2024
1 parent 3419055 commit f18abd5
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .changeset/famous-bottles-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@tevm/actions": patch
"@tevm/node": patch
---

Fixed bug with anvil_impersonateAccount woudln't properly throw an error for invalid addresses
72 changes: 72 additions & 0 deletions packages/actions/src/anvil/anvilImpersonateAccount.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { createAddress } from '@tevm/address'
import { createTevmNode } from '@tevm/node'
import { describe, expect, it } from 'vitest'
import { anvilImpersonateAccountJsonRpcProcedure } from './anvilImpersonateAccountProcedure.js'

describe('anvilImpersonateAccountJsonRpcProcedure', () => {
it('should impersonate an account successfully', async () => {
const client = createTevmNode()
const impersonateAccountProcedure = anvilImpersonateAccountJsonRpcProcedure(client)

const testAddress = createAddress('0x1234567890123456789012345678901234567890')

const request = {
method: 'anvil_impersonateAccount',
params: [testAddress.toString()],
jsonrpc: '2.0',
id: 1,
} as const

const result = await impersonateAccountProcedure(request)

// Check the returned result
expect(result).toEqual({
jsonrpc: '2.0',
method: 'anvil_impersonateAccount',
id: 1,
result: null,
})

// Verify that the impersonated account was set in the client
const impersonatedAccount = client.getImpersonatedAccount()
expect(impersonatedAccount).toBe(testAddress.toString())
})

it('should handle an invalid address', async () => {
const client = createTevmNode()
const impersonateAccountProcedure = anvilImpersonateAccountJsonRpcProcedure(client)

const invalidAddress = '0xinvalid'

const request = {
method: 'anvil_impersonateAccount',
params: [invalidAddress],
jsonrpc: '2.0',
id: 1,
} as const

const result = await impersonateAccountProcedure(request)

// Check the returned result for an error
expect(result).toMatchInlineSnapshot(`
{
"error": {
"code": -32602,
"message": "Address "0xinvalid" is invalid.
- Address must be a hex value of 20 bytes (40 hex characters).
- Address must match its checksum counterpart.
Version: 2.21.1",
},
"id": 1,
"jsonrpc": "2.0",
"method": "anvil_impersonateAccount",
}
`)

// Verify that the impersonated account was not set in the client
const impersonatedAccount = client.getImpersonatedAccount()
expect(impersonatedAccount).toBeUndefined()
})
})
45 changes: 45 additions & 0 deletions packages/actions/src/anvil/anvilLoadStateProcedure.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createAddress } from '@tevm/address'
import { createTevmNode } from '@tevm/node'
import { EthjsAccount, bytesToHex } from '@tevm/utils'
import { describe, expect, it } from 'vitest'
import { anvilLoadStateJsonRpcProcedure } from './anvilLoadStateProcedure.js'

describe('anvilLoadStateJsonRpcProcedure', () => {
it('should successfully load state', async () => {
const client = createTevmNode()
const loadStateProcedure = anvilLoadStateJsonRpcProcedure(client)

const testAddress = createAddress('0x1234567890123456789012345678901234567890')
const account = EthjsAccount.fromAccountData({ balance: 1000n, nonce: 1n })

const request = {
method: 'anvil_loadState',
params: [
{
state: {
[testAddress.toString()]: bytesToHex(account.serialize()),
},
},
],
jsonrpc: '2.0',
id: 1,
} as const

const result = await loadStateProcedure(request)

// Check successful state loading
const vm = await client.getVm()
const loadedAccount = await vm.stateManager.getAccount(testAddress)

expect(loadedAccount?.balance).toBe(1000n)
expect(loadedAccount?.nonce).toBe(1n)

// Check the returned result
expect(result).toEqual({
jsonrpc: '2.0',
method: 'anvil_loadState',
result: null,
id: 1,
})
})
})
56 changes: 56 additions & 0 deletions packages/actions/src/eth/ethNewBlockFilterProcedure.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Block } from '@tevm/block'
import { createTevmNode } from '@tevm/node'
import { describe, expect, it } from 'vitest'
import { ethNewBlockFilterProcedure } from './ethNewBlockFilterProcedure.js'

describe('ethNewBlockFilterProcedure', () => {
it('should create a new block filter and return its id', async () => {
const client = createTevmNode()
const newBlockFilterProcedure = ethNewBlockFilterProcedure(client)

const request = {
method: 'eth_newBlockFilter',
params: [],
jsonrpc: '2.0',
id: 1,
} as const

const result = await newBlockFilterProcedure(request)

// Check that the result contains a filter id
expect(result).toEqual({
id: 1,
method: 'eth_newBlockFilter',
jsonrpc: '2.0',
result: expect.any(String),
})

const filterId = result.result as `0x${string}`

// Verify that the filter was added to the client
const filters = client.getFilters()
expect(filters.has(filterId)).toBe(true)

const filter = filters.get(filterId)
expect(filter).toEqual({
id: filterId,
type: 'Block',
created: expect.any(Number),
logs: [],
tx: [],
blocks: [],
installed: {},
err: undefined,
registeredListeners: expect.any(Array),
})

const vm = await client.getVm()

// Test that the listener is working
const newBlock = Block.fromBlockData({ header: { number: 1n } }, { common: vm.common })
filter?.registeredListeners[0]?.(newBlock)

// Verify that the block was added to the filter
expect(filter?.blocks).toContain(newBlock)
})
})
66 changes: 66 additions & 0 deletions packages/actions/src/eth/ethSendTransactionProcedure.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { createAddress } from '@tevm/address'
import { createTevmNode } from '@tevm/node'
import { parseEther } from '@tevm/utils'
import { beforeEach, describe, expect, it } from 'vitest'
import { getAccountHandler } from '../GetAccount/getAccountHandler.js'
import { mineHandler } from '../Mine/mineHandler.js'
import { setAccountHandler } from '../SetAccount/setAccountHandler.js'
import { ethSendTransactionJsonRpcProcedure } from './ethSendTransactionProcedure.js'

describe('ethSendTransactionJsonRpcProcedure', () => {
let client: ReturnType<typeof createTevmNode>
let procedure: ReturnType<typeof ethSendTransactionJsonRpcProcedure>

beforeEach(() => {
client = createTevmNode()
procedure = ethSendTransactionJsonRpcProcedure(client)
})

it('should handle a simple transaction request', async () => {
const from = createAddress('0x1234')
const to = createAddress('0x5678')
const value = parseEther('1')

// Set up the sender's account
await setAccountHandler(client)({
address: from.toString(),
balance: parseEther('10'),
})

const request = {
method: 'eth_sendTransaction',
params: [
{
from: from.toString(),
to: to.toString(),
value: `0x${value.toString(16)}`,
gas: '0x5208', // 21000
},
],
jsonrpc: '2.0',
id: 1,
}

const result = await procedure(request)

// Check the response structure
expect(result).toEqual({
jsonrpc: '2.0',
id: 1,
method: 'eth_sendTransaction',
result: expect.stringMatching(/^0x[a-fA-F0-9]{64}$/), // Transaction hash
})

// Mine the block to process the transaction
await mineHandler(client)()

// Verify the balance change
const toAccount = await getAccountHandler(client)({ address: to.toString() })
expect(toAccount.balance).toBe(value)

// Verify the sender's balance change (should be less than 10 - 1 due to gas costs)
const fromAccount = await getAccountHandler(client)({ address: from.toString() })
expect(fromAccount.balance).toBeLessThan(parseEther('9'))
expect(fromAccount.balance).toBeGreaterThan(parseEther('8.9'))
})
})
4 changes: 2 additions & 2 deletions packages/node/src/createTevmNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createLogger } from '@tevm/logger'
import { ReceiptsManager, createMapDb } from '@tevm/receipt-manager'
import { createStateManager } from '@tevm/state'
import { TxPool } from '@tevm/txpool'
import { KECCAK256_RLP, bytesToHex, hexToBigInt, keccak256 } from '@tevm/utils'
import { KECCAK256_RLP, bytesToHex, getAddress, hexToBigInt, keccak256 } from '@tevm/utils'
import { createVm } from '@tevm/vm'
import { DEFAULT_CHAIN_ID } from './DEFAULT_CHAIN_ID.js'
import { GENESIS_STATE } from './GENESIS_STATE.js'
Expand Down Expand Up @@ -309,7 +309,7 @@ export const createTevmNode = (options = {}) => {
* returns {void}
*/
const setImpersonatedAccount = (address) => {
impersonatedAccount = address
impersonatedAccount = address && getAddress(address)
}
await readyPromise
const oldVm = await vmPromise
Expand Down

0 comments on commit f18abd5

Please sign in to comment.