UNPKG

viem

Version:

TypeScript Interface for Ethereum

141 lines (126 loc) 5.19 kB
import type { Address } from 'abitype' import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import { universalSignatureValidatorAbi } from '../../constants/abis.js' import { universalSignatureValidatorByteCode } from '../../constants/contracts.js' import { CallExecutionError } from '../../errors/contract.js' import type { InvalidHexBooleanError } from '../../errors/encoding.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' import type { ByteArray, Hex, Signature } from '../../types/misc.js' import type { OneOf } from '../../types/utils.js' import { type EncodeDeployDataErrorType, encodeDeployData, } from '../../utils/abi/encodeDeployData.js' import { getAddress } from '../../utils/address/getAddress.js' import { isAddressEqual } from '../../utils/address/isAddressEqual.js' import { type IsHexErrorType, isHex } from '../../utils/data/isHex.js' import { type ToHexErrorType, bytesToHex } from '../../utils/encoding/toHex.js' import { getAction } from '../../utils/getAction.js' import { encodeFunctionData, hexToBool } from '../../utils/index.js' import { isErc6492Signature } from '../../utils/signature/isErc6492Signature.js' import { recoverAddress } from '../../utils/signature/recoverAddress.js' import { serializeErc6492Signature } from '../../utils/signature/serializeErc6492Signature.js' import { serializeSignature } from '../../utils/signature/serializeSignature.js' import { type CallErrorType, type CallParameters, call } from './call.js' export type VerifyHashParameters = Pick< CallParameters, 'blockNumber' | 'blockTag' > & { /** The address that signed the original message. */ address: Address /** The hash to be verified. */ hash: Hex /** The signature that was generated by signing the message with the address's private key. */ signature: Hex | ByteArray | Signature universalSignatureVerifierAddress?: Address | undefined } & OneOf<{ factory: Address; factoryData: Hex } | {}> export type VerifyHashReturnType = boolean export type VerifyHashErrorType = | CallErrorType | IsHexErrorType | ToHexErrorType | InvalidHexBooleanError | EncodeDeployDataErrorType | ErrorType /** * Verifies a message hash onchain using ERC-6492. * * @param client - Client to use. * @param parameters - {@link VerifyHashParameters} * @returns Whether or not the signature is valid. {@link VerifyHashReturnType} */ export async function verifyHash<chain extends Chain | undefined>( client: Client<Transport, chain>, parameters: VerifyHashParameters, ): Promise<VerifyHashReturnType> { const { address, factory, factoryData, hash, signature, universalSignatureVerifierAddress = client.chain?.contracts ?.universalSignatureVerifier?.address, ...rest } = parameters const signatureHex = (() => { if (isHex(signature)) return signature if (typeof signature === 'object' && 'r' in signature && 's' in signature) return serializeSignature(signature) return bytesToHex(signature) })() const wrappedSignature = await (async () => { // If no `factory` or `factoryData` is provided, it is assumed that the // address is not a Smart Account, or the Smart Account is already deployed. if (!factory && !factoryData) return signatureHex // If the signature is already wrapped, return the signature. if (isErc6492Signature(signatureHex)) return signatureHex // If the Smart Account is not deployed, wrap the signature with a 6492 wrapper // to perform counterfactual validation. return serializeErc6492Signature({ address: factory!, data: factoryData!, signature: signatureHex, }) })() try { const args = universalSignatureVerifierAddress ? ({ to: universalSignatureVerifierAddress, data: encodeFunctionData({ abi: universalSignatureValidatorAbi, functionName: 'isValidSig', args: [address, hash, wrappedSignature], }), ...rest, } as unknown as CallParameters) : ({ data: encodeDeployData({ abi: universalSignatureValidatorAbi, args: [address, hash, wrappedSignature], bytecode: universalSignatureValidatorByteCode, }), ...rest, } as unknown as CallParameters) const { data } = await getAction(client, call, 'call')(args) return hexToBool(data ?? '0x0') } catch (error) { // Fallback attempt to verify the signature via ECDSA recovery. try { const verified = isAddressEqual( getAddress(address), await recoverAddress({ hash, signature }), ) if (verified) return true } catch {} if (error instanceof CallExecutionError) { // if the execution fails, the signature was not valid and an internal method inside of the validator reverted // this can happen for many reasons, for example if signer can not be recovered from the signature // or if the signature has no valid format return false } throw error } }