@0xfutbol/id
Version:
React component library with shared providers for 0xFutbol ID
1,394 lines • 53.6 kB
JavaScript
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