UNPKG

@tevm/test-matchers

Version:

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

750 lines (735 loc) 29.2 kB
import { CallResult, GetAccountResult } from '@tevm/actions'; import { Abi, AbiParameter, AbiParameterToPrimitiveType, Address, Log, Hex, TransactionReceipt, Client, ContractFunctionName, ContractErrorName, ContractEventName, IsAddressOptions } from 'viem'; export { IsAddressOptions } from 'viem'; import { TevmNode } from '@tevm/node'; import { Assertion } from 'vitest'; import { AbiParameter as AbiParameter$1, ExtractAbiFunction, AbiParametersToPrimitiveTypes, ExtractAbiError, AbiEventParameter, ExtractAbiEvent } from 'abitype'; interface ContainsContractAbi<TAbi extends Abi = Abi> { abi: TAbi; address?: `0x${string}`; } interface ContainsContractAddressAndOptionalAbi<TAbi extends Abi = Abi> { abi?: TAbi; address: `0x${string}`; } interface ContainsTransactionLogs { logs: Log[]; } interface ContainsAddress { address: Address; } type ContainsTransactionAny = Hex | CallResult | TransactionReceipt; type AbiInputsToNamedArgs<TInputs extends readonly AbiParameter[]> = { [K in TInputs[number] as K extends { name: infer TName extends string; } ? TName : never]: K extends { name: string; } ? AbiParameterToPrimitiveType<K> : never; }; /** * Balance change specification for the toChangeBalances matcher */ interface BalanceChange { account: Address | ContainsAddress; amount: bigint | number | string; } interface BalanceMatchers { /** * Asserts that a transaction changes an account's ETH balance by the expected amount. * * @param client - The client or node to use for balance queries * @param account - The account address or object with address * @param expectedChange - The expected balance change in wei (negative for decrease) * * @example * ```typescript * // Account gains 100 wei * await expect(txHash).toChangeBalance(client, '0x123...', 100n) * * // Account loses 50 wei * await expect(txHash).toChangeBalance(client, account, -50n) * * // Works with transaction promises * await expect(client.sendTransaction(tx)) * .toChangeBalance(client, sender, -1000n) * ``` * * @see {@link toChangeBalances} to test multiple accounts * @see {@link toChangeTokenBalance} to test ERC20 token balances */ toChangeBalance(client: Client | TevmNode, account: Address | ContainsAddress, expectedChange: bigint | number | string): Promise<void>; /** * Asserts that a transaction changes multiple accounts' ETH balances by the expected amounts. * * When using .not, it will pass if at least one balance change differs from expected. * * @param client - The client or node to use for balance queries * @param balanceChanges - Array of expected balance changes * * @example * ```typescript * // Test a simple transfer * await expect(txHash).toChangeBalances(client, [ * { account: sender, amount: -100n }, // sender loses 100 wei * { account: recipient, amount: 100n }, // recipient gains 100 wei * ]) * * // Test contract deployment (deployer pays gas) * await expect(deployTx).toChangeBalances(client, [ * { account: deployer, amount: -gasUsed }, * { account: contractAddress, amount: 0n }, * ]) * ``` * * @see {@link toChangeBalance} to test a single account * @see {@link toChangeTokenBalances} to test multiple ERC20 balances */ toChangeBalances(client: Client | TevmNode, balanceChanges: BalanceChange[]): Promise<void>; /** * Asserts that a transaction changes an account's ERC20 token balance by the expected amount. * * @param client - The client or node to use for balance queries * @param tokenContract - The ERC20 token contract address or object with address * @param account - The account address or object with address * @param expectedChange - The expected token balance change (negative for decrease) * * @example * ```typescript * // Account gains 100 tokens * await expect(txHash).toChangeTokenBalance( * client, * '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC * '0x123...', * 100n * ) * * // Using contract object * await expect(txHash).toChangeTokenBalance( * client, * tokenContract, * account, * -50_000000n // 50 USDC (6 decimals) * ) * ``` * * @see {@link toChangeTokenBalances} to test multiple accounts * @see {@link toChangeBalance} to test ETH balances */ toChangeTokenBalance(client: Client | TevmNode, tokenContract: Address | ContainsAddress, account: Address | ContainsAddress, expectedChange: bigint | number | string): Promise<void>; /** * Asserts that a transaction changes multiple accounts' ERC20 token balances by the expected amounts. * * When using .not, it will pass if at least one token balance change differs from expected. * * @param client - The client or node to use for balance queries * @param tokenContract - The ERC20 token contract address or object with address * @param balanceChanges - Array of expected token balance changes * * @example * ```typescript * // Test a token transfer * await expect(txHash).toChangeTokenBalances( * client, * '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC * [ * { account: sender, amount: -1000000n }, // -1 USDC * { account: recipient, amount: 1000000n }, // +1 USDC * ] * ) * * // Test token minting * await expect(mintTx).toChangeTokenBalances(client, tokenContract, [ * { account: mintRecipient, amount: 1000n }, * { account: treasury, amount: 50n }, // 5% mint fee * ]) * ``` * * @see {@link toChangeTokenBalance} to test a single account * @see {@link toChangeBalances} to test multiple ETH balances */ toChangeTokenBalances(client: Client | TevmNode, tokenContract: Address | ContainsAddress, balanceChanges: BalanceChange[]): Promise<void>; } type ChainableAssertion<T = unknown> = Promise<Assertion<T>> & Assertion<T>; interface ContractMatchers { /** * Asserts that a transaction called a specific contract function. * Can be used with contract objects, function signatures, or selectors. * * @param client - Client for transaction execution * @param contract - Contract object with ABI and address * @param functionName - Name of the function in the ABI * * @example * ```typescript * // Using contract object * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * * // Chain with argument assertions * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionArgs(to, 100n) * * // Chain with argument assertions by name (partial matching) * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionNamedArgs({ to, value: 100n }) * ``` * * @see {@link withFunctionArgs} to test function arguments positionally * @see {@link withFunctionNamedArgs} to test function arguments by name */ toCallContractFunction<TAbi extends Abi, TFunctionName extends ContractFunctionName<TAbi>>(client: Client | TevmNode, contract: ContainsContractAddressAndOptionalAbi<TAbi>, functionName: TFunctionName): Promise<ContractAssertionWithContract<TAbi, TFunctionName>> & ContractAssertionWithContract<TAbi, TFunctionName>; /** * Asserts that a transaction called a function matching the signature. * * @param client - Client for transaction execution * @param functionSignature - Function signature string (e.g., "transfer(address,uint256)") * * @example * ```typescript * await expect(txHash).toCallContractFunction(client, contract, 'transfer(address,uint256)') * await expect(txHash).toCallContractFunction(client, { address: '0x123...' }, 'transfer(address,uint256)') * ``` */ toCallContractFunction(client: Client | TevmNode, contract: ContainsContractAddressAndOptionalAbi, functionSignature: string): ChainableAssertion; /** * Asserts that a transaction called a function matching the selector. * * @param client - Client for transaction execution * @param functionSelector - Function selector (4-byte hex) * * @example * ```typescript * await expect(txHash).toCallContractFunction(client, contract, '0xa9059cbb') // transfer function selector * await expect(txHash).toCallContractFunction(client, { address: '0x123...' }, '0xa9059cbb') // transfer function selector * ``` */ toCallContractFunction(client: Client | TevmNode, contract: ContainsContractAddressAndOptionalAbi, functionSelector: Hex): ChainableAssertion; } interface ContractAssertionWithContract<TAbi extends Abi, TFunctionName extends ContractFunctionName<TAbi>> { /** * Chains with toCallContractFunction to assert function arguments in positional order. * Arguments must match exactly in the order they appear in the function. * * **Limitation**: Cannot use .not before this method. * * @param expectedArgs - Expected arguments in order * * @example * ```typescript * // transfer function: transfer(address to, uint256 value) * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionArgs( * '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', // to * 1000n // value * ) * ``` * * @see {@link withFunctionNamedArgs} for partial matching by name */ withFunctionArgs<TInputs extends readonly AbiParameter$1[] = ExtractAbiFunction<TAbi, TFunctionName> extends { inputs: infer U extends readonly AbiParameter$1[]; } ? U : readonly AbiParameter$1[]>(...expectedArgs: AbiParametersToPrimitiveTypes<TInputs>): ChainableAssertion; /** * Chains with toCallContractFunction to assert function arguments by name. * Supports partial matching - only specified arguments are checked. * * **Limitation**: Cannot use .not before this method. * * @param expectedArgs - Object with expected named arguments (partial) * * @example * ```typescript * // Check only specific arguments * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionNamedArgs({ * to: recipient, * value: 1000n * }) * * // Empty object matches any call to this function * await expect(txHash) * .toCallContractFunction(client, tokenContract, 'transfer') * .withFunctionNamedArgs({}) * ``` * * @see {@link withFunctionArgs} for positional argument matching */ withFunctionNamedArgs<TInputs extends readonly AbiParameter$1[] = ExtractAbiFunction<TAbi, TFunctionName> extends { inputs: infer U extends readonly AbiParameter$1[]; } ? U : readonly AbiParameter$1[]>(expectedArgs: Partial<AbiInputsToNamedArgs<TInputs>>): ChainableAssertion; } interface ErrorMatchers { /** * Asserts that a transaction reverted for any reason. * This includes string reverts, custom errors, and panics. * * @param client - Optional client for transaction execution * * @example * ```typescript * // Test any revert * await expect(writeContract(client, contract.write.failingFunction())) * .toBeReverted(client) * * // Works with .not for successful transactions * await expect(writeContract(client, contract.write.successfulFunction())) * .not.toBeReverted(client) * ``` * * @see {@link toBeRevertedWithString} for specific revert messages * @see {@link toBeRevertedWithError} for custom errors */ toBeReverted(client?: Client): Promise<void>; /** * Asserts that a transaction reverted with a specific revert string. * Use this for `revert("message")` style reverts. * * @param client - Client for transaction execution * @param revertString - Expected exact revert message * * @example * ```typescript * // Contract: require(amount > 0, "Amount must be positive") * await expect(writeContract(client, contract.write.transfer(to, 0n))) * .toBeRevertedWithString(client, 'Amount must be positive') * * // Note: Message must match exactly * await expect(transaction) * .not.toBeRevertedWithString(client, 'Different message') * ``` * * @see {@link toBeRevertedWithError} for custom errors */ toBeRevertedWithString(client: Client, revertString: string): Promise<void>; /** * Asserts that a transaction reverted with a specific custom error. * Use this for custom error types, not `revert()` strings. * * @param client - Client for transaction execution * @param contract - Contract object with ABI containing the error * @param errorName - Name of the custom error in the ABI * * @example * ```typescript * // error InsufficientBalance(uint256 available, uint256 required); * await expect(writeContract(client, contract.write.transfer(to, 1000n))) * .toBeRevertedWithError(client, contract, 'InsufficientBalance') * * // Chain with argument assertions * await expect(writeContract(client, contract.write.transfer(to, 1000n))) * .toBeRevertedWithError(client, contract, 'InsufficientBalance') * .withErrorArgs(50n, 1000n) * * // Chain with argument assertions by name (partial matching) * await expect(writeContract(client, contract.write.transfer(to, 1000n))) * .toBeRevertedWithError(client, contract, 'InsufficientBalance') * .withErrorNamedArgs({ required: 1000n }) * ``` * * @see {@link withErrorArgs} to test error arguments * @see {@link withErrorNamedArgs} to test error arguments by name * @see {@link toBeRevertedWithString} for revert strings */ toBeRevertedWithError<TAbi extends Abi, TErrorName extends ContractErrorName<TAbi>>(client: Client, contract: ContainsContractAbi<TAbi>, errorName: TErrorName): Promise<ErrorAssertionWithContract<TAbi, TErrorName>> & ErrorAssertionWithContract<TAbi, TErrorName>; /** * Asserts that a transaction reverted with an error matching the signature. * * @param client - Client for transaction execution * @param errorName - Error signature string (e.g., "InsufficientBalance(uint256,uint256)") * * @example * ```typescript * await expect(transaction) * .toBeRevertedWithError(client, 'InsufficientBalance(uint256,uint256)') * .withErrorArgs(50n, 1000n) * ``` */ toBeRevertedWithError(client: Client, errorName: string): ChainableAssertion; /** * Asserts that a transaction reverted with an error matching the selector. * * @param client - Client for transaction execution * @param errorSelector - Error selector (4-byte hex) * * @example * ```typescript * await expect(transaction) * .toBeRevertedWithError(client, '0x356680b7') // InsufficientBalance selector * ``` */ toBeRevertedWithError(client: Client, errorSelector: Hex): ChainableAssertion; } interface ErrorAssertionWithContract<TAbi extends Abi, TErrorName extends ContractErrorName<TAbi>> { /** * Chains with toBeRevertedWithError to assert error arguments in positional order. * Arguments must match exactly in the order they appear in the error. * * **Limitation**: Cannot use .not before this method. * * @param expectedArgs - Expected arguments in order * * @example * ```typescript * // error InsufficientBalance(uint256 available, uint256 required); * await expect(transaction) * .toBeRevertedWithError(client, contract, 'InsufficientBalance') * .withErrorArgs( * 50n, // available * 1000n // required * ) * ``` * * @see {@link withErrorNamedArgs} for partial matching by name */ withErrorArgs<TInputs extends readonly AbiParameter[] = ExtractAbiError<TAbi, TErrorName> extends { inputs: infer U extends readonly AbiParameter[]; } ? U : readonly AbiParameter[]>(...expectedArgs: AbiParametersToPrimitiveTypes<TInputs>): ChainableAssertion; /** * Chains with toBeRevertedWithError to assert error arguments by name. * Supports partial matching - only specified arguments are checked. * * **Limitation**: Cannot use .not before this method. * * @param expectedArgs - Object with expected named arguments (partial) * * @example * ```typescript * // Check only specific arguments * await expect(transaction) * .toBeRevertedWithError(client, contract, 'InsufficientBalance') * .withErrorNamedArgs({ required: 1000n }) * * // Check all arguments * await expect(transaction) * .toBeRevertedWithError(client, contract, 'InsufficientBalance') * .withErrorNamedArgs({ * available: 50n, * required: 1000n * }) * ``` * * @see {@link withErrorArgs} for positional argument matching */ withErrorNamedArgs<TInputs extends readonly AbiParameter[] = ExtractAbiError<TAbi, TErrorName> extends { inputs: infer U extends readonly AbiParameter[]; } ? U : readonly AbiParameter[]>(expectedArgs: Partial<AbiInputsToNamedArgs<TInputs>>): ChainableAssertion; } interface EmitMatchers { /** * Asserts that a transaction emitted a specific event. * Can be used with contract objects, event signatures, or selectors. * * @param contract - Contract object with ABI and address * @param eventName - Name of the event in the ABI * * @example * ```typescript * // Using contract object * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * * // Chain with argument assertions * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventArgs(from, to, 100n) * * // Chain with argument assertions by name (partial matching) * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventNamedArgs({ to }) * ``` * * @see {@link withEventArgs} to test event arguments positionally * @see {@link withEventNamedArgs} to test event arguments by name */ toEmit<TAbi extends Abi, TEventName extends ContractEventName<TAbi>>(contract: ContainsContractAbi<TAbi>, eventName: TEventName): Promise<EmitAssertionWithContract<TAbi, TEventName>> & EmitAssertionWithContract<TAbi, TEventName>; /** * Asserts that a transaction emitted an event matching the signature. * * @param eventSignature - Event signature string (e.g., "Transfer(address,address,uint256)") * * @example * ```typescript * await expect(txHash) * .toEmit('Transfer(address,address,uint256)') * .withEventArgs(from, to, amount) * ``` */ toEmit(eventSignature: string): ChainableAssertion; /** * Asserts that a transaction emitted an event matching the selector. * * @param eventSelector - Event selector (4-byte hex) * * @example * ```typescript * await expect(txHash) * .toEmit('0xddf252ad...') // Transfer event selector * ``` */ toEmit(eventSelector: Hex): ChainableAssertion; } interface EmitAssertionWithContract<TAbi extends Abi, TEventName extends ContractEventName<TAbi>> { /** * Chains with toEmit to assert event arguments in positional order. * Arguments must match exactly in the order they appear in the event. * * **Limitation**: Cannot use .not before this method. * * @param expectedArgs - Expected arguments in order * * @example * ```typescript * // Transfer event: Transfer(address from, address to, uint256 value) * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventArgs( * '0x742d35Cc6274c36e1019e41D77d0A4aa7D7dE01e', // from * '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', // to * 1000n // value * ) * ``` * * @see {@link withEventNamedArgs} for partial matching by name */ withEventArgs<TInputs extends readonly AbiEventParameter[] = ExtractAbiEvent<TAbi, TEventName> extends { inputs: infer U extends readonly AbiEventParameter[]; } ? U : readonly AbiEventParameter[]>(...expectedArgs: AbiParametersToPrimitiveTypes<TInputs>): ChainableAssertion; /** * Chains with toEmit to assert event arguments by name. * Supports partial matching - only specified arguments are checked. * * **Limitation**: Cannot use .not before this method. * * @param expectedArgs - Object with expected named arguments (partial) * * @example * ```typescript * // Check only specific arguments * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventNamedArgs({ * to: recipient, * value: 1000n * }) * * // Empty object matches any event of this type * await expect(txHash) * .toEmit(tokenContract, 'Transfer') * .withEventNamedArgs({}) * ``` * * @see {@link withEventArgs} for positional argument matching */ withEventNamedArgs<TInputs extends readonly AbiEventParameter[] = ExtractAbiEvent<TAbi, TEventName> extends { inputs: infer U extends readonly AbiEventParameter[]; } ? U : readonly AbiEventParameter[]>(expectedArgs: Partial<AbiInputsToNamedArgs<TInputs>>): ChainableAssertion; } type ExpectedState = Partial<Omit<GetAccountResult, 'address' | 'errors'>>; interface StorageEntry { slot: Hex; value: Hex; } type ExpectedStorage = StorageEntry | StorageEntry[]; interface StateMatchers { /** * Asserts that an address contains deployed contract code (is initialized). * Fails if the address is an EOA or has no code. * * @param client - The client or node to use for state queries * * @example * ```typescript * // Check if address has deployed code * await expect('0x742d35Cc5dB4c8E9f8D4Dc1Ef70c4c7c8E5b7A6b') * .toBeInitializedAccount(client) * * // Check EOA (should fail) * await expect('0x0000000000000000000000000000000000000000') * .not.toBeInitializedAccount(client) * ``` * * @see {@link toHaveState} to check specific state properties */ toBeInitializedAccount(client: Client | TevmNode): Promise<void>; /** * Asserts that an account has specific state properties. * Can check balance, nonce, code, and storage in a single assertion. * * @param client - The client or node to use for state queries * @param expectedState - The expected state properties (partial match) * * @example * ```typescript * // Check multiple state properties * await expect('0x742d35Cc5dB4c8E9f8D4Dc1Ef70c4c7c8E5b7A6b') * .toHaveState(client, { * balance: 1000n, * nonce: 5n, * code: '0x6080604052...', // contract bytecode * storage: { * '0x0': '0x1', * '0x1': '0x2' * } * }) * * // Check only balance * await expect(address).toHaveState(client, { balance: 0n }) * ``` * * @see {@link toHaveStorageAt} to check only storage * @see {@link toBeInitializedAccount} to check if contract exists */ toHaveState(client: Client | TevmNode, expectedState: ExpectedState): Promise<void>; /** * Asserts that a contract has specific storage values at given slots. * * @param client - The client or node to use for state queries * @param expectedStorage - Single storage entry or array of entries * * @example * ```typescript * // Check single storage slot * await expect(contractAddress) * .toHaveStorageAt(client, { * slot: '0x0', * value: '0x1' * }) * * // Check multiple storage slots * await expect(contractAddress) * .toHaveStorageAt(client, [ * { slot: '0x0', value: '0x1' }, // owner * { slot: '0x1', value: '0x64' }, // totalSupply = 100 * { slot: '0x2', value: '0x0' }, // paused = false * ]) * ``` * * @see {@link toHaveState} to check multiple state properties */ toHaveStorageAt(client: Client | TevmNode, expectedStorage: ExpectedStorage): Promise<void>; } type IsHexOptions = { /** * Whether to check for strict hex format or only for 0x prefix * @default true */ strict?: boolean; /** * Optional expected size in bytes */ size?: number; }; type EqualHexOptions = { /** * Whether to compare hex strings exactly as written or normalize them first. * When false (default), leading zeros are trimmed before byte comparison (e.g., "0x00123" equals "0x123"). * When true, hex strings must match exactly including leading zeros. * @default false */ exact?: boolean; }; interface UtilsMatchers { /** * Asserts that a value is a valid Ethereum address. * By default, requires EIP-55 checksum validation. * * @param opts - Options for address validation * @param opts.strict - If true (default), enforces EIP-55 checksum. If false, accepts any case. * * @example * ```typescript * // Validates checksummed address (default) * expect('0x742d35Cc5dB4c8E9f8D4Dc1Ef70c4c7c8E5b7A6b').toBeAddress() * * // Accept any case * expect('0x742d35cc5db4c8e9f8d4dc1ef70c4c7c8e5b7a6b').toBeAddress({ strict: false }) * * // Works with .not * expect('not-an-address').not.toBeAddress() * ``` * * @see {@link toEqualAddress} for case-insensitive address comparison */ toBeAddress(opts?: IsAddressOptions): void; /** * Asserts that a value is a valid hex string. * Optionally validates the exact byte size. * * @param opts - Options for hex validation * @param opts.strict - If true (default), validates hex characters. If false, only checks for 0x prefix. * @param opts.size - Expected size in bytes (e.g., 32 for a transaction hash) * * @example * ```typescript * // Basic hex validation * expect('0x1234abcd').toBeHex() * * // Validate transaction hash (32 bytes) * expect(txHash).toBeHex({ size: 32 }) * * // Validate function selector (4 bytes) * expect('0xa9059cbb').toBeHex({ size: 4 }) * ``` * * @see {@link toEqualHex} for hex string comparison */ toBeHex(opts?: IsHexOptions): void; /** * Asserts that two Ethereum addresses are equal (case-insensitive). * Uses viem's isAddressEqual for comparison. * * @param expected - The expected address * * @example * ```typescript * // Same address, different cases * expect('0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC') * .toEqualAddress('0xa5cc3c03994db5b0d9a5eedd10cabab0813678ac') * * // Works with .not * expect(address1).not.toEqualAddress(address2) * ``` * * @see {@link toBeAddress} for address validation */ toEqualAddress(expected: unknown): void; /** * Asserts that two hex strings are equal. * By default, normalizes hex strings by trimming leading zeros. * * @param expected - The expected hex string * @param opts - Options for comparison * @param opts.exact - If true, performs exact string comparison. If false (default), normalizes before comparing. * * @example * ```typescript * // Normalized comparison (default) * expect('0x000123').toEqualHex('0x123') * expect('0x0').toEqualHex('0x00') * * // Exact comparison * expect('0x000123').toEqualHex('0x000123', { exact: true }) * * // Case insensitive * expect('0xabcd').toEqualHex('0xABCD') * * // Chain with .not * expect('0x000123').not.toEqualHex('0x123') * ``` * * @see {@link toBeHex} for hex validation */ toEqualHex(expected: unknown, opts?: EqualHexOptions): void; } declare module 'vitest' { interface Assertion<T = any> extends UtilsMatchers, EmitMatchers, ErrorMatchers, StateMatchers, BalanceMatchers, ContractMatchers { } interface AsymmetricMatchersContaining extends UtilsMatchers, EmitMatchers, ErrorMatchers, StateMatchers, BalanceMatchers, ContractMatchers { } } export type { BalanceChange, ContainsAddress, ContainsContractAbi, ContainsContractAddressAndOptionalAbi, ContainsTransactionAny, ContainsTransactionLogs, EqualHexOptions, IsHexOptions };