@tevm/test-matchers
Version:
Vite test matchers for Tevm or EVM-related testing in TypeScript.
71 lines (62 loc) • 2.45 kB
text/typescript
import { getAccountHandler } from '@tevm/actions'
import { createTevmNode, type TevmNode } from '@tevm/node'
import { type Address, type Client, type Hex, isAddress } from 'viem'
import type { ContainsAddress } from '../../common/types.js'
import type { ExpectedState } from './types.js'
export const toHaveState = async (
received: Address | ContainsAddress,
client: Client | TevmNode,
expectedState: ExpectedState,
) => {
const address = typeof received === 'string' ? received : received.address
if (!isAddress(address)) throw new Error(`Invalid address: ${address}`)
const node = 'request' in client ? createTevmNode({ fork: { transport: client } }) : client
const account = await getAccountHandler(node, { throwOnFail: false })({ address, returnStorage: true })
if (account.errors) throw new Error(account.errors[0]?.message ?? 'Could not retrieve account')
const { storage = {}, ...state } = expectedState
const mismatchedKeys: (keyof ExpectedState)[] = []
// Check provided state entries (except storage)
Object.entries(state).forEach(([_key, expectedValue]) => {
const key = _key as keyof Omit<ExpectedState, 'storage'>
const actualValue = account[key]
if (actualValue !== expectedValue) mismatchedKeys.push(key)
})
// Check provided storage entries
for (const [_slot, expectedValue] of Object.entries(storage)) {
const slot = _slot as Hex
const actualValue = account.storage?.[slot]
if (actualValue !== expectedValue) {
mismatchedKeys.push('storage')
break
}
}
const pass = mismatchedKeys.length === 0
return {
pass,
message: () =>
pass
? `Expected account ${address} not to have state.`
: `Expected account ${address} to have state but received mismatched state at keys: ${mismatchedKeys.join(', ')}`,
// Only show provided keys with their actual values so it's not confusing
actual: pass
? undefined
: {
...Object.fromEntries(
Object.entries(expectedState)
.filter(([key]) => key !== 'storage')
.map(([key]) => [key, account[key as keyof typeof account] ?? undefined]),
),
...(Object.keys(expectedState).includes('storage') && expectedState.storage
? {
storage: Object.fromEntries(
Object.entries(expectedState.storage).map(([slot]) => [
slot,
account.storage?.[slot as Hex] ?? undefined,
]),
),
}
: {}),
},
expected: pass ? undefined : expectedState,
}
}