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 };
