UNPKG

@tevm/test-matchers

Version:

Vite test matchers for Tevm or EVM-related testing in TypeScript.

92 lines (81 loc) 3.47 kB
import type { AbiError } from 'abitype' import { AbiItem } from 'ox' import type { Abi, Client, ContractErrorName, Hex } from 'viem' import { isHex } from 'viem' import type { MatcherResult } from '../../chainable/types.js' import type { ContainsContractAbi, ContainsTransactionAny } from '../../common/types.js' import { handleTransaction } from './handleTransaction.js' import type { ToBeRevertedWithState } from './types.js' export const toBeRevertedWithError = async < TAbi extends Abi | undefined = Abi | undefined, TErrorName extends TAbi extends Abi ? ContractErrorName<TAbi> : never = TAbi extends Abi ? ContractErrorName<TAbi> : never, TContract extends TAbi extends Abi ? ContainsContractAbi<TAbi> : never = TAbi extends Abi ? ContainsContractAbi<TAbi> : never, >( received: ContainsTransactionAny | Promise<ContainsTransactionAny>, client: Client, contractOrErrorIdentifier: TContract | Hex | string, errorName?: TErrorName, ): Promise<MatcherResult<ToBeRevertedWithState>> => { const { error, isRevert, isError, revertReason, decodedRevertData, rawRevertData } = await handleTransaction( received, { client }, ) if (isError) { throw new Error('Expected transaction to be or not be reverted, but a different error was thrown', { cause: error }) } const actualSelector = decodedRevertData ? AbiItem.getSelector(decodedRevertData.abiItem) : rawRevertData?.slice(0, 10) if (actualSelector === undefined) throw new Error('Could not get selector from revert data') // Handle error signature or selector if (typeof contractOrErrorIdentifier === 'string') { const errorSelector = isHex(contractOrErrorIdentifier) ? contractOrErrorIdentifier : AbiItem.getSelector( contractOrErrorIdentifier.startsWith('error') ? contractOrErrorIdentifier : `error ${contractOrErrorIdentifier}`, ) const pass = isRevert && actualSelector === errorSelector return { pass, actual: actualSelector, expected: errorSelector, message: () => pass ? `Expected transaction not to be reverted with ${contractOrErrorIdentifier.startsWith('0x') ? 'selector' : 'signature'} ${contractOrErrorIdentifier}, but it reverted ${revertReason ? `with:\n\n${revertReason}` : 'without reason'}` : `Expected transaction to be reverted with ${contractOrErrorIdentifier.startsWith('0x') ? 'selector' : 'signature'} ${contractOrErrorIdentifier}`, state: { decodedRevertData, rawRevertData, }, } } // Contract + error name case if (typeof errorName !== 'string') throw new Error('You need to provide an error name as a second argument') const contract = contractOrErrorIdentifier const errorAbi = contract.abi.find((item): item is AbiError => item.type === 'error' && item.name === errorName) if (!errorAbi) throw new Error( `Error ${errorName} not found in contract ABI. Please make sure you've compiled the latest version before running the test.`, ) const pass = isRevert && actualSelector === AbiItem.getSelector(errorAbi) return { pass, actual: `error ${decodedRevertData?.errorName ?? rawRevertData?.slice(0, 10)}`, expected: `error ${errorName}`, message: () => pass ? `Expected transaction not to be reverted with error ${errorName}, but it reverted ${revertReason ? `with:\n\n${revertReason}` : 'without reason'}` : `Expected transaction to be reverted with error ${errorName}`, state: { decodedRevertData, rawRevertData, contract, }, } }