UNPKG

@0xfutbol/id

Version:

React component library with shared providers for 0xFutbol ID

1,435 lines 87.3 kB
import {B as BaseError,T as toSignatureHash,U as isHex,V as toFunctionSelector,W as isAddress,X as AbiItemAmbiguityError,Y as AbiEventNotFoundError,Z as formatAbiItem,_ as keccak256,$ as toBytes,a0 as encodeAbiParameters,a1 as InvalidAddressError,a2 as AbiEventSignatureEmptyTopicsError,a3 as AbiEventSignatureNotFoundError,a4 as DecodeLogTopicsMismatch,a5 as decodeAbiParameters,a6 as AbiDecodingDataSizeTooSmallError,a7 as PositionOutOfBoundsError,a8 as DecodeLogDataMismatch,a9 as size,P as readContract,aa as prepareContractCall,ab as once,r as randomBytesHex,ac as toWei,ad as ZERO_ADDRESS,ae as isContractDeployed,af as parseAbiItem,ag as withCache,j as isHex$1,M as stringToHex,ah as concat,ai as pad,aj as toHex,i as toHex$1,ak as getDefaultBundlerUrl,al as ENTRYPOINT_ADDRESS_v0_6,g as getClientFetch,s as stringify,am as hexToBigInt,an as getEntryPointVersion,ao as encode,ap as resolvePromisedValue,aq as hexToBytes,ar as isThirdwebUrl,as as getDefaultGasOverrides,at as getContract,au as keccak256$1,av as encodeAbiParameters$1,aw as maxUint96$1,ax as DUMMY_SIGNATURE,ay as ENTRYPOINT_ADDRESS_v0_7,az as decodeErrorResult,aA as MANAGED_ACCOUNT_GAS_BUFFER,aB as isZkSyncChain,aC as getDefaultAccountFactory,aD as sendTransaction,d as getAddress,p as parseTypedData,h as getCachedChain,t as trackTransaction,aE as allowance,aF as approve,aG as toSerializableTransaction,aH as isSmartWallet}from'./index-DNoa140s.js';import {t as toBigInt,p as populateEip712Transaction,s as signEip712Transaction}from'./send-eip712-transaction-uuOZ8__5.js';class FilterTypeNotSupportedError extends BaseError { constructor(type) { super(`Filter type "${type}" is not supported.`, { name: 'FilterTypeNotSupportedError', }); } }/** * Returns the event selector for a given event definition. * * @example * const selector = toEventSelector('Transfer(address indexed from, address indexed to, uint256 amount)') * // 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef */ const toEventSelector = toSignatureHash;function getAbiItem(parameters) { const { abi, args = [], name } = parameters; const isSelector = isHex(name, { strict: false }); const abiItems = abi.filter((abiItem) => { if (isSelector) { if (abiItem.type === 'function') return toFunctionSelector(abiItem) === name; if (abiItem.type === 'event') return toEventSelector(abiItem) === name; return false; } return 'name' in abiItem && abiItem.name === name; }); if (abiItems.length === 0) return undefined; if (abiItems.length === 1) return 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; 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 AbiItemAmbiguityError({ abiItem, type: ambiguousTypes[0], }, { abiItem: matchedAbiItem, type: ambiguousTypes[1], }); } matchedAbiItem = abiItem; } } if (matchedAbiItem) return matchedAbiItem; return abiItems[0]; } /** @internal */ function isArgOfType(arg, abiParameter) { const argType = typeof arg; const abiParameterType = abiParameter.type; switch (abiParameterType) { case 'address': return isAddress(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 isAddress(args[parameterIndex], { strict: false }); if (types.includes('address') && types.includes('bytes')) return isAddress(args[parameterIndex], { strict: false }); return false; })(); if (ambiguous) return types; } return; }const docsPath$1 = '/docs/contract/encodeEventTopics'; function encodeEventTopics(parameters) { const { abi, eventName, args } = parameters; let abiItem = abi[0]; if (eventName) { const item = getAbiItem({ abi, name: eventName }); if (!item) throw new AbiEventNotFoundError(eventName, { docsPath: docsPath$1 }); abiItem = item; } if (abiItem.type !== 'event') throw new AbiEventNotFoundError(undefined, { docsPath: docsPath$1 }); const definition = formatAbiItem(abiItem); const signature = toEventSelector(definition); let topics = []; if (args && 'inputs' in abiItem) { const indexedInputs = abiItem.inputs?.filter((param) => 'indexed' in param && param.indexed); const args_ = Array.isArray(args) ? args : Object.values(args).length > 0 ? (indexedInputs?.map((x) => args[x.name]) ?? []) : []; if (args_.length > 0) { topics = indexedInputs?.map((param, i) => { if (Array.isArray(args_[i])) return args_[i].map((_, j) => encodeArg({ param, value: args_[i][j] })); return typeof args_[i] !== 'undefined' && args_[i] !== null ? encodeArg({ param, value: args_[i] }) : null; }) ?? []; } } return [signature, ...topics]; } function encodeArg({ param, value, }) { if (param.type === 'string' || param.type === 'bytes') return keccak256(toBytes(value)); if (param.type === 'tuple' || param.type.match(/^(.*)\[(\d+)?\]$/)) throw new FilterTypeNotSupportedError(param.type); return encodeAbiParameters([param], [value]); }function isAddressEqual(a, b) { if (!isAddress(a, { strict: false })) throw new InvalidAddressError({ address: a }); if (!isAddress(b, { strict: false })) throw new InvalidAddressError({ address: b }); return a.toLowerCase() === b.toLowerCase(); }const docsPath = '/docs/contract/decodeEventLog'; function decodeEventLog(parameters) { const { abi, data, strict: strict_, topics, } = parameters; const strict = strict_ ?? true; const [signature, ...argTopics] = topics; if (!signature) throw new AbiEventSignatureEmptyTopicsError({ docsPath }); const abiItem = (() => { if (abi.length === 1) return abi[0]; return abi.find((x) => x.type === 'event' && signature === toEventSelector(formatAbiItem(x))); })(); if (!(abiItem && 'name' in abiItem) || abiItem.type !== 'event') throw new AbiEventSignatureNotFoundError(signature, { docsPath }); const { name, inputs } = abiItem; const isUnnamed = inputs?.some((x) => !('name' in x && x.name)); let args = isUnnamed ? [] : {}; // Decode topics (indexed args). const indexedInputs = inputs.filter((x) => 'indexed' in x && x.indexed); for (let i = 0; i < indexedInputs.length; i++) { const param = indexedInputs[i]; const topic = argTopics[i]; if (!topic) throw new DecodeLogTopicsMismatch({ abiItem, param: param, }); args[isUnnamed ? i : param.name || i] = decodeTopic({ param, value: topic }); } // Decode data (non-indexed args). const nonIndexedInputs = inputs.filter((x) => !('indexed' in x && x.indexed)); if (nonIndexedInputs.length > 0) { if (data && data !== '0x') { try { const decodedData = decodeAbiParameters(nonIndexedInputs, data); if (decodedData) { if (isUnnamed) args = [...args, ...decodedData]; else { for (let i = 0; i < nonIndexedInputs.length; i++) { args[nonIndexedInputs[i].name] = decodedData[i]; } } } } catch (err) { if (strict) { if (err instanceof AbiDecodingDataSizeTooSmallError || err instanceof PositionOutOfBoundsError) throw new DecodeLogDataMismatch({ abiItem, data: data, params: nonIndexedInputs, size: size(data), }); throw err; } } } else if (strict) { throw new DecodeLogDataMismatch({ abiItem, data: '0x', params: nonIndexedInputs, size: 0, }); } } return { eventName: name, args: Object.values(args).length > 0 ? args : undefined, }; } function decodeTopic({ param, value }) { if (param.type === 'string' || param.type === 'bytes' || param.type === 'tuple' || param.type.match(/^(.*)\[(\d+)?\]$/)) return value; const decodedArg = decodeAbiParameters([param], value) || []; return decodedArg[0]; }// TODO(v3): checksum address. /** * Extracts & decodes logs matching the provided signature(s) (`abi` + optional `eventName`) * from a set of opaque logs. * * @param parameters - {@link ParseEventLogsParameters} * @returns The logs. {@link ParseEventLogsReturnType} * * @example * import { createClient, http } from 'viem' * import { mainnet } from 'viem/chains' * import { parseEventLogs } from 'viem/op-stack' * * const client = createClient({ * chain: mainnet, * transport: http(), * }) * * const receipt = await getTransactionReceipt(client, { * hash: '0xec23b2ba4bc59ba61554507c1b1bc91649e6586eb2dd00c728e8ed0db8bb37ea', * }) * * const logs = parseEventLogs({ logs: receipt.logs }) * // [{ args: { ... }, eventName: 'TransactionDeposited', ... }, ...] */ function parseEventLogs$1(parameters) { const { abi, args, logs, strict = true } = parameters; const eventName = (() => { if (!parameters.eventName) return undefined; if (Array.isArray(parameters.eventName)) return parameters.eventName; return [parameters.eventName]; })(); return logs .map((log) => { try { const abiItem = abi.find((abiItem) => abiItem.type === 'event' && log.topics[0] === toEventSelector(abiItem)); if (!abiItem) return null; const event = decodeEventLog({ ...log, abi: [abiItem], strict, }); // Check that the decoded event name matches the provided event name. if (eventName && !eventName.includes(event.eventName)) return null; // Check that the decoded event args match the provided args. if (!includesArgs({ args: event.args, inputs: abiItem.inputs, matchArgs: args, })) return null; return { ...event, ...log }; } catch (err) { let eventName; let isUnnamed; if (err instanceof AbiEventSignatureNotFoundError) return null; if (err instanceof DecodeLogDataMismatch || err instanceof DecodeLogTopicsMismatch) { // If strict mode is on, and log data/topics do not match event definition, skip. if (strict) return null; eventName = err.abiItem.name; isUnnamed = err.abiItem.inputs?.some((x) => !('name' in x && x.name)); } // Set args to empty if there is an error decoding (e.g. indexed/non-indexed params mismatch). return { ...log, args: isUnnamed ? [] : {}, eventName }; } }) .filter(Boolean); } function includesArgs(parameters) { const { args, inputs, matchArgs } = parameters; if (!matchArgs) return true; if (!args) return false; function isEqual(input, value, arg) { try { if (input.type === 'address') return isAddressEqual(value, arg); if (input.type === 'string' || input.type === 'bytes') return keccak256(toBytes(value)) === arg; return value === arg; } catch { return false; } } if (Array.isArray(args) && Array.isArray(matchArgs)) { return matchArgs.every((value, index) => { if (value === null || value === undefined) return true; const input = inputs[index]; if (!input) return false; const value_ = Array.isArray(value) ? value : [value]; return value_.some((value) => isEqual(input, value, args[index])); }); } if (typeof args === 'object' && !Array.isArray(args) && typeof matchArgs === 'object' && !Array.isArray(matchArgs)) return Object.entries(matchArgs).every(([key, value]) => { if (value === null || value === undefined) return true; const input = inputs.find((input) => input.name === key); if (!input) return false; const value_ = Array.isArray(value) ? value : [value]; return value_.some((value) => isEqual(input, value, args[key])); }); return false; }const FN_SELECTOR$4 = "0xf15d424e"; const FN_INPUTS$4 = [ { type: "address", name: "signer", }, ]; const FN_OUTPUTS$4 = [ { type: "tuple", name: "permissions", components: [ { type: "address", name: "signer", }, { type: "address[]", name: "approvedTargets", }, { type: "uint256", name: "nativeTokenLimitPerTransaction", }, { type: "uint128", name: "startTimestamp", }, { type: "uint128", name: "endTimestamp", }, ], }, ]; /** * Calls the "getPermissionsForSigner" function on the contract. * @param options - The options for the getPermissionsForSigner function. * @returns The parsed result of the function call. * @extension ERC4337 * @example * ```ts * import { getPermissionsForSigner } from "thirdweb/extensions/erc4337"; * * const result = await getPermissionsForSigner({ * contract, * signer: ..., * }); * * ``` */ async function getPermissionsForSigner(options) { return readContract({ contract: options.contract, method: [FN_SELECTOR$4, FN_INPUTS$4, FN_OUTPUTS$4], params: [options.signer], }); }const FN_SELECTOR$3 = "0x5892e236"; const FN_INPUTS$3 = [ { type: "tuple", name: "req", components: [ { type: "address", name: "signer", }, { type: "uint8", name: "isAdmin", }, { type: "address[]", name: "approvedTargets", }, { type: "uint256", name: "nativeTokenLimitPerTransaction", }, { type: "uint128", name: "permissionStartTimestamp", }, { type: "uint128", name: "permissionEndTimestamp", }, { type: "uint128", name: "reqValidityStartTimestamp", }, { type: "uint128", name: "reqValidityEndTimestamp", }, { type: "bytes32", name: "uid", }, ], }, { type: "bytes", name: "signature", }, ]; const FN_OUTPUTS$3 = []; /** * Prepares a transaction to call the "setPermissionsForSigner" function on the contract. * @param options - The options for the "setPermissionsForSigner" function. * @returns A prepared transaction object. * @extension ERC4337 * @example * ```ts * import { sendTransaction } from "thirdweb"; * import { setPermissionsForSigner } from "thirdweb/extensions/erc4337"; * * const transaction = setPermissionsForSigner({ * contract, * req: ..., * signature: ..., * overrides: { * ... * } * }); * * // Send the transaction * await sendTransaction({ transaction, account }); * ``` */ function setPermissionsForSigner(options) { const asyncOptions = once(async () => { return "asyncParams" in options ? await options.asyncParams() : options; }); return prepareContractCall({ contract: options.contract, method: [FN_SELECTOR$3, FN_INPUTS$3, FN_OUTPUTS$3], params: async () => { const resolvedOptions = await asyncOptions(); return [resolvedOptions.req, resolvedOptions.signature]; }, value: async () => (await asyncOptions()).overrides?.value, accessList: async () => (await asyncOptions()).overrides?.accessList, gas: async () => (await asyncOptions()).overrides?.gas, gasPrice: async () => (await asyncOptions()).overrides?.gasPrice, maxFeePerGas: async () => (await asyncOptions()).overrides?.maxFeePerGas, maxPriorityFeePerGas: async () => (await asyncOptions()).overrides?.maxPriorityFeePerGas, nonce: async () => (await asyncOptions()).overrides?.nonce, extraGas: async () => (await asyncOptions()).overrides?.extraGas, erc20Value: async () => (await asyncOptions()).overrides?.erc20Value, }); }/** * @internal */ function tenYearsFromNow() { return new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10); // 10 years } /** * @internal */ function dateToSeconds(date) { return toBigInt(Math.floor(date.getTime() / 1000)); }const SignerPermissionRequest = [ { name: "signer", type: "address" }, { name: "isAdmin", type: "uint8" }, { name: "approvedTargets", type: "address[]" }, { name: "nativeTokenLimitPerTransaction", type: "uint256" }, { name: "permissionStartTimestamp", type: "uint128" }, { name: "permissionEndTimestamp", type: "uint128" }, { name: "reqValidityStartTimestamp", type: "uint128" }, { name: "reqValidityEndTimestamp", type: "uint128" }, { name: "uid", type: "bytes32" }, ];/** * @internal */ async function signPermissionRequest(options) { const { account, contract, req } = options; const signature = await account.signTypedData({ domain: { name: "Account", version: "1", verifyingContract: contract.address, chainId: contract.chain.id, }, primaryType: "SignerPermissionRequest", types: { SignerPermissionRequest }, message: req, }); return { req, signature }; } /** * @internal */ async function toContractPermissions(options) { const { target, permissions } = options; return { approvedTargets: permissions.approvedTargets === "*" ? [ZERO_ADDRESS] : permissions.approvedTargets, nativeTokenLimitPerTransaction: toWei(permissions.nativeTokenLimitPerTransaction?.toString() || "0"), permissionStartTimestamp: dateToSeconds(permissions.permissionStartTimestamp || new Date(0)), permissionEndTimestamp: dateToSeconds(permissions.permissionEndTimestamp || tenYearsFromNow()), reqValidityStartTimestamp: 0n, reqValidityEndTimestamp: dateToSeconds(tenYearsFromNow()), uid: await randomBytesHex(), isAdmin: 0, // session key flag signer: target, }; }/** * Adds session key permissions for a specified address. * @param options - The options for the removeSessionKey function. * @param {Contract} options.contract - The smart account contract to add the session key to * @returns The transaction object to be sent. * @example * ```ts * import { addSessionKey } from 'thirdweb/extensions/erc4337'; * import { sendTransaction } from 'thirdweb'; * * const transaction = addSessionKey({ * contract, * account, * sessionKeyAddress, * permissions: { * approvedTargets: ['0x...'], * nativeTokenLimitPerTransaction: 0.1, // in ETH * permissionStartTimestamp: new Date(), * permissionEndTimestamp: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), // 1 year from now * } * }); * * await sendTransaction({ transaction, account }); * ``` * @extension ERC4337 */ function addSessionKey(options) { const { contract, sessionKeyAddress, account, permissions } = options; return setPermissionsForSigner({ contract, async asyncParams() { const { req, signature } = await signPermissionRequest({ account, contract, req: await toContractPermissions({ target: sessionKeyAddress, permissions, }), }); return { signature, req }; }, }); } /** * Checks if the session key should be updated. * @param currentPermissions - The current permissions of the session key. * @param newPermissions - The new permissions to set for the session key. * @returns A boolean indicating if the session key should be updated. * @extension ERC4337 * @example * ```ts * import { shouldUpdateSessionKey } from "thirdweb/extensions/erc4337"; * * const shouldUpdate = await shouldUpdateSessionKey({ accountContract, sessionKeyAddress, newPermissions }); * ``` */ async function shouldUpdateSessionKey(args) { const { accountContract, sessionKeyAddress, newPermissions } = args; // check if account is deployed const accountDeployed = await isContractDeployed(accountContract); if (!accountDeployed) { return true; } // get current permissions const currentPermissions = await getPermissionsForSigner({ contract: accountContract, signer: sessionKeyAddress, }); // check end time validity if (currentPermissions.endTimestamp && currentPermissions.endTimestamp < Math.floor(new Date().getTime() / 1000)) { return true; } // check targets if (!areSessionKeyContractTargetsEqual(currentPermissions.approvedTargets, newPermissions.approvedTargets)) { return true; } // check if the new native token limit is greater than the current one if (toWei(newPermissions.nativeTokenLimitPerTransaction?.toString() ?? "0") > currentPermissions.nativeTokenLimitPerTransaction) { return true; } return false; } function areSessionKeyContractTargetsEqual(currentTargets, newTargets) { // Handle the case where approvedTargets is "*" if (newTargets === "*" && currentTargets.length === 1 && currentTargets[0] === ZERO_ADDRESS) { return true; } if (newTargets !== "*") { return newTargets .map((target) => target.toLowerCase()) .every((target) => currentTargets.map((t) => t.toLowerCase()).includes(target)); } return false; }/** * @internal */ const maxUint96 = 2n ** 96n - 1n;/** * Parses logs and returns the corresponding events. * @param options - The options for parsing logs. * @returns The parsed events. * @example * ```ts * import { parseEventLogs } from "thirdweb"; * const events = parseEventLogs({ * logs, * events: [preparedEvent, preparedEvent2], * }); * ``` * @contract */ function parseEventLogs(options) { const { logs, events, strict } = options; return parseEventLogs$1({ logs, abi: events.map((e) => e.abiEvent), strict, }); }/** * @internal */ function isAbiEvent(item) { return !!(item && typeof item === "object" && "type" in item && item.type === "event"); }/** * Prepares an event by parsing the signature, generating the event hash, and encoding the event topics. * @param options - The options for preparing the event. * @returns The prepared event object. * @example * ```ts * import { prepareEvent } from "thirdweb"; * const myEvent = prepareEvent({ * signature: "event MyEvent(uint256 myArg)", * }); * ``` * @contract */ function prepareEvent(options) { const { signature } = options; let resolvedSignature; if (isAbiEvent(signature)) { resolvedSignature = signature; } else { resolvedSignature = parseAbiItem(signature); } return { abiEvent: resolvedSignature, hash: toSignatureHash(resolvedSignature), // @ts-expect-error - TODO: investiagte why this complains, it works fine however topics: encodeEventTopics({ abi: [resolvedSignature], args: options.filters, }), }; }/** * Creates an event object for the UserOperationRevertReason event. * @param filters - Optional filters to apply to the event. * @returns The prepared event object. * @extension ERC4337 * @example * ```ts * import { getContractEvents } from "thirdweb"; * import { userOperationRevertReasonEvent } from "thirdweb/extensions/erc4337"; * * const events = await getContractEvents({ * contract, * events: [ * userOperationRevertReasonEvent({ * userOpHash: ..., * sender: ..., * }) * ], * }); * ``` */ function userOperationRevertReasonEvent(filters = {}) { return prepareEvent({ signature: "event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason)", filters, }); }/** * Creates an event object for the PostOpRevertReason event. * @param filters - Optional filters to apply to the event. * @returns The prepared event object. * @extension ERC4337 * @example * ```ts * import { getContractEvents } from "thirdweb"; * import { postOpRevertReasonEvent } from "thirdweb/extensions/erc4337"; * * const events = await getContractEvents({ * contract, * events: [ * postOpRevertReasonEvent({ * userOpHash: ..., * sender: ..., * }) * ], * }); * ``` */ function postOpRevertReasonEvent(filters = {}) { return prepareEvent({ signature: "event PostOpRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason)", filters, }); }function formatUserOperationReceipt(userOpReceiptRaw) { const { receipt: transactionReceipt } = userOpReceiptRaw; const receipt = { ...transactionReceipt, transactionHash: transactionReceipt.transactionHash, blockNumber: transactionReceipt.blockNumber ? BigInt(transactionReceipt.blockNumber) : null, contractAddress: transactionReceipt.contractAddress ? transactionReceipt.contractAddress : null, cumulativeGasUsed: transactionReceipt.cumulativeGasUsed ? BigInt(transactionReceipt.cumulativeGasUsed) : null, effectiveGasPrice: transactionReceipt.effectiveGasPrice ? BigInt(transactionReceipt.effectiveGasPrice) : null, gasUsed: transactionReceipt.gasUsed ? BigInt(transactionReceipt.gasUsed) : null, logs: transactionReceipt.logs, to: transactionReceipt.to ? transactionReceipt.to : null, transactionIndex: transactionReceipt.transactionIndex, status: transactionReceipt.status, type: transactionReceipt.type, }; if (transactionReceipt.blobGasPrice) receipt.blobGasPrice = BigInt(transactionReceipt.blobGasPrice); if (transactionReceipt.blobGasUsed) receipt.blobGasUsed = BigInt(transactionReceipt.blobGasUsed); const userOpReceipt = { ...userOpReceiptRaw, receipt, userOpHash: userOpReceiptRaw.userOpHash, actualGasCost: BigInt(userOpReceiptRaw.actualGasCost), actualGasUsed: BigInt(userOpReceiptRaw.actualGasUsed), nonce: BigInt(userOpReceiptRaw.nonce), }; return userOpReceipt; }/** * Predict the address of a smart account. * @param args - The options for predicting the address of a smart account. * @returns The predicted address of the smart account. * @example * ```ts * import { predictAddress } from "thirdweb/wallets/smart"; * * const predictedAddress = await predictAddress({ * factoryContract, * adminAddress, * accountSalt, * }); * ``` * @walletUtils * @deprecated Use `predictSmartAccountAddress` instead. */ async function predictAddress(args) { const { factoryContract, predictAddressOverride: predictAddress, adminAddress, accountSalt, accountAddress, } = args; if (predictAddress) { return predictAddress(factoryContract, adminAddress); } if (accountAddress) { return accountAddress; } if (!adminAddress) { throw new Error("Account address is required to predict the smart wallet address."); } return withCache(async () => { const saltHex = accountSalt && isHex$1(accountSalt) ? accountSalt : stringToHex(accountSalt ?? ""); return readContract({ contract: factoryContract, method: "function getAddress(address, bytes) returns (address)", params: [adminAddress, saltHex], }); }, { cacheKey: `${args.factoryContract.chain.id}-${args.factoryContract.address}-${args.adminAddress}-${args.accountSalt}`, cacheTime: 1000 * 60 * 60 * 24, // 1 day }); } /** * @internal */ function prepareCreateAccount(args) { const { adminAddress, factoryContract, createAccountOverride: createAccount, accountSalt, } = args; if (createAccount) { return createAccount(factoryContract, adminAddress); } const saltHex = accountSalt && isHex$1(accountSalt) ? accountSalt : stringToHex(accountSalt ?? ""); return prepareContractCall({ contract: factoryContract, method: "function createAccount(address, bytes) returns (address)", params: [adminAddress, saltHex], }); } /** * @internal */ function prepareExecute(args) { const { accountContract, transaction, executeOverride: execute } = args; if (execute) { return execute(accountContract, transaction); } return prepareContractCall({ contract: accountContract, method: "function execute(address, uint256, bytes)", params: [ transaction.to || "", transaction.value || 0n, transaction.data || "0x", ], // if gas is specified for the inner tx, use that and add 21k for the execute call on the account contract // this avoids another estimateGas call when bundling the userOp // and also allows for passing custom gas limits for the inner tx gas: transaction.gas ? transaction.gas + 21000n : undefined, }); } /** * @internal */ function prepareBatchExecute(args) { const { accountContract, transactions, executeBatchOverride: executeBatch, } = args; if (executeBatch) { return executeBatch(accountContract, transactions); } return prepareContractCall({ contract: accountContract, method: "function executeBatch(address[], uint256[], bytes[])", params: [ transactions.map((tx) => tx.to || ""), transactions.map((tx) => tx.value || 0n), transactions.map((tx) => tx.data || "0x"), ], }); }const FN_SELECTOR$2 = "0x35567e1a"; const FN_INPUTS$2 = [ { type: "address", name: "sender", }, { type: "uint192", name: "key", }, ]; const FN_OUTPUTS$2 = [ { type: "uint256", name: "nonce", }, ]; /** * Calls the "getNonce" function on the contract. * @param options - The options for the getNonce function. * @returns The parsed result of the function call. * @extension ERC4337 * @example * ```ts * import { getNonce } from "thirdweb/extensions/erc4337"; * * const result = await getNonce({ * contract, * sender: ..., * key: ..., * }); * * ``` */ async function getNonce(options) { return readContract({ contract: options.contract, method: [FN_SELECTOR$2, FN_INPUTS$2, FN_OUTPUTS$2], params: [options.sender, options.key], }); }const FN_SELECTOR$1 = "0xa6193531"; const FN_INPUTS$1 = [ { type: "tuple", name: "userOp", components: [ { type: "address", name: "sender", }, { type: "uint256", name: "nonce", }, { type: "bytes", name: "initCode", }, { type: "bytes", name: "callData", }, { type: "uint256", name: "callGasLimit", }, { type: "uint256", name: "verificationGasLimit", }, { type: "uint256", name: "preVerificationGas", }, { type: "uint256", name: "maxFeePerGas", }, { type: "uint256", name: "maxPriorityFeePerGas", }, { type: "bytes", name: "paymasterAndData", }, { type: "bytes", name: "signature", }, ], }, ]; const FN_OUTPUTS$1 = [ { type: "bytes32", }, ]; /** * Calls the "getUserOpHash" function on the contract. * @param options - The options for the getUserOpHash function. * @returns The parsed result of the function call. * @extension ERC4337 * @example * ```ts * import { getUserOpHash } from "thirdweb/extensions/erc4337"; * * const result = await getUserOpHash({ * contract, * userOp: ..., * }); * * ``` */ async function getUserOpHash$2(options) { return readContract({ contract: options.contract, method: [FN_SELECTOR$1, FN_INPUTS$1, FN_OUTPUTS$1], params: [options.userOp], }); }const FN_SELECTOR = "0x22cdde4c"; const FN_INPUTS = [ { type: "tuple", name: "userOp", components: [ { type: "address", name: "sender", }, { type: "uint256", name: "nonce", }, { type: "bytes", name: "initCode", }, { type: "bytes", name: "callData", }, { type: "bytes32", name: "accountGasLimits", }, { type: "uint256", name: "preVerificationGas", }, { type: "bytes32", name: "gasFees", }, { type: "bytes", name: "paymasterAndData", }, { type: "bytes", name: "signature", }, ], }, ]; const FN_OUTPUTS = [ { type: "bytes32", }, ]; /** * Calls the "getUserOpHash" function on the contract. * @param options - The options for the getUserOpHash function. * @returns The parsed result of the function call. * @extension ERC4337 * @example * ```ts * import { getUserOpHash } from "thirdweb/extensions/erc4337"; * * const result = await getUserOpHash({ * contract, * userOp: ..., * }); * * ``` */ async function getUserOpHash$1(options) { return readContract({ contract: options.contract, method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS], params: [options.userOp], }); }function getInitCode(unpackedUserOperation) { return unpackedUserOperation.factory ? concat([ unpackedUserOperation.factory, unpackedUserOperation.factoryData || "0x", ]) : "0x"; } function getAccountGasLimits(unpackedUserOperation) { return concat([ pad(toHex(BigInt(unpackedUserOperation.verificationGasLimit)), { size: 16, }), pad(toHex(BigInt(unpackedUserOperation.callGasLimit)), { size: 16 }), ]); } function getGasLimits(unpackedUserOperation) { return concat([ pad(toHex(BigInt(unpackedUserOperation.maxPriorityFeePerGas)), { size: 16, }), pad(toHex(BigInt(unpackedUserOperation.maxFeePerGas)), { size: 16 }), ]); } function getPaymasterAndData$1(unpackedUserOperation) { return unpackedUserOperation.paymaster ? concat([ unpackedUserOperation.paymaster, pad(toHex(BigInt(unpackedUserOperation.paymasterVerificationGasLimit || 0)), { size: 16, }), pad(toHex(BigInt(unpackedUserOperation.paymasterPostOpGasLimit || 0)), { size: 16, }), unpackedUserOperation.paymasterData || "0x", ]) : "0x"; } const getPackedUserOperation = (userOperation) => { return { sender: userOperation.sender, nonce: BigInt(userOperation.nonce), initCode: getInitCode(userOperation), callData: userOperation.callData, accountGasLimits: getAccountGasLimits(userOperation), preVerificationGas: BigInt(userOperation.preVerificationGas), gasFees: getGasLimits(userOperation), paymasterAndData: getPaymasterAndData$1(userOperation), signature: userOperation.signature, }; };const generateRandomUint192 = () => { const rand1 = BigInt(Math.floor(Math.random() * 0x100000000)); const rand2 = BigInt(Math.floor(Math.random() * 0x100000000)); const rand3 = BigInt(Math.floor(Math.random() * 0x100000000)); const rand4 = BigInt(Math.floor(Math.random() * 0x100000000)); const rand5 = BigInt(Math.floor(Math.random() * 0x100000000)); const rand6 = BigInt(Math.floor(Math.random() * 0x100000000)); return ((rand1 << BigInt(160)) | (rand2 << BigInt(128)) | (rand3 << BigInt(96)) | (rand4 << BigInt(64)) | (rand5 << BigInt(32)) | rand6); }; /** * @internal */ function hexlifyUserOp(userOp) { return Object.fromEntries(Object.entries(userOp).map(([key, val]) => [ key, // turn any value that's not hex into hex val === undefined || val === null || isHex$1(val) ? val : toHex$1(val), ])); }/** * Get paymaster and data details for a user operation. * @param args - The userOp and options * @returns - The paymaster and data details for the user operation. * @example * ```ts * import { getPaymasterAndData } from "thirdweb/wallets/smart"; * * const userOp = createUnsignedUserOp(...); * * const paymasterAndData = await getPaymasterAndData({ * userOp, * client, * chain, * }); * ``` * @walletUtils */ async function getPaymasterAndData(args) { const { userOp, paymasterOverride, client, chain, entrypointAddress } = args; if (paymasterOverride) { return paymasterOverride(userOp); } const headers = { "Content-Type": "application/json", }; const entrypoint = entrypointAddress ?? ENTRYPOINT_ADDRESS_v0_6; const paymasterUrl = getDefaultBundlerUrl(chain); const body = { jsonrpc: "2.0", id: 1, method: "pm_sponsorUserOperation", params: [hexlifyUserOp(userOp), entrypoint], }; // Ask the paymaster to sign the transaction and return a valid paymasterAndData value. const fetchWithHeaders = getClientFetch(client); const response = await fetchWithHeaders(paymasterUrl, { method: "POST", headers, body: stringify(body), }); const res = await response.json(); if (!response.ok) { const error = res.error || response.statusText; const code = res.code || "UNKNOWN"; throw new Error(`Paymaster error: ${error} Status: ${response.status} Code: ${code}`); } if (res.result) { // some paymasters return a string, some return an object with more data if (typeof res.result === "string") { return { paymasterAndData: res.result, }; } // check for policy errors if (res.result.policyId && res.result.reason) { console.warn(`Paymaster policy rejected this transaction with reason: ${res.result.reason} (policyId: ${res.result.policyId})`); } return { paymasterAndData: res.result.paymasterAndData, verificationGasLimit: res.result.verificationGasLimit ? hexToBigInt(res.result.verificationGasLimit) : undefined, preVerificationGas: res.result.preVerificationGas ? hexToBigInt(res.result.preVerificationGas) : undefined, callGasLimit: res.result.callGasLimit ? hexToBigInt(res.result.callGasLimit) : undefined, paymaster: res.result.paymaster, paymasterData: res.result.paymasterData, paymasterVerificationGasLimit: res.result.paymasterVerificationGasLimit ? hexToBigInt(res.result.paymasterVerificationGasLimit) : undefined, paymasterPostOpGasLimit: res.result.paymasterPostOpGasLimit ? hexToBigInt(res.result.paymasterPostOpGasLimit) : undefined, }; } const error = res.error?.message || res.error || response.statusText || "unknown error"; throw new Error(`Paymaster error from ${paymasterUrl}: ${error}`); }const isDeployingSet = new Set(); const getKey = (accountContract) => { return `${accountContract.chain.id}:${accountContract.address}`; }; const markAccountDeploying = (accountContract) => { isDeployingSet.add(getKey(accountContract)); }; const clearAccountDeploying = (accountContract) => { isDeployingSet.delete(getKey(accountContract)); }; const isAccountDeploying = (accountContract) => { return isDeployingSet.has(getKey(accountContract)); }; /** * Wait for the user operation to be mined. * @param args - The options and user operation hash * @returns - The transaction receipt * * @example * ```ts * import { waitForUserOpReceipt } from "thirdweb/wallets/smart"; * * const receipt = await waitForUserOpReceipt({ * chain, * client, * userOpHash, * }); * ``` * @walletUtils */ async function waitForUserOpReceipt(args) { const timeout = args.timeoutMs || 120000; // 2mins const interval = args.intervalMs || 1000; // 1s const endtime = Date.now() + timeout; while (Date.now() < endtime) { const userOpReceipt = await getUserOpReceipt(args); if (userOpReceipt) { return userOpReceipt; } await new Promise((resolve) => setTimeout(resolve, interval)); } throw new Error(`Timeout waiting for userOp to be mined on chain ${args.chain.id} with UserOp hash: ${args.userOpHash}`); } /** * Creates an unsigned user operation from a prepared transaction. * @param args - The prepared transaction and options * @returns - The unsigned user operation * @example * ```ts * import { createUnsignedUserOp } from "thirdweb/wallets/smart"; * * const transaction = prepareContractCall(...); * * const userOp = await createUnsignedUserOp({ * transaction, * factoryContract, * accountContract, * adminAddress, * sponsorGas, * overrides, * }); * ``` * @walletUtils */ async function createUnsignedUserOp(args) { const { transaction: executeTx, accountContract, factoryContract, adminAddress, overrides, sponsorGas, waitForDeployment = true, isDeployedOverride, } = args; const chain = executeTx.chain; const client = executeTx.client; const bundlerOptions = { client, chain, bundlerUrl: overrides?.bundlerUrl, entrypointAddress: overrides?.entrypointAddress, }; const entrypointVersion = getEntryPointVersion(args.overrides?.entrypointAddress || ENTRYPOINT_ADDRESS_v0_6); const [isDeployed, callData, callGasLimit, gasFees, nonce] = await Promise.all([ typeof isDeployedOverride === "boolean" ? isDeployedOverride : isContractDeployed(accountContract).then((isDeployed) => isDeployed || isAccountDeploying(accountContract)), encode(executeTx), resolvePromisedValue(executeTx.gas), getGasFees({ executeTx, bundlerOptions, chain, client, }), getAccountNonce({ accountContract, chain, client, entrypointAddress: overrides?.entrypointAddress, getNonceOverride: overrides?.getAccountNonce, }), ]); const { maxFeePerGas, maxPriorityFeePerGas } = gasFees; if (entrypointVersion === "v0.7") { return populateUserOp_v0_7({ bundlerOptions, factoryContract, accountContract, adminAddress, sponsorGas, overrides, isDeployed, nonce, callData, callGasLimit, maxFeePerGas, maxPriorityFeePerGas, waitForDeployment, }); } // default to v0.6 return populateUserOp_v0_6({ bundlerOptions, factoryContract, accountContract, adminAddress, sponsorGas, overrides, isDeployed, nonce, callData, callGasLimit, maxFeePerGas, maxPriorityFeePerGas, waitForDeployment, }); } async function getGasFees(args) { const { executeTx, bundlerOptions, chain, client } = args; let { maxFeePerGas, maxPriorityFeePerGas } = executeTx; const bundlerUrl = bundlerOptions?.bundlerUrl ?? getDefaultBundlerUrl(chain); if (isThirdwebUrl(bundlerUrl)) { // get gas prices from bundler const bundlerGasPrice = await getUserOpGasFees({ options: bundlerOptions, }); maxFeePerGas = bundlerGasPrice.maxFeePerGas; maxPriorityFeePerGas = bundlerGasPrice.maxPriorityFeePerGas; } else { // Check for expl