UNPKG

@0xfutbol/id

Version:

React component library with shared providers for 0xFutbol ID

1,394 lines 53.6 kB
import {aw as encodeAbiParameters,ch as parseAbi,ci as validate$1,bl as BaseError,cj as validate$2,bv as slice,ck as keccak256,cl as fromString,cm as formatAbiItem,bb as concat,cn as encode$1,P as readContract,N as uint8ArrayToHex,co as toString,cp as toBoolean,cq as toBigInt,cr as toNumber,j as isHex,af as isContractDeployed,au as getContract,aC as isZkSyncChain,f as getRpcClient,cs as eth_call,ct as hexToBool,cu as stringToBytes,cv as toBytes,av as keccak256$1,cw as concat$1,cx as extractEip712DomainTypes,cy as validate$3,cz as hashDomain,bn as fromHex,i as toHex$1,ap as encode$2}from'./index-BJCJzM2_.js';import {concatHex}from'./concat-hex-P_Wlv6qy.js';import {a as toHex}from'./Signature-TDCoSgpl.js';import {p as prepareCreateAccount}from'./index-M-ATGGAh.js';import'react';import'react/jsx-runtime';import'@0xfutbol/id-sign';import'react-use';import'@0xfutbol/constants';import'thirdweb';import'@matchain/matchid-sdk-react';import'@tanstack/react-query';import'@matchain/matchid-sdk-react/index.css';import'react-dom';import'./send-eip712-transaction-Cgw_Juhh.js';import'./eth_sendRawTransaction-IkplcvCa.js';import'./sha256-DQJaatHj.js';const ERC_6492_MAGIC_VALUE = "0x6492649264926492649264926492649264926492649264926492649264926492";/** * @description Serializes a signature for use with [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492). The signature must be generated by a signer for an [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) Account Factory account with counterfactual deployment addresses. * * @param {@link Erc6492Signature} signature The signature object to serialize into Hex format * @param {string} signature.address The ERC-4337 Account Factory address * @param {Hex} signature.data Account deployment calldata (if not deployed) for counterfactual verification * @param {Hex} signature.signature The original signature * * @returns {Hex} The serialized signature * * @example * ```ts * import { serializeErc6492Signature } from 'thirdweb/auth'; * * const serializedSignature = serializeErc6492Signature({ * address: '0x...', * data: '0x...', * signature: '0x...', * }); * // 0x000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b000000000000000000000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492 * ``` * @auth */ function serializeErc6492Signature({ address, data, signature, }) { return concatHex([ encodeAbiParameters([{ type: "address" }, { type: "bytes" }, { type: "bytes" }], [address, data, signature]), ERC_6492_MAGIC_VALUE, ]); }/** @internal */ function isSignatures(value) { for (const item of value) { if (typeof item !== 'string') return false; } return true; }/** @internal */ function from(abi) { if (isSignatures(abi)) return parseAbi(abi); return abi; }/** @internal */ function normalizeSignature(signature) { let active = true; let current = ''; let level = 0; let result = ''; let valid = false; for (let i = 0; i < signature.length; i++) { const char = signature[i]; // If the character is a separator, we want to reactivate. if (['(', ')', ','].includes(char)) active = true; // If the character is a "level" token, we want to increment/decrement. if (char === '(') level++; if (char === ')') level--; // If we aren't active, we don't want to mutate the result. if (!active) continue; // If level === 0, we are at the definition level. if (level === 0) { if (char === ' ' && ['event', 'function', 'error', ''].includes(result)) result = ''; else { result += char; // If we are at the end of the definition, we must be finished. if (char === ')') { valid = true; break; } } continue; } // Ignore spaces if (char === ' ') { // If the previous character is a separator, and the current section isn't empty, we want to deactivate. if (signature[i - 1] !== ',' && current !== ',' && current !== ',(') { current = ''; active = false; } continue; } result += char; current += char; } if (!valid) throw new BaseError('Unable to normalize signature.'); return result; } /** @internal */ function isArgOfType(arg, abiParameter) { const argType = typeof arg; const abiParameterType = abiParameter.type; switch (abiParameterType) { case 'address': return validate$1(arg, { strict: false }); case 'bool': return argType === 'boolean'; case 'function': return argType === 'string'; case 'string': return argType === 'string'; default: { if (abiParameterType === 'tuple' && 'components' in abiParameter) return Object.values(abiParameter.components).every((component, index) => { return isArgOfType(Object.values(arg)[index], component); }); // `(u)int<M>`: (un)signed integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0` // https://regexr.com/6v8hp if (/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(abiParameterType)) return argType === 'number' || argType === 'bigint'; // `bytes<M>`: binary type of `M` bytes, `0 < M <= 32` // https://regexr.com/6va55 if (/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(abiParameterType)) return argType === 'string' || arg instanceof Uint8Array; // fixed-length (`<type>[M]`) and dynamic (`<type>[]`) arrays // https://regexr.com/6va6i if (/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(abiParameterType)) { return (Array.isArray(arg) && arg.every((x) => isArgOfType(x, { ...abiParameter, // Pop off `[]` or `[M]` from end of type type: abiParameterType.replace(/(\[[0-9]{0,}\])$/, ''), }))); } return false; } } } /** @internal */ function getAmbiguousTypes(sourceParameters, targetParameters, args) { for (const parameterIndex in sourceParameters) { const sourceParameter = sourceParameters[parameterIndex]; const targetParameter = targetParameters[parameterIndex]; if (sourceParameter.type === 'tuple' && targetParameter.type === 'tuple' && 'components' in sourceParameter && 'components' in targetParameter) return getAmbiguousTypes(sourceParameter.components, targetParameter.components, args[parameterIndex]); const types = [sourceParameter.type, targetParameter.type]; const ambiguous = (() => { if (types.includes('address') && types.includes('bytes20')) return true; if (types.includes('address') && types.includes('string')) return validate$1(args[parameterIndex], { strict: false, }); if (types.includes('address') && types.includes('bytes')) return validate$1(args[parameterIndex], { strict: false, }); return false; })(); if (ambiguous) return types; } return; }/** * Extracts an {@link ox#AbiItem.AbiItem} from an {@link ox#Abi.Abi} given a name and optional arguments. * * @example * ABI Items can be extracted by their name using the `name` option: * * ```ts twoslash * import { Abi, AbiItem } from 'ox' * * const abi = Abi.from([ * 'function foo()', * 'event Transfer(address owner, address to, uint256 tokenId)', * 'function bar(string a) returns (uint256 x)', * ]) * * const item = AbiItem.fromAbi(abi, 'Transfer') // [!code focus] * // ^? * * * * * * * ``` * * @example * ### Extracting by Selector * * ABI Items can be extract by their selector when {@link ox#Hex.Hex} is provided to `name`. * * ```ts twoslash * import { Abi, AbiItem } from 'ox' * * const abi = Abi.from([ * 'function foo()', * 'event Transfer(address owner, address to, uint256 tokenId)', * 'function bar(string a) returns (uint256 x)', * ]) * const item = AbiItem.fromAbi(abi, '0x095ea7b3') // [!code focus] * // ^? * * * * * * * * * * * * * * ``` * * :::note * * Extracting via a hex selector is useful when extracting an ABI Item from an `eth_call` RPC response, * a Transaction `input`, or from Event Log `topics`. * * ::: * * @param abi - The ABI to extract from. * @param name - The name (or selector) of the ABI item to extract. * @param options - Extraction options. * @returns The ABI item. */ function fromAbi$2(abi, name, options) { const { args = [], prepare = true } = (options ?? {}); const isSelector = validate$2(name, { strict: false }); const abiItems = abi.filter((abiItem) => { if (isSelector) { if (abiItem.type === 'function' || abiItem.type === 'error') return getSelector$1(abiItem) === slice(name, 0, 4); if (abiItem.type === 'event') return getSignatureHash(abiItem) === name; return false; } return 'name' in abiItem && abiItem.name === name; }); if (abiItems.length === 0) throw new NotFoundError({ name: name }); if (abiItems.length === 1) return { ...abiItems[0], ...(prepare ? { hash: getSignatureHash(abiItems[0]) } : {}), }; let matchedAbiItem = undefined; for (const abiItem of abiItems) { if (!('inputs' in abiItem)) continue; if (!args || args.length === 0) { if (!abiItem.inputs || abiItem.inputs.length === 0) return { ...abiItem, ...(prepare ? { hash: getSignatureHash(abiItem) } : {}), }; continue; } if (!abiItem.inputs) continue; if (abiItem.inputs.length === 0) continue; if (abiItem.inputs.length !== args.length) continue; const matched = args.every((arg, index) => { const abiParameter = 'inputs' in abiItem && abiItem.inputs[index]; if (!abiParameter) return false; return isArgOfType(arg, abiParameter); }); if (matched) { // Check for ambiguity against already matched parameters (e.g. `address` vs `bytes20`). if (matchedAbiItem && 'inputs' in matchedAbiItem && matchedAbiItem.inputs) { const ambiguousTypes = getAmbiguousTypes(abiItem.inputs, matchedAbiItem.inputs, args); if (ambiguousTypes) throw new AmbiguityError({ abiItem, type: ambiguousTypes[0], }, { abiItem: matchedAbiItem, type: ambiguousTypes[1], }); } matchedAbiItem = abiItem; } } const abiItem = (() => { if (matchedAbiItem) return matchedAbiItem; const [abiItem, ...overloads] = abiItems; return { ...abiItem, overloads }; })(); if (!abiItem) throw new NotFoundError({ name: name }); return { ...abiItem, ...(prepare ? { hash: getSignatureHash(abiItem) } : {}), }; } /** * Computes the [4-byte selector](https://solidity-by-example.org/function-selector/) for an {@link ox#AbiItem.AbiItem}. * * Useful for computing function selectors for calldata. * * @example * ```ts twoslash * import { AbiItem } from 'ox' * * const selector = AbiItem.getSelector('function ownerOf(uint256 tokenId)') * // @log: '0x6352211e' * ``` * * @example * ```ts twoslash * import { AbiItem } from 'ox' * * const selector = AbiItem.getSelector({ * inputs: [{ type: 'uint256' }], * name: 'ownerOf', * outputs: [], * stateMutability: 'view', * type: 'function' * }) * // @log: '0x6352211e' * ``` * * @param abiItem - The ABI item to compute the selector for. Can be a signature or an ABI item for an error, event, function, etc. * @returns The first 4 bytes of the {@link ox#Hash.(keccak256:function)} hash of the function signature. */ function getSelector$1(abiItem) { return slice(getSignatureHash(abiItem), 0, 4); } /** * Computes the stringified signature for a given {@link ox#AbiItem.AbiItem}. * * @example * ```ts twoslash * import { AbiItem } from 'ox' * * const signature = AbiItem.getSignature('function ownerOf(uint256 tokenId)') * // @log: 'ownerOf(uint256)' * ``` * * @example * ```ts twoslash * import { AbiItem } from 'ox' * * const signature = AbiItem.getSignature({ * name: 'ownerOf', * type: 'function', * inputs: [{ name: 'tokenId', type: 'uint256' }], * outputs: [], * stateMutability: 'view', * }) * // @log: 'ownerOf(uint256)' * ``` * * @param abiItem - The ABI Item to compute the signature for. * @returns The stringified signature of the ABI Item. */ function getSignature(abiItem) { const signature = (() => { if (typeof abiItem === 'string') return abiItem; return formatAbiItem(abiItem); })(); return normalizeSignature(signature); } /** * Computes the signature hash for an {@link ox#AbiItem.AbiItem}. * * Useful for computing Event Topic values. * * @example * ```ts twoslash * import { AbiItem } from 'ox' * * const hash = AbiItem.getSignatureHash('event Transfer(address indexed from, address indexed to, uint256 amount)') * // @log: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' * ``` * * @example * ```ts twoslash * import { AbiItem } from 'ox' * * const hash = AbiItem.getSignatureHash({ * name: 'Transfer', * type: 'event', * inputs: [ * { name: 'from', type: 'address', indexed: true }, * { name: 'to', type: 'address', indexed: true }, * { name: 'amount', type: 'uint256', indexed: false }, * ], * }) * // @log: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' * ``` * * @param abiItem - The ABI Item to compute the signature hash for. * @returns The {@link ox#Hash.(keccak256:function)} hash of the ABI item's signature. */ function getSignatureHash(abiItem) { if (typeof abiItem !== 'string' && 'hash' in abiItem && abiItem.hash) return abiItem.hash; return keccak256(fromString(getSignature(abiItem))); } /** * Throws when ambiguous types are found on overloaded ABI items. * * @example * ```ts twoslash * import { Abi, AbiFunction } from 'ox' * * const foo = Abi.from(['function foo(address)', 'function foo(bytes20)']) * AbiFunction.fromAbi(foo, 'foo', { * args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'], * }) * // @error: AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items. * // @error: `bytes20` in `foo(bytes20)`, and * // @error: `address` in `foo(address)` * // @error: These types encode differently and cannot be distinguished at runtime. * // @error: Remove one of the ambiguous items in the ABI. * ``` * * ### Solution * * Remove one of the ambiguous types from the ABI. * * ```ts twoslash * import { Abi, AbiFunction } from 'ox' * * const foo = Abi.from([ * 'function foo(address)', * 'function foo(bytes20)' // [!code --] * ]) * AbiFunction.fromAbi(foo, 'foo', { * args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'], * }) * // @error: AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items. * // @error: `bytes20` in `foo(bytes20)`, and * // @error: `address` in `foo(address)` * // @error: These types encode differently and cannot be distinguished at runtime. * // @error: Remove one of the ambiguous items in the ABI. * ``` */ class AmbiguityError extends BaseError { constructor(x, y) { super('Found ambiguous types in overloaded ABI Items.', { metaMessages: [ // TODO: abitype to add support for signature-formatted ABI items. `\`${x.type}\` in \`${normalizeSignature(formatAbiItem(x.abiItem))}\`, and`, `\`${y.type}\` in \`${normalizeSignature(formatAbiItem(y.abiItem))}\``, '', 'These types encode differently and cannot be distinguished at runtime.', 'Remove one of the ambiguous items in the ABI.', ], }); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'AbiItem.AmbiguityError' }); } } /** * Throws when an ABI item is not found in the ABI. * * @example * ```ts twoslash * // @noErrors * import { Abi, AbiFunction } from 'ox' * * const foo = Abi.from([ * 'function foo(address)', * 'function bar(uint)' * ]) * AbiFunction.fromAbi(foo, 'baz') * // @error: AbiItem.NotFoundError: ABI function with name "baz" not found. * ``` * * ### Solution * * Ensure the ABI item exists on the ABI. * * ```ts twoslash * // @noErrors * import { Abi, AbiFunction } from 'ox' * * const foo = Abi.from([ * 'function foo(address)', * 'function bar(uint)', * 'function baz(bool)' // [!code ++] * ]) * AbiFunction.fromAbi(foo, 'baz') * ``` */ class NotFoundError extends BaseError { constructor({ name, data, type = 'item', }) { const selector = (() => { if (name) return ` with name "${name}"`; if (data) return ` with data "${data}"`; return ''; })(); super(`ABI ${type}${selector} not found.`); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'AbiItem.NotFoundError' }); } }/** * ABI-encodes the provided constructor input (`inputs`). * * @example * ```ts twoslash * import { AbiConstructor } from 'ox' * * const constructor = AbiConstructor.from('constructor(address, uint256)') * * const data = AbiConstructor.encode(constructor, { * bytecode: '0x...', * args: ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 123n], * }) * ``` * * @example * ### End-to-end * * Below is an end-to-end example of using `AbiConstructor.encode` to encode the constructor of a contract and deploy it. * * ```ts twoslash * import 'ox/window' * import { AbiConstructor, Hex } from 'ox' * * // 1. Instantiate the ABI Constructor. * const constructor = AbiConstructor.from( * 'constructor(address owner, uint256 amount)', * ) * * // 2. Encode the ABI Constructor. * const data = AbiConstructor.encode(constructor, { * bytecode: '0x...', * args: ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 123n], * }) * * // 3. Deploy the contract. * const hash = await window.ethereum!.request({ * method: 'eth_sendTransaction', * params: [{ data }], * }) * ``` * * :::note * * For simplicity, the above example uses `window.ethereum.request`, but you can use any * type of JSON-RPC interface. * * ::: * * @param abiConstructor - The ABI Constructor to encode. * @param options - Encoding options. * @returns The encoded constructor. */ function encode(abiConstructor, options) { const { bytecode, args } = options; return concat(bytecode, abiConstructor.inputs?.length && args?.length ? encode$1(abiConstructor.inputs, args) : '0x'); } /** @internal */ function fromAbi$1(abi) { const item = abi.find((item) => item.type === 'constructor'); if (!item) throw new NotFoundError({ name: 'constructor' }); return item; }/** * ABI-encodes function arguments (`inputs`), prefixed with the 4 byte function selector. * * :::tip * * This function is typically used to encode a contract function and its arguments for contract calls (e.g. `data` parameter of an `eth_call` or `eth_sendTransaction`). * * See the [End-to-end Example](#end-to-end). * * ::: * * @example * ```ts twoslash * import { AbiFunction } from 'ox' * * const approve = AbiFunction.from('function approve(address, uint256)') * * const data = AbiFunction.encodeData( // [!code focus] * approve, // [!code focus] * ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 69420n] // [!code focus] * ) // [!code focus] * // @log: '0x095ea7b3000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000000000000010f2c' * ``` * * @example * You can extract an ABI Function from a JSON ABI with {@link ox#AbiFunction.(fromAbi:function)}: * * ```ts twoslash * // @noErrors * import { Abi, AbiFunction } from 'ox' * * const erc20Abi = Abi.from([...]) // [!code hl] * const approve = AbiFunction.fromAbi(erc20Abi, 'approve') // [!code hl] * * const data = AbiFunction.encodeData( * approve, * ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 69420n] * ) * // @log: '0x095ea7b3000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000000000000010f2c' * ``` * * @example * ### End-to-end * * Below is an end-to-end example of using `AbiFunction.encodeData` to encode the input of a `balanceOf` contract call on the [Wagmi Mint Example contract](https://etherscan.io/address/0xfba3912ca04dd458c843e2ee08967fc04f3579c2). * * ```ts twoslash * import 'ox/window' * import { Abi, AbiFunction } from 'ox' * * // 1. Extract the Function from the Contract's ABI. * const abi = Abi.from([ * // ... * { * name: 'balanceOf', * type: 'function', * inputs: [{ name: 'account', type: 'address' }], * outputs: [{ name: 'balance', type: 'uint256' }], * stateMutability: 'view', * }, * // ... * ]) * const balanceOf = AbiFunction.fromAbi(abi, 'balanceOf') * * // 2. Encode the Function Input. // [!code focus] * const data = AbiFunction.encodeData( // [!code focus] * balanceOf, // [!code focus] * ['0xd2135CfB216b74109775236E36d4b433F1DF507B'] // [!code focus] * ) // [!code focus] * * // 3. Perform the Contract Call. * const response = await window.ethereum!.request({ * method: 'eth_call', * params: [ * { * data, * to: '0xfba3912ca04dd458c843e2ee08967fc04f3579c2', * }, * ], * }) * * // 4. Decode the Function Output. * const balance = AbiFunction.decodeResult(balanceOf, response) * ``` * * :::note * * For simplicity, the above example uses `window.ethereum.request`, but you can use any * type of JSON-RPC interface. * * ::: * * @param abiFunction - ABI Function to encode * @param args - Function arguments * @returns ABI-encoded function name and arguments */ function encodeData$1(abiFunction, ...args) { const { overloads } = abiFunction; const item = overloads ? fromAbi([abiFunction, ...overloads], abiFunction.name, { args: args[0], }) : abiFunction; const selector = getSelector(item); const data = args.length > 0 ? encode$1(item.inputs, args[0]) : undefined; return data ? concat(selector, data) : selector; } /** * Extracts an {@link ox#AbiFunction.AbiFunction} from an {@link ox#Abi.Abi} given a name and optional arguments. * * @example * ### Extracting by Name * * ABI Functions can be extracted by their name using the `name` option: * * ```ts twoslash * import { Abi, AbiFunction } from 'ox' * * const abi = Abi.from([ * 'function foo()', * 'event Transfer(address owner, address to, uint256 tokenId)', * 'function bar(string a) returns (uint256 x)', * ]) * * const item = AbiFunction.fromAbi(abi, 'foo') // [!code focus] * // ^? * * * * * * * ``` * * @example * ### Extracting by Selector * * ABI Functions can be extract by their selector when {@link ox#Hex.Hex} is provided to `name`. * * ```ts twoslash * import { Abi, AbiFunction } from 'ox' * * const abi = Abi.from([ * 'function foo()', * 'event Transfer(address owner, address to, uint256 tokenId)', * 'function bar(string a) returns (uint256 x)', * ]) * const item = AbiFunction.fromAbi(abi, '0x095ea7b3') // [!code focus] * // ^? * * * * * * * * * * ``` * * :::note * * Extracting via a hex selector is useful when extracting an ABI Function from an `eth_call` RPC response or * from a Transaction `input`. * * ::: * * @param abi - The ABI to extract from. * @param name - The name (or selector) of the ABI item to extract. * @param options - Extraction options. * @returns The ABI item. */ function fromAbi(abi, name, options) { const item = fromAbi$2(abi, name, options); if (item.type !== 'function') throw new NotFoundError({ name, type: 'function' }); return item; } /** * Computes the [4-byte selector](https://solidity-by-example.org/function-selector/) for an {@link ox#AbiFunction.AbiFunction}. * * Useful for computing function selectors for calldata. * * @example * ```ts twoslash * import { AbiFunction } from 'ox' * * const selector = AbiFunction.getSelector('function ownerOf(uint256 tokenId)') * // @log: '0x6352211e' * ``` * * @example * ```ts twoslash * import { AbiFunction } from 'ox' * * const selector = AbiFunction.getSelector({ * inputs: [{ type: 'uint256' }], * name: 'ownerOf', * outputs: [], * stateMutability: 'view', * type: 'function' * }) * // @log: '0x6352211e' * ``` * * @param abiItem - The ABI item to compute the selector for. * @returns The first 4 bytes of the {@link ox#Hash.(keccak256:function)} hash of the function signature. */ function getSelector(abiItem) { return getSelector$1(abiItem); }/** * Magic bytes used to identify ERC-6492 wrapped signatures. */ const magicBytes = '0x6492649264926492649264926492649264926492649264926492649264926492'; /** * Deployless ERC-6492 signature verification bytecode. */ const universalSignatureValidatorBytecode = '0x608060405234801561001057600080fd5b5060405161069438038061069483398101604081905261002f9161051e565b600061003c848484610048565b9050806000526001601ff35b60007f64926492649264926492649264926492649264926492649264926492649264926100748361040c565b036101e7576000606080848060200190518101906100929190610577565b60405192955090935091506000906001600160a01b038516906100b69085906105dd565b6000604051808303816000865af19150503d80600081146100f3576040519150601f19603f3d011682016040523d82523d6000602084013e6100f8565b606091505b50509050876001600160a01b03163b60000361016057806101605760405162461bcd60e51b815260206004820152601e60248201527f5369676e617475726556616c696461746f723a206465706c6f796d656e74000060448201526064015b60405180910390fd5b604051630b135d3f60e11b808252906001600160a01b038a1690631626ba7e90610190908b9087906004016105f9565b602060405180830381865afa1580156101ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d19190610633565b6001600160e01b03191614945050505050610405565b6001600160a01b0384163b1561027a57604051630b135d3f60e11b808252906001600160a01b03861690631626ba7e9061022790879087906004016105f9565b602060405180830381865afa158015610244573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102689190610633565b6001600160e01b031916149050610405565b81516041146102df5760405162461bcd60e51b815260206004820152603a602482015260008051602061067483398151915260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610157565b6102e7610425565b5060208201516040808401518451859392600091859190811061030c5761030c61065d565b016020015160f81c9050601b811480159061032b57508060ff16601c14155b1561038c5760405162461bcd60e51b815260206004820152603b602482015260008051602061067483398151915260448201527f3a20696e76616c6964207369676e617475726520762076616c756500000000006064820152608401610157565b60408051600081526020810180835289905260ff83169181019190915260608101849052608081018390526001600160a01b0389169060019060a0016020604051602081039080840390855afa1580156103ea573d6000803e3d6000fd5b505050602060405103516001600160a01b0316149450505050505b9392505050565b600060208251101561041d57600080fd5b508051015190565b60405180606001604052806003906020820280368337509192915050565b6001600160a01b038116811461045857600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561048c578181015183820152602001610474565b50506000910152565b600082601f8301126104a657600080fd5b81516001600160401b038111156104bf576104bf61045b565b604051601f8201601f19908116603f011681016001600160401b03811182821017156104ed576104ed61045b565b60405281815283820160200185101561050557600080fd5b610516826020830160208701610471565b949350505050565b60008060006060848603121561053357600080fd5b835161053e81610443565b6020850151604086015191945092506001600160401b0381111561056157600080fd5b61056d86828701610495565b9150509250925092565b60008060006060848603121561058c57600080fd5b835161059781610443565b60208501519093506001600160401b038111156105b357600080fd5b6105bf86828701610495565b604086015190935090506001600160401b0381111561056157600080fd5b600082516105ef818460208701610471565b9190910192915050565b828152604060208201526000825180604084015261061e816060850160208701610471565b601f01601f1916919091016060019392505050565b60006020828403121561064557600080fd5b81516001600160e01b03198116811461040557600080fd5b634e487b7160e01b600052603260045260246000fdfe5369676e617475726556616c696461746f72237265636f7665725369676e6572'; /** * ABI for the ERC-6492 universal deployless signature validator contract. * * Constructor return value is `0x1` (valid) or `0x0` (invalid). */ const universalSignatureValidatorAbi = [ { inputs: [ { name: '_signer', type: 'address', }, { name: '_hash', type: 'bytes32', }, { name: '_signature', type: 'bytes', }, ], stateMutability: 'nonpayable', type: 'constructor', }, { inputs: [ { name: '_signer', type: 'address', }, { name: '_hash', type: 'bytes32', }, { name: '_signature', type: 'bytes', }, ], outputs: [ { type: 'bool', }, ], stateMutability: 'nonpayable', type: 'function', name: 'isValidSig', }, ]; /** * Asserts that the wrapped signature is valid. * * @example * ```ts twoslash * import { WrappedSignature } from 'ox/erc6492' * * WrappedSignature.assert('0xdeadbeef') * // @error: InvalidWrappedSignatureError: Value `0xdeadbeef` is an invalid ERC-6492 wrapped signature. * ``` * * @param wrapped - The wrapped signature to assert. */ function assert(wrapped) { if (slice(wrapped, -32) !== magicBytes) throw new InvalidWrappedSignatureError(wrapped); } /** * Validates a wrapped signature. Returns `true` if the wrapped signature is valid, `false` otherwise. * * @example * ```ts twoslash * import { WrappedSignature } from 'ox/erc6492' * * const valid = WrappedSignature.validate('0xdeadbeef') * // @log: false * ``` * * @param wrapped - The wrapped signature to validate. * @returns `true` if the wrapped signature is valid, `false` otherwise. */ function validate(wrapped) { try { assert(wrapped); return true; } catch { return false; } } /** Thrown when the ERC-6492 wrapped signature is invalid. */ class InvalidWrappedSignatureError extends BaseError { constructor(wrapped) { super(`Value \`${wrapped}\` is an invalid ERC-6492 wrapped signature.`); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'WrappedSignature.InvalidWrappedSignatureError' }); } }const FN_SELECTOR = "0x1626ba7e"; const FN_INPUTS = [ { type: "bytes32", name: "hash", }, { type: "bytes", name: "signature", }, ]; const FN_OUTPUTS = [ { type: "bytes4", }, ]; /** * Calls the "isValidSignature" function on the contract. * @param options - The options for the isValidSignature function. * @returns The parsed result of the function call. * @extension ERC1271 * @example * ```ts * import { isValidSignature } from "thirdweb/extensions/erc1271"; * * const result = await isValidSignature({ * contract, * hash: ..., * signature: ..., * }); * * ``` */ async function isValidSignature(options) { return readContract({ contract: options.contract, method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS], params: [options.hash, options.signature], }); }/** * Converts a Uint8Array to the specified type. * @param bytes - The Uint8Array to convert. * @param toOrOpts - The target type or conversion options. * @returns The converted value of the specified type. * @example * ```ts * import { fromBytes } from "thirdweb/utils"; * const bytes = new Uint8Array([1, 164]); * const number = fromBytes(bytes, "number"); * console.log(number); // 420 * ``` * @utils */ function fromBytes(bytes, toOrOpts) { const opts = { to: toOrOpts } ; switch (opts.to) { case "number": return bytesToNumber(bytes, opts); case "bigint": return bytesToBigInt(bytes, opts); case "boolean": return bytesToBool(bytes, opts); case "string": return bytesToString(bytes, opts); default: return uint8ArrayToHex(bytes, opts); } } /** * Converts a Uint8Array of bytes to a bigint. * @param bytes - The Uint8Array of bytes to convert. * @param opts - Optional parameters for the conversion. * @returns The converted bigint. * @example * ```ts * import { bytesToBigInt } from "thirdweb/utils"; * const bytes = new Uint8Array([1, 164]); * const bigInt = bytesToBigInt(bytes); * console.log(bigInt); // 420n * ``` * @utils */ function bytesToBigInt(bytes, opts = {}) { return toBigInt(bytes, opts); } /** * Converts a byte array to a boolean value. * @param bytes_ - The byte array to convert. * @param opts - Optional parameters for the conversion. * @returns The boolean value converted from the byte array. * @throws Error if the byte array is invalid or the boolean representation is invalid. * @example * ```ts * import { bytesToBool } from "thirdweb/utils"; * const bytes = new Uint8Array([1]); * const bool = bytesToBool(bytes); * console.log(bool); // true * ``` * @utils */ function bytesToBool(bytes_, opts = {}) { return toBoolean(bytes_, opts); } /** * Converts a Uint8Array of bytes to a number. * @param bytes - The Uint8Array of bytes to convert. * @param opts - Optional configuration options. * @returns The converted number. * @example * ```ts * import { bytesToNumber } from "thirdweb/utils"; * const bytes = new Uint8Array([1, 164]); * const number = bytesToNumber(bytes); * console.log(number); // 420 * ``` * @utils */ function bytesToNumber(bytes, opts = {}) { return toNumber(bytes, opts); } /** * Converts an array of bytes to a string using UTF-8 encoding. * @param bytes_ - The array of bytes to convert. * @param opts - Optional parameters for the conversion. * @returns The resulting string. * @example * ```ts * import { bytesToString } from "thirdweb/utils"; * const bytes = new Uint8Array([72, 101, 108, 108, 111]); * const string = bytesToString(bytes); * console.log(string); // "Hello" * ``` * @utils */ function bytesToString(bytes_, opts = {}) { return toString(bytes_, opts); }const ZKSYNC_VALIDATOR_ADDRESS = "0xfB688330379976DA81eB64Fe4BF50d7401763B9C"; /** * @description Verify that an address created the provided signature for a given hash using [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492). This function is interoperable with all wallet types, including EOAs. * This function should rarely be used directly, instead use @see {import("./verify-signature.js")} and @see {import("./verify-typed-data.js")}} * * @param {Hex} options.hash The hash that was signed * @param {string | Uint8Array | Signature} options.signature The signature that was signed * @param {string} options.address The address that signed the hash * @param {ThirdwebClient} options.client The Thirdweb client * @param {Chain} options.chain The chain that the address is on. For an EOA, this can be any chain. * @param {string} [options.accountFactory.address] The address of the account factory that created the account if using a smart account with a custom account factory * @param {Hex} [options.accountFactory.verificationCalldata] The calldata that was used to create the account if using a smart account with a custom account factory * * @returns {Promise<boolean>} A promise that resolves to `true` if the signature is valid, or `false` otherwise. * * @example * ```ts * import { verifyHash } from "thirdweb/utils"; * const isValid = await verifyHash({ * hash: "0x1234", * signature: "0x1234", * address: "0x1234", * client, * chain, * }); * ``` * * @auth */ async function verifyHash({ hash, signature, address, client, chain, accountFactory, }) { const signatureHex = (() => { if (isHex(signature)) return signature; if (typeof signature === "object" && "r" in signature && "s" in signature) return toHex(signature); if (signature instanceof Uint8Array) return fromBytes(signature, "hex"); // We should never hit this but TS doesn't know that throw new Error(`Invalid signature type for signature ${signature}: ${typeof signature}`); })(); const isDeployed = await isContractDeployed(getContract({ address, client, chain, })); if (isDeployed) { const validEip1271 = await verifyEip1271Signature({ hash, signature: signatureHex, contract: getContract({ chain, address, client, }), }).catch((err) => { console.error("Error verifying EIP-1271 signature", err); return false; }); if (validEip1271) { return true; } } // contract not deployed, use erc6492 validator to verify signature const wrappedSignature = await (async () => { // If no factory is provided, we have to assume its already deployed or is an EOA // TODO: Figure out how to automatically tell if our default factory was used if (!accountFactory) return signatureHex; // If this sigature was already wrapped for ERC-6492, carry on if (validate(signatureHex)) return signatureHex; // Otherwise, serialize the signature for ERC-6492 validation return serializeErc6492Signature({ address: accountFactory.address, data: accountFactory.verificationCalldata, signature: signatureHex, }); })(); let verificationData; const zkSyncChain = await isZkSyncChain(chain); const abi = from(universalSignatureValidatorAbi); if (zkSyncChain) { // zksync chains dont support deploying code with eth_call // need to call a deployed contract instead verificationData = { to: ZKSYNC_VALIDATOR_ADDRESS, data: encodeData$1(fromAbi(abi, "isValidSig"), [address, hash, wrappedSignature]), }; } else { const validatorConstructor = fromAbi$1(abi); verificationData = { data: encode(validatorConstructor, { args: [address, hash, wrappedSignature], bytecode: universalSignatureValidatorBytecode, }), }; } const rpcRequest = getRpcClient({ chain, client, }); try { const result = await eth_call(rpcRequest, verificationData); return hexToBool(result); } catch { // Some chains do not support the eth_call simulation and will fail, so we fall back to regular EIP1271 validation const validEip1271 = await verifyEip1271Signature({ hash, signature: signatureHex, contract: getContract({ chain, address, client, }), }).catch((err) => { console.error("Error verifying EIP-1271 signature", err); return false; }); if (validEip1271) { return true; } // TODO: Improve overall RPC error handling so we can tell if this was an actual verification failure or some other error // Verification failed somehow return false; } } const EIP_1271_MAGIC_VALUE = "0x1626ba7e"; async function verifyEip1271Signature({ hash, signature, contract, }) { try { const result = await isValidSignature({ hash, signature, contract, }); return result === EIP_1271_MAGIC_VALUE; } catch (err) { console.error("Error verifying EIP-1271 signature", err); return false; } }const presignMessagePrefix = "\x19Ethereum Signed Message:\n"; /** * Ethereum Signed Message hashing * @param message - The message to hash, either as a string, a Uint8Array, or an object with a `raw` property containing a Uint8Array. * @param to_ - The desired output format of the hash (optional). Defaults to 'hex'. * @example * ```ts * import { hashMessage } from "thirdweb/utils"; * const hash = hashMessage("hello world"); * ``` * @returns The Ethereum Signed Message hash of the message in the specified format. * @utils */ function hashMessage(message, to_) { const messageBytes = (() => { if (typeof message === "string") { return stringToBytes(message); } if (message.raw instanceof Uint8Array) { return message.raw; } return toBytes(message.raw); })(); const prefixBytes = stringToBytes(`${presignMessagePrefix}${messageBytes.length}`); return keccak256$1(concat$1(prefixBytes, messageBytes), to_); }/** * @internal */ function hashTypedData(parameters) { const { domain = {}, message, primaryType, } = parameters; const types = { EIP712Domain: extractEip712DomainTypes(domain), ...parameters.types, }; // Need to do a runtime validation check on addresses, byte ranges, integer ranges, etc // as we can't statically check this with TypeScript. validate$3({ domain, message, primaryType, types, }); const parts = ["0x1901"]; if (domain) parts.push(hashDomain({ domain, types: types, })); if (primaryType !== "EIP712Domain") { const hashedStruct = (() => { const encoded = encodeData({ data: message, primaryType, types: types, }); return keccak256$1(encoded); })(); parts.push(hashedStruct); } return keccak256$1(concat$1(...parts.map((p) => fromHex(p)))); } function encodeData({ data, primaryType, types, }) { const encodedTypes = [{ type: "bytes32" }]; const encodedValues = [hashType({ primaryType, types })]; if (!types[primaryType]) throw new Error("Invalid types"); for (const field of types[primaryType]) { const [type, value] = encodeField({ types, name: field.name, type: field.type, value: data[field.name], }); encodedTypes.push(type); encodedValues.push(value); } return encodeAbiParameters(encodedTypes, encodedValues); } function hashType({ primaryType, types, }) { const encodedHashType = toHex$1(encodeType({ primaryType, types })); return keccak256$1(encodedHashType); } function encodeType({ primaryType, types, }) { let result = ""; const unsortedDeps = findTypeDependencies({ primaryType, types }); unsortedDeps.delete(primaryType); const deps = [primaryType, ...Array.from(unsortedDeps).sort()]; for (const type of deps) { if (!types[type]) throw new Error("Invalid types"); result += `${type}(${types[type] .map(({ name, type: t }) => `${t} ${name}`) .join(",")})`; } return result; } function findTypeDependencies({ primaryType: primaryType_, types, }, results = new Set()) { const match = primaryType_.match(/^\w*/u); const primaryType = match?.[0]; if (results.has(primaryType) || types[primaryType] === undefined) { return results; } results.add(primaryType); for (const field of types[primaryType]) { findTypeDependencies({ primaryType: field.type, types }, results); } return results; } function encodeField({ types, name, type, value, }) { if (types[type] !== undefined) { return [ { type: "bytes32" }, keccak256$1(encodeData({ data: value, primaryType: type, types })), ]; } if (type === "bytes") { const prepend = value.length % 2 ? "0" : ""; value = `0x${prepend + value.slice(2)}`; return [{ type: "bytes32" }, keccak256$1(value)]; } if (type === "string") return [{ type: "bytes32" }, keccak256$1(toHex$1(value))]; if (type.lastIndexOf("]") === type.length - 1) { const parsedType = type.slice(0, type.lastIndexOf("[")); const typeValuePairs = // biome-ignore lint/suspicious/noExplicitAny: Can't anticipate types of nested values value.map((item) => encodeField({ name, type: parsedType, types, value: item, })); return [ { type: "bytes32" }, keccak256$1(encodeAbiParameters(typeValuePairs.map(([t]) => t), typeValuePairs.map(([, v]) => v))), ]; } return [{ type }, value]; }/** * If the account is already deployed, generate an ERC-1271 signature. * If the account is not deployed, generate an ERC-6492 signature unless otherwise specified. * * @internal */ async function smartAccountSignMessage({ accountContract, factoryContract, options, message, }) { const originalMsgHash = hashMessage(message); const is712Factory = await checkFor712Factory({ factoryContract, accountContract, originalMsgHash, }); let sig; if (is712Factory) { const wrappedMessageHash = encodeAbiParameters([{ type: "bytes32" }], [originalMsgHash]); sig = await options.personalAccount.signTypedData({ domain: { name: "Account", version: "1", chainId: options.chain.id, verifyingContract: accountContract.address, }, primaryType: "AccountMessage", types: { AccountMessage: [{ name: "message", type: "bytes" }] }, message: { message: wrappedMessageHash }, }); } else { sig = await options.personalAccount.signMessage({ message }); } const isDeployed = await isContractDeployed(accountContract); if (isDeployed) { const isValid = await verifyEip1271Signature({ hash: originalMsgHash, signature: sig, contract: accountContract, }); if (isValid) { return sig; } throw new Error("Failed to verify signature"); } else { const deployTx = prepareCreateAccount({ factoryContract, adminAddress: options.personalAccount.address, accountSalt: options.overrides?.accountSalt, createAccountOverride: options.overrides?.createAccount, }); if (!deployTx) { throw new Error("Create account override not provided"); } const initCode = await encode$2(deployTx); const erc6492Sig = serializeErc6492Signature({ address: factoryContract.address, data: initCode, signature: sig, }); // check if the signature is valid const isValid = await verifyHash({ hash: originalMsgHash, signature: erc6492Sig, address: accountContract.address, chain: accountContract.chain, client: accountContract.client, }); if (isValid) { return erc6492Sig; } throw new Error("Unable to verify ERC-6492 signature after signing."); } } async function smartAccountSignTypedData({ accountContract, factoryContract, options, typedData, }) { const isSelfVerifyingContract = typedData.domain?.verifyingContract?.toLowerCase() === accountContract.address?.toLowerCase(); if (isSelfVerifyingContract) { // if the contract is self-verifying, we can just sign the mes