FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

View File

@@ -0,0 +1,27 @@
// TODO(v3): Remove this in favor of `ox/WebAuthnP256` entirely.
import * as PublicKey from 'ox/PublicKey'
import * as WebAuthnP256 from 'ox/WebAuthnP256'
import type { Hex } from '../../types/misc.js'
export type P256Credential = {
id: WebAuthnP256.P256Credential['id']
publicKey: Hex
raw: WebAuthnP256.P256Credential['raw']
}
export type CreateWebAuthnCredentialParameters =
WebAuthnP256.createCredential.Options
export type CreateWebAuthnCredentialReturnType = P256Credential
export async function createWebAuthnCredential(
parameters: CreateWebAuthnCredentialParameters,
): Promise<CreateWebAuthnCredentialReturnType> {
const credential = await WebAuthnP256.createCredential(parameters)
return {
id: credential.id,
publicKey: PublicKey.toHex(credential.publicKey, { includePrefix: false }),
raw: credential.raw,
}
}

View File

@@ -0,0 +1,879 @@
import type { Address, TypedData } from 'abitype'
import * as Signature from 'ox/Signature'
import type * as WebAuthnP256 from 'ox/WebAuthnP256'
import type { LocalAccount } from '../../../accounts/types.js'
import { readContract } from '../../../actions/public/readContract.js'
import { BaseError } from '../../../errors/base.js'
import type { Hash, Hex } from '../../../types/misc.js'
import type { TypedDataDefinition } from '../../../types/typedData.js'
import type { Assign, OneOf, Prettify } from '../../../types/utils.js'
import { decodeFunctionData } from '../../../utils/abi/decodeFunctionData.js'
import { encodeAbiParameters } from '../../../utils/abi/encodeAbiParameters.js'
import { encodeFunctionData } from '../../../utils/abi/encodeFunctionData.js'
import { encodePacked } from '../../../utils/abi/encodePacked.js'
import { pad } from '../../../utils/data/pad.js'
import { size } from '../../../utils/data/size.js'
import { stringToHex } from '../../../utils/encoding/toHex.js'
import { hashMessage } from '../../../utils/signature/hashMessage.js'
import { hashTypedData } from '../../../utils/signature/hashTypedData.js'
import { parseSignature } from '../../../utils/signature/parseSignature.js'
import { entryPoint06Abi } from '../../constants/abis.js'
import { entryPoint06Address } from '../../constants/address.js'
import type { UserOperation } from '../../types/userOperation.js'
import { getUserOperationHash } from '../../utils/userOperation/getUserOperationHash.js'
import { toSmartAccount } from '../toSmartAccount.js'
import type {
SmartAccount,
SmartAccountImplementation,
WebAuthnAccount,
} from '../types.js'
export type ToCoinbaseSmartAccountParameters = {
address?: Address | undefined
client: CoinbaseSmartAccountImplementation['client']
ownerIndex?: number | undefined
owners: readonly (Address | OneOf<LocalAccount | WebAuthnAccount>)[]
nonce?: bigint | undefined
version: '1.1' | '1'
}
export type ToCoinbaseSmartAccountReturnType = Prettify<
SmartAccount<CoinbaseSmartAccountImplementation>
>
export type CoinbaseSmartAccountImplementation = Assign<
SmartAccountImplementation<
typeof entryPoint06Abi,
'0.6',
{ abi: typeof abi; factory: { abi: typeof factoryAbi; address: Address } }
>,
{
decodeCalls: NonNullable<SmartAccountImplementation['decodeCalls']>
sign: NonNullable<SmartAccountImplementation['sign']>
}
>
const factoryAddress = {
'1.1': '0xba5ed110efdba3d005bfc882d75358acbbb85842',
'1': '0x0ba5ed0c6aa8c49038f819e587e2633c4a9f428a',
} as const
/**
* @description Create a Coinbase Smart Account.
*
* @param parameters - {@link ToCoinbaseSmartAccountParameters}
* @returns Coinbase Smart Account. {@link ToCoinbaseSmartAccountReturnType}
*
* @example
* import { toCoinbaseSmartAccount } from 'viem/account-abstraction'
* import { privateKeyToAccount } from 'viem/accounts'
* import { client } from './client.js'
*
* const account = toCoinbaseSmartAccount({
* client,
* owners: [privateKeyToAccount('0x...')],
* version: '1.1',
* })
*/
export async function toCoinbaseSmartAccount(
parameters: ToCoinbaseSmartAccountParameters,
): Promise<ToCoinbaseSmartAccountReturnType> {
const {
client,
ownerIndex = 0,
owners,
nonce = 0n,
version = '1',
} = parameters
let address = parameters.address
const entryPoint = {
abi: entryPoint06Abi,
address: entryPoint06Address,
version: '0.6',
} as const
const factory = {
abi: factoryAbi,
address: factoryAddress[version],
} as const
const owners_bytes = owners.map((owner) => {
if (typeof owner === 'string') return pad(owner)
if (owner.type === 'webAuthn') return owner.publicKey
if (owner.type === 'local') return pad(owner.address)
throw new BaseError('invalid owner type')
})
const owner = (() => {
const owner = owners[ownerIndex] ?? owners[0]
if (typeof owner === 'string')
return { address: owner, type: 'address' } as const
return owner
})()
return toSmartAccount({
client,
entryPoint,
extend: { abi, factory },
async decodeCalls(data) {
const result = decodeFunctionData({
abi,
data,
})
if (result.functionName === 'execute')
return [
{ to: result.args[0], value: result.args[1], data: result.args[2] },
]
if (result.functionName === 'executeBatch')
return result.args[0].map((arg) => ({
to: arg.target,
value: arg.value,
data: arg.data,
}))
throw new BaseError(`unable to decode calls for "${result.functionName}"`)
},
async encodeCalls(calls) {
if (calls.length === 1)
return encodeFunctionData({
abi,
functionName: 'execute',
args: [calls[0].to, calls[0].value ?? 0n, calls[0].data ?? '0x'],
})
return encodeFunctionData({
abi,
functionName: 'executeBatch',
args: [
calls.map((call) => ({
data: call.data ?? '0x',
target: call.to,
value: call.value ?? 0n,
})),
],
})
},
async getAddress() {
address ??= await readContract(client, {
...factory,
functionName: 'getAddress',
args: [owners_bytes, nonce],
})
return address
},
async getFactoryArgs() {
const factoryData = encodeFunctionData({
abi: factory.abi,
functionName: 'createAccount',
args: [owners_bytes, nonce],
})
return { factory: factory.address, factoryData }
},
async getStubSignature() {
if (owner.type === 'webAuthn')
return '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000'
return wrapSignature({
ownerIndex,
signature:
'0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c',
})
},
async sign(parameters) {
const address = await this.getAddress()
const typedData = toReplaySafeTypedData({
address,
chainId: client.chain!.id,
hash: parameters.hash,
})
if (owner.type === 'address') throw new Error('owner cannot sign')
const signature = await signTypedData({ owner, typedData })
return wrapSignature({
ownerIndex,
signature,
})
},
async signMessage(parameters) {
const { message } = parameters
const address = await this.getAddress()
const typedData = toReplaySafeTypedData({
address,
chainId: client.chain!.id,
hash: hashMessage(message),
})
if (owner.type === 'address') throw new Error('owner cannot sign')
const signature = await signTypedData({ owner, typedData })
return wrapSignature({
ownerIndex,
signature,
})
},
async signTypedData(parameters) {
const { domain, types, primaryType, message } =
parameters as TypedDataDefinition<TypedData, string>
const address = await this.getAddress()
const typedData = toReplaySafeTypedData({
address,
chainId: client.chain!.id,
hash: hashTypedData({
domain,
message,
primaryType,
types,
}),
})
if (owner.type === 'address') throw new Error('owner cannot sign')
const signature = await signTypedData({ owner, typedData })
return wrapSignature({
ownerIndex,
signature,
})
},
async signUserOperation(parameters) {
const { chainId = client.chain!.id, ...userOperation } = parameters
const address = await this.getAddress()
const hash = getUserOperationHash({
chainId,
entryPointAddress: entryPoint.address,
entryPointVersion: entryPoint.version,
userOperation: {
...(userOperation as unknown as UserOperation),
sender: address,
},
})
if (owner.type === 'address') throw new Error('owner cannot sign')
const signature = await sign({ hash, owner })
return wrapSignature({
ownerIndex,
signature,
})
},
userOperation: {
async estimateGas(userOperation) {
if (owner.type !== 'webAuthn') return
// Accounts with WebAuthn owner require a minimum verification gas limit of 800,000.
return {
verificationGasLimit: BigInt(
Math.max(Number(userOperation.verificationGasLimit ?? 0n), 800_000),
),
}
},
},
})
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Utilities
/////////////////////////////////////////////////////////////////////////////////////////////
/** @internal */
export async function signTypedData({
typedData,
owner,
}: {
typedData: TypedDataDefinition
owner: OneOf<LocalAccount | WebAuthnAccount>
}) {
if (owner.type === 'local' && owner.signTypedData)
return owner.signTypedData(typedData)
const hash = hashTypedData(typedData)
return sign({ hash, owner })
}
/** @internal */
export async function sign({
hash,
owner,
}: {
hash: Hash
owner: OneOf<LocalAccount | WebAuthnAccount>
}) {
// WebAuthn Account (Passkey)
if (owner.type === 'webAuthn') {
const { signature, webauthn } = await owner.sign({
hash,
})
return toWebAuthnSignature({ signature, webauthn })
}
if (owner.sign) return owner.sign({ hash })
throw new BaseError('`owner` does not support raw sign.')
}
/** @internal */
export function toReplaySafeTypedData({
address,
chainId,
hash,
}: {
address: Address
chainId: number
hash: Hash
}) {
return {
domain: {
chainId,
name: 'Coinbase Smart Wallet',
verifyingContract: address,
version: '1',
},
types: {
CoinbaseSmartWalletMessage: [
{
name: 'hash',
type: 'bytes32',
},
],
},
primaryType: 'CoinbaseSmartWalletMessage',
message: {
hash,
},
} as const
}
/** @internal */
export function toWebAuthnSignature({
webauthn,
signature,
}: {
webauthn: WebAuthnP256.SignMetadata
signature: Hex
}) {
const { r, s } = Signature.fromHex(signature)
return encodeAbiParameters(
[
{
components: [
{
name: 'authenticatorData',
type: 'bytes',
},
{ name: 'clientDataJSON', type: 'bytes' },
{ name: 'challengeIndex', type: 'uint256' },
{ name: 'typeIndex', type: 'uint256' },
{
name: 'r',
type: 'uint256',
},
{
name: 's',
type: 'uint256',
},
],
type: 'tuple',
},
],
[
{
authenticatorData: webauthn.authenticatorData,
clientDataJSON: stringToHex(webauthn.clientDataJSON),
challengeIndex: BigInt(webauthn.challengeIndex ?? 0n),
typeIndex: BigInt(webauthn.typeIndex ?? 0n),
r,
s,
},
],
)
}
/** @internal */
export function wrapSignature(parameters: {
ownerIndex?: number | undefined
signature: Hex
}) {
const { ownerIndex = 0 } = parameters
const signatureData = (() => {
if (size(parameters.signature) !== 65) return parameters.signature
const signature = parseSignature(parameters.signature)
return encodePacked(
['bytes32', 'bytes32', 'uint8'],
[signature.r, signature.s, signature.yParity === 0 ? 27 : 28],
)
})()
return encodeAbiParameters(
[
{
components: [
{
name: 'ownerIndex',
type: 'uint8',
},
{
name: 'signatureData',
type: 'bytes',
},
],
type: 'tuple',
},
],
[
{
ownerIndex,
signatureData,
},
],
)
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Constants
/////////////////////////////////////////////////////////////////////////////////////////////
const abi = [
{ inputs: [], stateMutability: 'nonpayable', type: 'constructor' },
{
inputs: [{ name: 'owner', type: 'bytes' }],
name: 'AlreadyOwner',
type: 'error',
},
{ inputs: [], name: 'Initialized', type: 'error' },
{
inputs: [{ name: 'owner', type: 'bytes' }],
name: 'InvalidEthereumAddressOwner',
type: 'error',
},
{
inputs: [{ name: 'key', type: 'uint256' }],
name: 'InvalidNonceKey',
type: 'error',
},
{
inputs: [{ name: 'owner', type: 'bytes' }],
name: 'InvalidOwnerBytesLength',
type: 'error',
},
{ inputs: [], name: 'LastOwner', type: 'error' },
{
inputs: [{ name: 'index', type: 'uint256' }],
name: 'NoOwnerAtIndex',
type: 'error',
},
{
inputs: [{ name: 'ownersRemaining', type: 'uint256' }],
name: 'NotLastOwner',
type: 'error',
},
{
inputs: [{ name: 'selector', type: 'bytes4' }],
name: 'SelectorNotAllowed',
type: 'error',
},
{ inputs: [], name: 'Unauthorized', type: 'error' },
{ inputs: [], name: 'UnauthorizedCallContext', type: 'error' },
{ inputs: [], name: 'UpgradeFailed', type: 'error' },
{
inputs: [
{ name: 'index', type: 'uint256' },
{ name: 'expectedOwner', type: 'bytes' },
{ name: 'actualOwner', type: 'bytes' },
],
name: 'WrongOwnerAtIndex',
type: 'error',
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'index',
type: 'uint256',
},
{ indexed: false, name: 'owner', type: 'bytes' },
],
name: 'AddOwner',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'index',
type: 'uint256',
},
{ indexed: false, name: 'owner', type: 'bytes' },
],
name: 'RemoveOwner',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'implementation',
type: 'address',
},
],
name: 'Upgraded',
type: 'event',
},
{ stateMutability: 'payable', type: 'fallback' },
{
inputs: [],
name: 'REPLAYABLE_NONCE_KEY',
outputs: [{ name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [{ name: 'owner', type: 'address' }],
name: 'addOwnerAddress',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{ name: 'x', type: 'bytes32' },
{ name: 'y', type: 'bytes32' },
],
name: 'addOwnerPublicKey',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [{ name: 'functionSelector', type: 'bytes4' }],
name: 'canSkipChainIdValidation',
outputs: [{ name: '', type: 'bool' }],
stateMutability: 'pure',
type: 'function',
},
{
inputs: [],
name: 'domainSeparator',
outputs: [{ name: '', type: 'bytes32' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'eip712Domain',
outputs: [
{ name: 'fields', type: 'bytes1' },
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
{ name: 'salt', type: 'bytes32' },
{ name: 'extensions', type: 'uint256[]' },
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'entryPoint',
outputs: [{ name: '', type: 'address' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{ name: 'target', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'data', type: 'bytes' },
],
name: 'execute',
outputs: [],
stateMutability: 'payable',
type: 'function',
},
{
inputs: [
{
components: [
{ name: 'target', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'data', type: 'bytes' },
],
name: 'calls',
type: 'tuple[]',
},
],
name: 'executeBatch',
outputs: [],
stateMutability: 'payable',
type: 'function',
},
{
inputs: [{ name: 'calls', type: 'bytes[]' }],
name: 'executeWithoutChainIdValidation',
outputs: [],
stateMutability: 'payable',
type: 'function',
},
{
inputs: [
{
components: [
{ name: 'sender', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'initCode', type: 'bytes' },
{ name: 'callData', type: 'bytes' },
{ name: 'callGasLimit', type: 'uint256' },
{
name: 'verificationGasLimit',
type: 'uint256',
},
{
name: 'preVerificationGas',
type: 'uint256',
},
{ name: 'maxFeePerGas', type: 'uint256' },
{
name: 'maxPriorityFeePerGas',
type: 'uint256',
},
{ name: 'paymasterAndData', type: 'bytes' },
{ name: 'signature', type: 'bytes' },
],
name: 'userOp',
type: 'tuple',
},
],
name: 'getUserOpHashWithoutChainId',
outputs: [{ name: '', type: 'bytes32' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'implementation',
outputs: [{ name: '$', type: 'address' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [{ name: 'owners', type: 'bytes[]' }],
name: 'initialize',
outputs: [],
stateMutability: 'payable',
type: 'function',
},
{
inputs: [{ name: 'account', type: 'address' }],
name: 'isOwnerAddress',
outputs: [{ name: '', type: 'bool' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [{ name: 'account', type: 'bytes' }],
name: 'isOwnerBytes',
outputs: [{ name: '', type: 'bool' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{ name: 'x', type: 'bytes32' },
{ name: 'y', type: 'bytes32' },
],
name: 'isOwnerPublicKey',
outputs: [{ name: '', type: 'bool' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{ name: 'hash', type: 'bytes32' },
{ name: 'signature', type: 'bytes' },
],
name: 'isValidSignature',
outputs: [{ name: 'result', type: 'bytes4' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'nextOwnerIndex',
outputs: [{ name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [{ name: 'index', type: 'uint256' }],
name: 'ownerAtIndex',
outputs: [{ name: '', type: 'bytes' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'ownerCount',
outputs: [{ name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'proxiableUUID',
outputs: [{ name: '', type: 'bytes32' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{ name: 'index', type: 'uint256' },
{ name: 'owner', type: 'bytes' },
],
name: 'removeLastOwner',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{ name: 'index', type: 'uint256' },
{ name: 'owner', type: 'bytes' },
],
name: 'removeOwnerAtIndex',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'removedOwnersCount',
outputs: [{ name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [{ name: 'hash', type: 'bytes32' }],
name: 'replaySafeHash',
outputs: [{ name: '', type: 'bytes32' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{ name: 'newImplementation', type: 'address' },
{ name: 'data', type: 'bytes' },
],
name: 'upgradeToAndCall',
outputs: [],
stateMutability: 'payable',
type: 'function',
},
{
inputs: [
{
components: [
{ name: 'sender', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'initCode', type: 'bytes' },
{ name: 'callData', type: 'bytes' },
{ name: 'callGasLimit', type: 'uint256' },
{
name: 'verificationGasLimit',
type: 'uint256',
},
{
name: 'preVerificationGas',
type: 'uint256',
},
{ name: 'maxFeePerGas', type: 'uint256' },
{
name: 'maxPriorityFeePerGas',
type: 'uint256',
},
{ name: 'paymasterAndData', type: 'bytes' },
{ name: 'signature', type: 'bytes' },
],
name: 'userOp',
type: 'tuple',
},
{ name: 'userOpHash', type: 'bytes32' },
{ name: 'missingAccountFunds', type: 'uint256' },
],
name: 'validateUserOp',
outputs: [{ name: 'validationData', type: 'uint256' }],
stateMutability: 'nonpayable',
type: 'function',
},
{ stateMutability: 'payable', type: 'receive' },
] as const
const factoryAbi = [
{
inputs: [{ name: 'implementation_', type: 'address' }],
stateMutability: 'payable',
type: 'constructor',
},
{ inputs: [], name: 'OwnerRequired', type: 'error' },
{
inputs: [
{ name: 'owners', type: 'bytes[]' },
{ name: 'nonce', type: 'uint256' },
],
name: 'createAccount',
outputs: [
{
name: 'account',
type: 'address',
},
],
stateMutability: 'payable',
type: 'function',
},
{
inputs: [
{ name: 'owners', type: 'bytes[]' },
{ name: 'nonce', type: 'uint256' },
],
name: 'getAddress',
outputs: [{ name: '', type: 'address' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'implementation',
outputs: [{ name: '', type: 'address' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'initCodeHash',
outputs: [{ name: '', type: 'bytes32' }],
stateMutability: 'view',
type: 'function',
},
] as const

View File

@@ -0,0 +1,363 @@
import type { Abi, Address, TypedData } from 'abitype'
import type { PrivateKeyAccount } from '../../../accounts/types.js'
import { BaseError } from '../../../errors/base.js'
import type { TypedDataDefinition } from '../../../types/typedData.js'
import type { Prettify } from '../../../types/utils.js'
import { decodeFunctionData } from '../../../utils/abi/decodeFunctionData.js'
import { encodeFunctionData } from '../../../utils/abi/encodeFunctionData.js'
import { entryPoint08Abi, entryPoint09Abi } from '../../constants/abis.js'
import {
entryPoint08Address,
entryPoint09Address,
} from '../../constants/address.js'
import type { EntryPointVersion } from '../../types/entryPointVersion.js'
import { getUserOperationTypedData } from '../../utils/userOperation/getUserOperationTypedData.js'
import { toSmartAccount } from '../toSmartAccount.js'
import type { SmartAccount, SmartAccountImplementation } from '../types.js'
type EntryPoint =
| '0.8'
| '0.9'
| {
abi: Abi
address: Address
version: EntryPointVersion
}
export type ToSimple7702SmartAccountParameters<
entryPoint extends EntryPoint = '0.8',
> = {
client: Simple7702SmartAccountImplementation['client']
entryPoint?: entryPoint | EntryPoint | undefined
implementation?: Address | undefined
getNonce?: SmartAccountImplementation['getNonce'] | undefined
owner: PrivateKeyAccount
}
export type ToSimple7702SmartAccountReturnType<
entryPoint extends EntryPoint = '0.8',
> = Prettify<SmartAccount<Simple7702SmartAccountImplementation<entryPoint>>>
export type Simple7702SmartAccountImplementation<
entryPoint extends EntryPoint = '0.8',
> = SmartAccountImplementation<
entryPoint extends { abi: infer abi }
? abi
: entryPoint extends '0.9'
? typeof entryPoint09Abi
: typeof entryPoint08Abi,
entryPoint extends string
? entryPoint
: entryPoint extends { version: infer version }
? version
: EntryPointVersion,
{
abi: entryPoint extends { abi: infer abi }
? abi
: entryPoint extends '0.9'
? typeof entryPoint09Abi
: typeof entryPoint08Abi
owner: PrivateKeyAccount
},
true
>
/**
* @description Create a Simple7702 Smart Account based off [eth-infinitism's `Simple7702Account.sol`](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/accounts/Simple7702Account.sol).
*
* @param parameters - {@link ToSimple7702SmartAccountParameters}
* @returns Simple7702 Smart Account. {@link ToSimple7702SmartAccountReturnType}
*
* @example
* import { toSimple7702SmartAccount } from 'viem/account-abstraction'
* import { client } from './client.js'
*
* const implementation = toSimple7702SmartAccount({
* client,
* owner: '0x...',
* })
*/
export async function toSimple7702SmartAccount<
entryPoint extends EntryPoint = '0.8',
>(
parameters: ToSimple7702SmartAccountParameters<entryPoint>,
): Promise<ToSimple7702SmartAccountReturnType<entryPoint>> {
const { client, getNonce, owner } = parameters
const entryPoint = (() => {
if (parameters.entryPoint === '0.9')
return {
abi: entryPoint09Abi,
address: entryPoint09Address,
version: '0.9',
} as const
if (typeof parameters.entryPoint === 'object') return parameters.entryPoint
return {
abi: entryPoint08Abi,
address: entryPoint08Address,
version: '0.8',
} as const
})()
const implementation = (() => {
if (parameters.implementation) return parameters.implementation
if (parameters.entryPoint === '0.9')
return '0xa46cc63eBF4Bd77888AA327837d20b23A63a56B5'
return '0xe6Cae83BdE06E4c305530e199D7217f42808555B'
})()
return toSmartAccount({
authorization: { account: owner, address: implementation },
abi,
client,
extend: { abi, owner }, // not removing abi from here as this will be a breaking change
entryPoint,
getNonce,
async decodeCalls(data) {
const result = decodeFunctionData({
abi,
data,
})
if (result.functionName === 'execute')
return [
{ to: result.args[0], value: result.args[1], data: result.args[2] },
]
if (result.functionName === 'executeBatch')
return result.args[0].map((arg) => ({
to: arg.target,
value: arg.value,
data: arg.data,
}))
throw new BaseError(`unable to decode calls for "${result.functionName}"`)
},
async encodeCalls(calls) {
if (calls.length === 1)
return encodeFunctionData({
abi,
functionName: 'execute',
args: [calls[0].to, calls[0].value ?? 0n, calls[0].data ?? '0x'],
})
return encodeFunctionData({
abi,
functionName: 'executeBatch',
args: [
calls.map((call) => ({
data: call.data ?? '0x',
target: call.to,
value: call.value ?? 0n,
})),
],
})
},
async getAddress() {
return owner.address
},
async getFactoryArgs() {
return { factory: '0x7702', factoryData: '0x' }
},
async getStubSignature() {
return '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
},
async signMessage(parameters) {
const { message } = parameters
return await owner.signMessage({ message })
},
async signTypedData(parameters) {
const { domain, types, primaryType, message } =
parameters as TypedDataDefinition<TypedData, string>
return await owner.signTypedData({
domain,
message,
primaryType,
types,
})
},
async signUserOperation(parameters) {
const { chainId = client.chain!.id, ...userOperation } = parameters
const address = await this.getAddress()
const typedData = getUserOperationTypedData({
chainId,
entryPointAddress: entryPoint.address,
userOperation: {
...userOperation,
sender: address,
},
})
return await owner.signTypedData(typedData)
},
}) as unknown as ToSimple7702SmartAccountReturnType<entryPoint>
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Constants
const abi = [
{ inputs: [], name: 'ECDSAInvalidSignature', type: 'error' },
{
inputs: [{ internalType: 'uint256', name: 'length', type: 'uint256' }],
name: 'ECDSAInvalidSignatureLength',
type: 'error',
},
{
inputs: [{ internalType: 'bytes32', name: 's', type: 'bytes32' }],
name: 'ECDSAInvalidSignatureS',
type: 'error',
},
{
inputs: [
{ internalType: 'uint256', name: 'index', type: 'uint256' },
{ internalType: 'bytes', name: 'error', type: 'bytes' },
],
name: 'ExecuteError',
type: 'error',
},
{ stateMutability: 'payable', type: 'fallback' },
{
inputs: [],
name: 'entryPoint',
outputs: [
{ internalType: 'contract IEntryPoint', name: '', type: 'address' },
],
stateMutability: 'pure',
type: 'function',
},
{
inputs: [
{ internalType: 'address', name: 'target', type: 'address' },
{ internalType: 'uint256', name: 'value', type: 'uint256' },
{ internalType: 'bytes', name: 'data', type: 'bytes' },
],
name: 'execute',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
components: [
{ internalType: 'address', name: 'target', type: 'address' },
{ internalType: 'uint256', name: 'value', type: 'uint256' },
{ internalType: 'bytes', name: 'data', type: 'bytes' },
],
internalType: 'struct BaseAccount.Call[]',
name: 'calls',
type: 'tuple[]',
},
],
name: 'executeBatch',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'getNonce',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{ internalType: 'bytes32', name: 'hash', type: 'bytes32' },
{ internalType: 'bytes', name: 'signature', type: 'bytes' },
],
name: 'isValidSignature',
outputs: [{ internalType: 'bytes4', name: 'magicValue', type: 'bytes4' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'uint256[]', name: '', type: 'uint256[]' },
{ internalType: 'uint256[]', name: '', type: 'uint256[]' },
{ internalType: 'bytes', name: '', type: 'bytes' },
],
name: 'onERC1155BatchReceived',
outputs: [{ internalType: 'bytes4', name: '', type: 'bytes4' }],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'uint256', name: '', type: 'uint256' },
{ internalType: 'uint256', name: '', type: 'uint256' },
{ internalType: 'bytes', name: '', type: 'bytes' },
],
name: 'onERC1155Received',
outputs: [{ internalType: 'bytes4', name: '', type: 'bytes4' }],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'uint256', name: '', type: 'uint256' },
{ internalType: 'bytes', name: '', type: 'bytes' },
],
name: 'onERC721Received',
outputs: [{ internalType: 'bytes4', name: '', type: 'bytes4' }],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [{ internalType: 'bytes4', name: 'id', type: 'bytes4' }],
name: 'supportsInterface',
outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
stateMutability: 'pure',
type: 'function',
},
{
inputs: [
{
components: [
{ internalType: 'address', name: 'sender', type: 'address' },
{ internalType: 'uint256', name: 'nonce', type: 'uint256' },
{ internalType: 'bytes', name: 'initCode', type: 'bytes' },
{ internalType: 'bytes', name: 'callData', type: 'bytes' },
{
internalType: 'bytes32',
name: 'accountGasLimits',
type: 'bytes32',
},
{
internalType: 'uint256',
name: 'preVerificationGas',
type: 'uint256',
},
{ internalType: 'bytes32', name: 'gasFees', type: 'bytes32' },
{ internalType: 'bytes', name: 'paymasterAndData', type: 'bytes' },
{ internalType: 'bytes', name: 'signature', type: 'bytes' },
],
internalType: 'struct PackedUserOperation',
name: 'userOp',
type: 'tuple',
},
{ internalType: 'bytes32', name: 'userOpHash', type: 'bytes32' },
{ internalType: 'uint256', name: 'missingAccountFunds', type: 'uint256' },
],
name: 'validateUserOp',
outputs: [
{ internalType: 'uint256', name: 'validationData', type: 'uint256' },
],
stateMutability: 'nonpayable',
type: 'function',
},
{ stateMutability: 'payable', type: 'receive' },
] as const

View File

@@ -0,0 +1,796 @@
import type { Abi, Address, TypedData } from 'abitype'
import { parseAccount } from '../../../accounts/utils/parseAccount.js'
import { readContract } from '../../../actions/public/readContract.js'
import { signMessage as signMessage_ } from '../../../actions/wallet/signMessage.js'
import { BaseError } from '../../../errors/base.js'
import { signMessage } from '../../../experimental/erc7739/actions/signMessage.js'
import { signTypedData } from '../../../experimental/erc7739/actions/signTypedData.js'
import type { Account } from '../../../types/account.js'
import type { Hex } from '../../../types/misc.js'
import type { TypedDataDefinition } from '../../../types/typedData.js'
import type { Prettify } from '../../../types/utils.js'
import { decodeFunctionData } from '../../../utils/abi/decodeFunctionData.js'
import { encodeFunctionData } from '../../../utils/abi/encodeFunctionData.js'
import { pad } from '../../../utils/data/pad.js'
import { getAction } from '../../../utils/getAction.js'
import { entryPoint07Abi } from '../../constants/abis.js'
import { entryPoint07Address } from '../../constants/address.js'
import type { EntryPointVersion } from '../../types/entryPointVersion.js'
import { getUserOperationHash } from '../../utils/userOperation/getUserOperationHash.js'
import { toSmartAccount } from '../toSmartAccount.js'
import type { SmartAccount, SmartAccountImplementation } from '../types.js'
export type ToSoladySmartAccountParameters<
entryPointAbi extends Abi = Abi,
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> = {
address?: Address | undefined
client: SoladySmartAccountImplementation['client']
entryPoint?:
| {
abi: entryPointAbi
address: Address
version: entryPointVersion | EntryPointVersion
}
| undefined
factoryAddress?: Address | undefined
getNonce?: SmartAccountImplementation['getNonce'] | undefined
owner: Address | Account
salt?: Hex | undefined
}
export type ToSoladySmartAccountReturnType<
entryPointAbi extends Abi = Abi,
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> = Prettify<
SmartAccount<
SoladySmartAccountImplementation<entryPointAbi, entryPointVersion>
>
>
export type SoladySmartAccountImplementation<
entryPointAbi extends Abi = Abi,
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> = SmartAccountImplementation<
entryPointAbi,
entryPointVersion,
{ abi: typeof abi; factory: { abi: typeof factoryAbi; address: Address } }
>
/**
* @description Create a Solady Smart Account based off [Solady's `ERC4337.sol`](https://github.com/Vectorized/solady/blob/main/src/accounts/ERC4337.sol).
*
* @param parameters - {@link ToSoladySmartAccountParameters}
* @returns Solady Smart Account. {@link ToSoladySmartAccountReturnType}
*
* @example
* import { toSoladySmartAccount } from 'viem/account-abstraction'
* import { client } from './client.js'
*
* const implementation = toSoladySmartAccount({
* client,
* owner: '0x...',
* })
*/
export async function toSoladySmartAccount<
entryPointAbi extends Abi = typeof entryPoint07Abi,
entryPointVersion extends EntryPointVersion = '0.7',
>(
parameters: ToSoladySmartAccountParameters<entryPointAbi, entryPointVersion>,
): Promise<ToSoladySmartAccountReturnType<entryPointAbi, entryPointVersion>> {
const {
address,
client,
entryPoint: entryPoint_ = {
abi: entryPoint07Abi,
address: entryPoint07Address,
version: '0.7',
},
factoryAddress = '0x5d82735936c6Cd5DE57cC3c1A799f6B2E6F933Df',
getNonce,
salt = '0x0',
} = parameters
const entryPoint = {
abi: entryPoint_.abi as entryPointAbi,
address: entryPoint_.address,
version: entryPoint_.version as entryPointVersion,
} as const
const factory = {
abi: factoryAbi,
address: factoryAddress,
} as const
const owner = parseAccount(parameters.owner)
return toSmartAccount({
client,
entryPoint,
getNonce,
extend: { abi, factory },
async decodeCalls(data) {
const result = decodeFunctionData({
abi,
data,
})
if (result.functionName === 'execute')
return [
{ to: result.args[0], value: result.args[1], data: result.args[2] },
]
if (result.functionName === 'executeBatch')
return result.args[0].map((arg) => ({
to: arg.target,
value: arg.value,
data: arg.data,
}))
throw new BaseError(`unable to decode calls for "${result.functionName}"`)
},
async encodeCalls(calls) {
if (calls.length === 1)
return encodeFunctionData({
abi,
functionName: 'execute',
args: [calls[0].to, calls[0].value ?? 0n, calls[0].data ?? '0x'],
})
return encodeFunctionData({
abi,
functionName: 'executeBatch',
args: [
calls.map((call) => ({
data: call.data ?? '0x',
target: call.to,
value: call.value ?? 0n,
})),
],
})
},
async getAddress() {
if (address) return address
return await readContract(client, {
...factory,
functionName: 'getAddress',
args: [pad(salt)],
})
},
async getFactoryArgs() {
const factoryData = encodeFunctionData({
abi: factory.abi,
functionName: 'createAccount',
args: [owner.address, pad(salt)],
})
return { factory: factory.address, factoryData }
},
async getStubSignature() {
return '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
},
async signMessage(parameters) {
const { message } = parameters
const [address, { factory, factoryData }] = await Promise.all([
this.getAddress(),
this.getFactoryArgs(),
])
return await signMessage(client, {
account: owner,
factory,
factoryData,
message,
verifier: address,
})
},
async signTypedData(parameters) {
const { domain, types, primaryType, message } =
parameters as TypedDataDefinition<TypedData, string>
const [address, { factory, factoryData }] = await Promise.all([
this.getAddress(),
this.getFactoryArgs(),
])
return await signTypedData(client, {
account: owner,
domain,
message,
factory,
factoryData,
primaryType,
types,
verifier: address,
})
},
async signUserOperation(parameters) {
const { chainId = client.chain!.id, ...userOperation } = parameters
const address = await this.getAddress()
const userOpHash = getUserOperationHash({
chainId,
entryPointAddress: entryPoint.address,
entryPointVersion: entryPoint.version,
userOperation: {
...(userOperation as any),
sender: address,
},
})
const signature = await getAction(
client,
signMessage_,
'signMessage',
)({
account: owner,
message: {
raw: userOpHash,
},
})
return signature
},
})
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Constants
const abi = [
{
type: 'fallback',
stateMutability: 'payable',
},
{
type: 'receive',
stateMutability: 'payable',
},
{
type: 'function',
name: 'addDeposit',
inputs: [],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'cancelOwnershipHandover',
inputs: [],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'completeOwnershipHandover',
inputs: [
{
name: 'pendingOwner',
type: 'address',
},
],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'delegateExecute',
inputs: [
{
name: 'delegate',
type: 'address',
},
{
name: 'data',
type: 'bytes',
},
],
outputs: [
{
name: 'result',
type: 'bytes',
},
],
stateMutability: 'payable',
},
{
type: 'function',
name: 'eip712Domain',
inputs: [],
outputs: [
{
name: 'name',
type: 'string',
},
{
name: 'version',
type: 'string',
},
{
name: 'chainId',
type: 'uint256',
},
{
name: 'verifyingContract',
type: 'address',
},
{
name: 'salt',
type: 'bytes32',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'entryPoint',
inputs: [],
outputs: [
{
name: '',
type: 'address',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'execute',
inputs: [
{
name: 'target',
type: 'address',
},
{
name: 'value',
type: 'uint256',
},
{
name: 'data',
type: 'bytes',
},
],
outputs: [
{
name: 'result',
type: 'bytes',
},
],
stateMutability: 'payable',
},
{
type: 'function',
name: 'executeBatch',
inputs: [
{
name: 'calls',
type: 'tuple[]',
components: [
{
name: 'target',
type: 'address',
},
{
name: 'value',
type: 'uint256',
},
{
name: 'data',
type: 'bytes',
},
],
},
],
outputs: [
{
name: 'results',
type: 'bytes[]',
},
],
stateMutability: 'payable',
},
{
type: 'function',
name: 'getDeposit',
inputs: [],
outputs: [
{
name: 'result',
type: 'uint256',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'initialize',
inputs: [
{
name: 'newOwner',
type: 'address',
},
],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'isValidSignature',
inputs: [
{
name: 'hash',
type: 'bytes32',
},
{
name: 'signature',
type: 'bytes',
},
],
outputs: [
{
name: 'result',
type: 'bytes4',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'owner',
inputs: [],
outputs: [
{
name: 'result',
type: 'address',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'ownershipHandoverExpiresAt',
inputs: [
{
name: 'pendingOwner',
type: 'address',
},
],
outputs: [
{
name: 'result',
type: 'uint256',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'proxiableUUID',
inputs: [],
outputs: [
{
name: '',
type: 'bytes32',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'renounceOwnership',
inputs: [],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'requestOwnershipHandover',
inputs: [],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'storageLoad',
inputs: [
{
name: 'storageSlot',
type: 'bytes32',
},
],
outputs: [
{
name: 'result',
type: 'bytes32',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'storageStore',
inputs: [
{
name: 'storageSlot',
type: 'bytes32',
},
{
name: 'storageValue',
type: 'bytes32',
},
],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'transferOwnership',
inputs: [
{
name: 'newOwner',
type: 'address',
},
],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'upgradeToAndCall',
inputs: [
{
name: 'newImplementation',
type: 'address',
},
{
name: 'data',
type: 'bytes',
},
],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'validateUserOp',
inputs: [
{
name: 'userOp',
type: 'tuple',
components: [
{
name: 'sender',
type: 'address',
},
{
name: 'nonce',
type: 'uint256',
},
{
name: 'initCode',
type: 'bytes',
},
{
name: 'callData',
type: 'bytes',
},
{
name: 'accountGasLimits',
type: 'bytes32',
},
{
name: 'preVerificationGas',
type: 'uint256',
},
{
name: 'gasFees',
type: 'bytes32',
},
{
name: 'paymasterAndData',
type: 'bytes',
},
{
name: 'signature',
type: 'bytes',
},
],
},
{
name: 'userOpHash',
type: 'bytes32',
},
{
name: 'missingAccountFunds',
type: 'uint256',
},
],
outputs: [
{
name: 'validationData',
type: 'uint256',
},
],
stateMutability: 'payable',
},
{
type: 'function',
name: 'withdrawDepositTo',
inputs: [
{
name: 'to',
type: 'address',
},
{
name: 'amount',
type: 'uint256',
},
],
outputs: [],
stateMutability: 'payable',
},
{
type: 'event',
name: 'OwnershipHandoverCanceled',
inputs: [
{
name: 'pendingOwner',
type: 'address',
indexed: true,
},
],
anonymous: false,
},
{
type: 'event',
name: 'OwnershipHandoverRequested',
inputs: [
{
name: 'pendingOwner',
type: 'address',
indexed: true,
},
],
anonymous: false,
},
{
type: 'event',
name: 'OwnershipTransferred',
inputs: [
{
name: 'oldOwner',
type: 'address',
indexed: true,
},
{
name: 'newOwner',
type: 'address',
indexed: true,
},
],
anonymous: false,
},
{
type: 'event',
name: 'Upgraded',
inputs: [
{
name: 'implementation',
type: 'address',
indexed: true,
},
],
anonymous: false,
},
{
type: 'error',
name: 'AlreadyInitialized',
inputs: [],
},
{
type: 'error',
name: 'FnSelectorNotRecognized',
inputs: [],
},
{
type: 'error',
name: 'NewOwnerIsZeroAddress',
inputs: [],
},
{
type: 'error',
name: 'NoHandoverRequest',
inputs: [],
},
{
type: 'error',
name: 'Unauthorized',
inputs: [],
},
{
type: 'error',
name: 'UnauthorizedCallContext',
inputs: [],
},
{
type: 'error',
name: 'UpgradeFailed',
inputs: [],
},
] as const
const factoryAbi = [
{
type: 'constructor',
inputs: [
{
name: 'erc4337',
type: 'address',
},
],
stateMutability: 'nonpayable',
},
{
type: 'function',
name: 'createAccount',
inputs: [
{
name: 'owner',
type: 'address',
},
{
name: 'salt',
type: 'bytes32',
},
],
outputs: [
{
name: '',
type: 'address',
},
],
stateMutability: 'payable',
},
{
type: 'function',
name: 'getAddress',
inputs: [
{
name: 'salt',
type: 'bytes32',
},
],
outputs: [
{
name: '',
type: 'address',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'implementation',
inputs: [],
outputs: [
{
name: '',
type: 'address',
},
],
stateMutability: 'view',
},
{
type: 'function',
name: 'initCodeHash',
inputs: [],
outputs: [
{
name: '',
type: 'bytes32',
},
],
stateMutability: 'view',
},
] as const

View File

@@ -0,0 +1,141 @@
import { type Abi, parseAbi } from 'abitype'
import { getCode } from '../../actions/public/getCode.js'
import { readContract } from '../../actions/public/readContract.js'
import type { Prettify } from '../../types/utils.js'
import { getAction } from '../../utils/getAction.js'
import { createNonceManager } from '../../utils/nonceManager.js'
import { serializeErc6492Signature } from '../../utils/signature/serializeErc6492Signature.js'
import type { EntryPointVersion } from '../types/entryPointVersion.js'
import type { SmartAccount, SmartAccountImplementation } from './types.js'
export type ToSmartAccountParameters<
entryPointAbi extends Abi | readonly unknown[] = Abi,
entryPointVersion extends EntryPointVersion = EntryPointVersion,
extend extends object = object,
> = SmartAccountImplementation<entryPointAbi, entryPointVersion, extend>
export type ToSmartAccountReturnType<
implementation extends
SmartAccountImplementation = SmartAccountImplementation,
> = Prettify<SmartAccount<implementation>>
/**
* @description Creates a Smart Account with a provided account implementation.
*
* @param parameters - {@link ToSmartAccountParameters}
* @returns A Smart Account. {@link ToSmartAccountReturnType}
*/
export async function toSmartAccount<
implementation extends SmartAccountImplementation,
>(
implementation: implementation,
): Promise<ToSmartAccountReturnType<implementation>> {
const {
extend,
nonceKeyManager = createNonceManager({
source: {
get() {
return Date.now()
},
set() {},
},
}),
...rest
} = implementation
let deployed = false
const address = await implementation.getAddress()
return {
...extend,
...rest,
address,
async getFactoryArgs() {
if ('isDeployed' in this && (await this.isDeployed()))
return { factory: undefined, factoryData: undefined }
return implementation.getFactoryArgs()
},
async getNonce(parameters) {
const key =
parameters?.key ??
BigInt(
await nonceKeyManager.consume({
address,
chainId: implementation.client.chain!.id!,
client: implementation.client,
}),
)
if (implementation.getNonce)
return await implementation.getNonce({ ...parameters, key })
const nonce = await readContract(implementation.client, {
abi: parseAbi([
'function getNonce(address, uint192) pure returns (uint256)',
]),
address: implementation.entryPoint.address,
functionName: 'getNonce',
args: [address, key],
})
return nonce
},
async isDeployed() {
if (deployed) return true
const code = await getAction(
implementation.client,
getCode,
'getCode',
)({
address,
})
deployed = Boolean(code)
return deployed
},
...(implementation.sign
? {
async sign(parameters) {
const [{ factory, factoryData }, signature] = await Promise.all([
this.getFactoryArgs(),
implementation.sign!(parameters),
])
if (factory && factoryData)
return serializeErc6492Signature({
address: factory,
data: factoryData,
signature,
})
return signature
},
}
: {}),
async signMessage(parameters) {
const [{ factory, factoryData }, signature] = await Promise.all([
this.getFactoryArgs(),
implementation.signMessage(parameters),
])
if (factory && factoryData && factory !== '0x7702')
return serializeErc6492Signature({
address: factory,
data: factoryData,
signature,
})
return signature
},
async signTypedData(parameters) {
const [{ factory, factoryData }, signature] = await Promise.all([
this.getFactoryArgs(),
implementation.signTypedData(parameters),
])
if (factory && factoryData && factory !== '0x7702')
return serializeErc6492Signature({
address: factory,
data: factoryData,
signature,
})
return signature
},
type: 'smart',
} as ToSmartAccountReturnType<implementation>
}

View File

@@ -0,0 +1,69 @@
import * as Signature from 'ox/Signature'
import * as WebAuthnP256 from 'ox/WebAuthnP256'
import type { ErrorType } from '../../errors/utils.js'
import { hashMessage } from '../../utils/signature/hashMessage.js'
import { hashTypedData } from '../../utils/signature/hashTypedData.js'
import type { P256Credential } from './createWebAuthnCredential.js'
import type { WebAuthnAccount } from './types.js'
export type ToWebAuthnAccountParameters = {
/**
* The WebAuthn P256 credential to use.
*/
credential: {
id: P256Credential['id']
publicKey: P256Credential['publicKey']
}
/**
* Credential request function. Useful for environments that do not support
* the WebAuthn API natively (i.e. React Native or testing environments).
*
* @default window.navigator.credentials.get
*/
getFn?: WebAuthnP256.sign.Options['getFn'] | undefined
/**
* The relying party identifier to use.
*/
rpId?: WebAuthnP256.sign.Options['rpId'] | undefined
}
export type ToWebAuthnAccountReturnType = WebAuthnAccount
export type ToWebAuthnAccountErrorType = ErrorType
/**
* @description Creates an Account from a WebAuthn Credential.
*
* @returns A WebAuthn Account.
*/
export function toWebAuthnAccount(
parameters: ToWebAuthnAccountParameters,
): WebAuthnAccount {
const { getFn, rpId } = parameters
const { id, publicKey } = parameters.credential
return {
id,
publicKey,
async sign({ hash }) {
const { metadata, raw, signature } = await WebAuthnP256.sign({
credentialId: id,
getFn,
challenge: hash,
rpId,
})
return {
signature: Signature.toHex(signature),
raw,
webauthn: metadata,
}
},
async signMessage({ message }) {
return this.sign({ hash: hashMessage(message) })
},
async signTypedData(parameters) {
return this.sign({ hash: hashTypedData(parameters) })
},
type: 'webAuthn',
}
}

269
node_modules/viem/account-abstraction/accounts/types.ts generated vendored Normal file
View File

@@ -0,0 +1,269 @@
import type { Abi, Address, TypedData } from 'abitype'
import type * as WebAuthnP256 from 'ox/WebAuthnP256'
import type {
JsonRpcAccount,
LocalAccount,
PrivateKeyAccount,
} from '../../accounts/types.js'
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { Chain } from '../../types/chain.js'
import type { Hash, Hex, SignableMessage } from '../../types/misc.js'
import type { TypedDataDefinition } from '../../types/typedData.js'
import type { Assign, ExactPartial, UnionPartialBy } from '../../types/utils.js'
import type { NonceManager } from '../../utils/nonceManager.js'
import type { EntryPointVersion } from '../types/entryPointVersion.js'
import type {
EstimateUserOperationGasReturnType,
UserOperation,
UserOperationRequest,
} from '../types/userOperation.js'
type Call = {
to: Hex
data?: Hex | undefined
value?: bigint | undefined
}
export type SmartAccountImplementation<
entryPointAbi extends Abi | readonly unknown[] = Abi,
entryPointVersion extends EntryPointVersion = EntryPointVersion,
extend extends object = object,
eip7702 extends boolean = boolean,
> = {
/** Client used to retrieve Smart Account data, and perform signing (if owner is a JSON-RPC Account). */
client: Client<
Transport,
Chain | undefined,
JsonRpcAccount | LocalAccount | undefined
>
/** Compatible EntryPoint of the Smart Account. */
entryPoint: {
/** Compatible EntryPoint ABI. */
abi: entryPointAbi
/** Compatible EntryPoint address. */
address: Address
/** Compatible EntryPoint version. */
version: entryPointVersion
}
/** Extend the Smart Account with custom properties. */
extend?: extend | undefined
/**
* Retrieves the Smart Account's address.
*
* @example
* ```ts
* const address = await account.getAddress()
* // '0x...'
* ```
*/
getAddress: () => Promise<Address>
/**
* Decodes calldata into structured calls.
*
* @example
* ```ts
* const calls = await account.decodeCalls('0x...')
* // [{ to: '0x...', data: '0x...', value: 100n }, ...]
* ```
*/
decodeCalls?: ((data: Hex) => Promise<readonly Call[]>) | undefined
/**
* Encodes the calls into calldata for executing a User Operation.
*
* @example
* ```ts
* const callData = await account.encodeCalls([
* { to: '0x...', data: '0x...' },
* { to: '0x...', data: '0x...', value: 100n },
* ])
* // '0x...'
* ```
*/
encodeCalls: (calls: readonly Call[]) => Promise<Hex>
/**
* Retrieves the calldata for factory call to deploy a Smart Account.
* If the Smart Account has already been deployed, this will return undefined values.
*
* @example Counterfactual account
* ```ts
* const { factory, factoryData } = await account.getFactoryArgs()
* // { factory: '0x...', factoryData: '0x...' }
* ```
*
* @example Deployed account
* ```ts
* const { factory, factoryData } = await account.getFactoryArgs()
* // { factory: undefined, factoryData: undefined }
* ```
*/
getFactoryArgs: () => Promise<{
factory?: Address | undefined
factoryData?: Hex | undefined
}>
/**
* Retrieves the nonce of the Account.
*
* @example
* ```ts
* const nonce = await account.getNonce()
* // 1n
* ```
*/
getNonce?:
| ((
parameters?: { key?: bigint | undefined } | undefined,
) => Promise<bigint>)
| undefined
/**
* Retrieves the User Operation "stub" signature for gas estimation.
*
* ```ts
* const signature = await account.getStubSignature()
* // '0x...'
* ```
*/
getStubSignature: (
parameters?: UserOperationRequest | undefined,
) => Promise<Hex>
/** Custom nonce key manager. */
nonceKeyManager?: NonceManager | undefined
/**
* Signs a hash via the Smart Account's owner.
*
* @example
* ```ts
* const signature = await account.sign({
* hash: '0x...'
* })
* // '0x...'
* ```
*/
sign?: ((parameters: { hash: Hash }) => Promise<Hex>) | undefined
/**
* Signs a [EIP-191 Personal Sign message](https://eips.ethereum.org/EIPS/eip-191).
*
* @example
* ```ts
* const signature = await account.signMessage({
* message: 'Hello, World!'
* })
* // '0x...'
* ```
*/
signMessage: (parameters: { message: SignableMessage }) => Promise<Hex>
/**
* Signs [EIP-712 Typed Data](https://eips.ethereum.org/EIPS/eip-712).
*
* @example
* ```ts
* const signature = await account.signTypedData({
* domain,
* types,
* primaryType: 'Mail',
* message,
* })
* ```
*/
signTypedData: <
const typedData extends TypedData | Record<string, unknown>,
primaryType extends keyof typedData | 'EIP712Domain' = keyof typedData,
>(
parameters: TypedDataDefinition<typedData, primaryType>,
) => Promise<Hex>
/**
* Signs the User Operation.
*
* @example
* ```ts
* const signature = await account.signUserOperation({
* chainId: 1,
* userOperation,
* })
* ```
*/
signUserOperation: (
parameters: UnionPartialBy<UserOperation, 'sender'> & {
chainId?: number | undefined
},
) => Promise<Hex>
/** User Operation configuration properties. */
userOperation?:
| {
/** Prepares gas properties for the User Operation request. */
estimateGas?:
| ((
userOperation: UserOperationRequest,
) => Promise<
ExactPartial<EstimateUserOperationGasReturnType> | undefined
>)
| undefined
}
| undefined
} & (eip7702 extends true
? {
/** EIP-7702 authorization properties, if applicable. */
authorization: {
/** EOA to delegate to. */
account: PrivateKeyAccount
/** Delegation address. */
address: Address
}
}
: {
authorization?: undefined
})
export type SmartAccount<
implementation extends
SmartAccountImplementation = SmartAccountImplementation,
> = Assign<
implementation['extend'],
Assign<
implementation,
{
/** Address of the Smart Account. */
address: Address
/**
* Retrieves the nonce of the Account.
*
* @example
* ```ts
* const nonce = await account.getNonce()
* // 1n
* ```
*/
getNonce: NonNullable<SmartAccountImplementation['getNonce']>
/** Whether or not the Smart Account has been deployed. */
isDeployed: () => Promise<boolean>
/** Type of account. */
type: 'smart'
}
>
>
// TODO(v3): Remove this in favor of `WebAuthnP256.sign.ReturnType` from Ox.
export type WebAuthnSignReturnType = {
signature: Hex
webauthn: WebAuthnP256.SignMetadata
raw: WebAuthnP256.sign.ReturnType['raw']
}
export type WebAuthnAccount = {
id: string
publicKey: Hex
sign: ({ hash }: { hash: Hash }) => Promise<WebAuthnSignReturnType>
signMessage: ({
message,
}: {
message: SignableMessage
}) => Promise<WebAuthnSignReturnType>
signTypedData: <
const typedData extends TypedDataDefinition | Record<string, unknown>,
primaryType extends keyof typedData | 'EIP712Domain' = keyof typedData,
>(
typedDataDefinition: TypedDataDefinition<typedData, primaryType>,
) => Promise<WebAuthnSignReturnType>
type: 'webAuthn'
}

View File

@@ -0,0 +1,208 @@
import type { Address, Narrow } from 'abitype'
import {
type ParseAccountErrorType,
parseAccount,
} from '../../../accounts/utils/parseAccount.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import { AccountNotFoundError } from '../../../errors/account.js'
import type { BaseError } from '../../../errors/base.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Calls } from '../../../types/calls.js'
import type { Chain } from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
import type { StateOverride } from '../../../types/stateOverride.js'
import type {
Assign,
MaybeRequired,
OneOf,
Prettify,
} from '../../../types/utils.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
import { getAction } from '../../../utils/getAction.js'
import { serializeStateOverride } from '../../../utils/stateOverride.js'
import type { SmartAccount } from '../../accounts/types.js'
import type { PaymasterActions } from '../../clients/decorators/paymaster.js'
import type {
DeriveSmartAccount,
GetSmartAccountParameter,
} from '../../types/account.js'
import type {
DeriveEntryPointVersion,
EntryPointVersion,
} from '../../types/entryPointVersion.js'
import type {
EstimateUserOperationGasReturnType as EstimateUserOperationGasReturnType_,
UserOperation,
UserOperationRequest,
} from '../../types/userOperation.js'
import { getUserOperationError } from '../../utils/errors/getUserOperationError.js'
import {
type FormatUserOperationGasErrorType,
formatUserOperationGas,
} from '../../utils/formatters/userOperationGas.js'
import {
type FormatUserOperationRequestErrorType,
formatUserOperationRequest,
} from '../../utils/formatters/userOperationRequest.js'
import {
type PrepareUserOperationErrorType,
type PrepareUserOperationParameters,
prepareUserOperation,
} from './prepareUserOperation.js'
export type EstimateUserOperationGasParameters<
account extends SmartAccount | undefined = SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = SmartAccount | undefined,
calls extends readonly unknown[] = readonly unknown[],
//
_derivedAccount extends SmartAccount | undefined = DeriveSmartAccount<
account,
accountOverride
>,
_derivedVersion extends
EntryPointVersion = DeriveEntryPointVersion<_derivedAccount>,
> = GetSmartAccountParameter<account, accountOverride, false> &
(
| UserOperation // Accept a full-formed User Operation.
| Assign<
// Accept a partially-formed User Operation (UserOperationRequest) to be filled.
UserOperationRequest<_derivedVersion>,
OneOf<{ calls: Calls<Narrow<calls>> } | { callData: Hex }> & {
paymaster?:
| Address
| true
| {
/** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */
getPaymasterData?:
| PaymasterActions['getPaymasterData']
| undefined
/** Retrieves paymaster-related User Operation properties to be used for gas estimation. */
getPaymasterStubData?:
| PaymasterActions['getPaymasterStubData']
| undefined
}
| undefined
/** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */
paymasterContext?: unknown | undefined
}
>
) &
// Allow the EntryPoint address to be overridden, if no Account is provided, it will need to be required.
MaybeRequired<
{ entryPointAddress?: Address },
_derivedAccount extends undefined ? true : false
> & {
/** State overrides for the User Operation call. */
stateOverride?: StateOverride | undefined
}
export type EstimateUserOperationGasReturnType<
account extends SmartAccount | undefined = SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = SmartAccount | undefined,
//
_derivedAccount extends SmartAccount | undefined = DeriveSmartAccount<
account,
accountOverride
>,
_derivedVersion extends
EntryPointVersion = DeriveEntryPointVersion<_derivedAccount>,
> = Prettify<EstimateUserOperationGasReturnType_<_derivedVersion>>
export type EstimateUserOperationGasErrorType =
| ParseAccountErrorType
| PrepareUserOperationErrorType
| FormatUserOperationRequestErrorType
| FormatUserOperationGasErrorType
| RequestErrorType
| ErrorType
/**
* Returns an estimate of gas values necessary to execute the User Operation.
*
* - Docs: https://viem.sh/actions/bundler/estimateUserOperationGas
*
* @param client - Client to use
* @param parameters - {@link EstimateUserOperationGasParameters}
* @returns The gas estimate (in wei). {@link EstimateUserOperationGasReturnType}
*
* @example
* import { createBundlerClient, http, parseEther } from 'viem'
* import { toSmartAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { estimateUserOperationGas } from 'viem/actions'
*
* const account = await toSmartAccount({ ... })
*
* const bundlerClient = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const values = await estimateUserOperationGas(bundlerClient, {
* account,
* calls: [{ to: '0x...', value: parseEther('1') }],
* })
*/
export async function estimateUserOperationGas<
const calls extends readonly unknown[],
account extends SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = undefined,
>(
client: Client<Transport, Chain | undefined, account>,
parameters: EstimateUserOperationGasParameters<
account,
accountOverride,
calls
>,
): Promise<EstimateUserOperationGasReturnType<account, accountOverride>> {
const {
account: account_ = client.account,
entryPointAddress,
stateOverride,
} = parameters
if (!account_ && !parameters.sender) throw new AccountNotFoundError()
const account = account_ ? parseAccount(account_) : undefined
const rpcStateOverride = serializeStateOverride(stateOverride)
const request = account
? await getAction(
client,
prepareUserOperation,
'prepareUserOperation',
)({
...parameters,
parameters: [
'authorization',
'factory',
'nonce',
'paymaster',
'signature',
],
} as unknown as PrepareUserOperationParameters)
: parameters
try {
const params = [
formatUserOperationRequest(request as UserOperation),
(entryPointAddress ?? account?.entryPoint?.address)!,
] as const
const result = await client.request({
method: 'eth_estimateUserOperationGas',
params: rpcStateOverride ? [...params, rpcStateOverride] : [...params],
})
return formatUserOperationGas(result) as EstimateUserOperationGasReturnType<
account,
accountOverride
>
} catch (error) {
const calls = (parameters as any).calls
throw getUserOperationError(error as BaseError, {
...(request as UserOperation),
...(calls ? { calls } : {}),
})
}
}

View File

@@ -0,0 +1,33 @@
import type { Address } from 'abitype'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
export type GetSupportedEntryPointsReturnType = readonly Address[]
export type GetSupportedEntryPointsErrorType = RequestErrorType | ErrorType
/**
* Returns the EntryPoints that the bundler supports.
*
* - Docs: https://viem.sh/actions/bundler/getSupportedEntryPoints
*
* @param client - Client to use
* @param parameters - {@link GetSupportedEntryPointsParameters}
* @returns Supported Entry Points. {@link GetSupportedEntryPointsReturnType}
*
* @example
* import { createBundlerClient, http, parseEther } from 'viem'
* import { mainnet } from 'viem/chains'
* import { getSupportedEntryPoints } from 'viem/actions'
*
* const bundlerClient = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const addresses = await getSupportedEntryPoints(bundlerClient)
*/
export function getSupportedEntryPoints(client: Client<Transport>) {
return client.request({ method: 'eth_supportedEntryPoints' })
}

View File

@@ -0,0 +1,85 @@
import type { Address } from 'abitype'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Hash } from '../../../types/misc.js'
import type { Prettify } from '../../../types/utils.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
import {
UserOperationNotFoundError,
type UserOperationNotFoundErrorType,
} from '../../errors/userOperation.js'
import type { UserOperation } from '../../types/userOperation.js'
import { formatUserOperation } from '../../utils/formatters/userOperation.js'
export type GetUserOperationParameters = {
/** The hash of the User Operation. */
hash: Hash
}
export type GetUserOperationReturnType = Prettify<{
/** The block hash the User Operation was included on. */
blockHash: Hash
/** The block number the User Operation was included on. */
blockNumber: bigint
/** The EntryPoint which handled the User Operation. */
entryPoint: Address
/** The hash of the transaction which included the User Operation. */
transactionHash: Hash
/** The User Operation. */
userOperation: UserOperation
}>
export type GetUserOperationErrorType =
| RequestErrorType
| UserOperationNotFoundErrorType
| ErrorType
/**
* Retrieves information about a User Operation given a hash.
*
* - Docs: https://viem.sh/account-abstraction/actions/bundler/getUserOperation
*
* @param client - Client to use
* @param parameters - {@link GetUserOperationParameters}
* @returns The receipt. {@link GetUserOperationReturnType}
*
* @example
* import { createBundlerClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { getUserOperation } from 'viem/actions
*
* const client = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const receipt = await getUserOperation(client, {
* hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
* })
*/
export async function getUserOperation(
client: Client<Transport>,
{ hash }: GetUserOperationParameters,
) {
const result = await client.request(
{
method: 'eth_getUserOperationByHash',
params: [hash],
},
{ dedupe: true },
)
if (!result) throw new UserOperationNotFoundError({ hash })
const { blockHash, blockNumber, entryPoint, transactionHash, userOperation } =
result
return {
blockHash,
blockNumber: BigInt(blockNumber),
entryPoint,
transactionHash,
userOperation: formatUserOperation(userOperation),
}
}

View File

@@ -0,0 +1,64 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Hash } from '../../../types/misc.js'
import type { Prettify } from '../../../types/utils.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
import {
UserOperationReceiptNotFoundError,
type UserOperationReceiptNotFoundErrorType,
} from '../../errors/userOperation.js'
import type { UserOperationReceipt } from '../../types/userOperation.js'
import { formatUserOperationReceipt } from '../../utils/formatters/userOperationReceipt.js'
export type GetUserOperationReceiptParameters = {
/** The hash of the User Operation. */
hash: Hash
}
export type GetUserOperationReceiptReturnType = Prettify<UserOperationReceipt>
export type GetUserOperationReceiptErrorType =
| RequestErrorType
| UserOperationReceiptNotFoundErrorType
| ErrorType
/**
* Returns the User Operation Receipt given a User Operation hash.
*
* - Docs: https://viem.sh/docs/actions/bundler/getUserOperationReceipt
*
* @param client - Client to use
* @param parameters - {@link GetUserOperationReceiptParameters}
* @returns The receipt. {@link GetUserOperationReceiptReturnType}
*
* @example
* import { createBundlerClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { getUserOperationReceipt } from 'viem/actions
*
* const client = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const receipt = await getUserOperationReceipt(client, {
* hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
* })
*/
export async function getUserOperationReceipt(
client: Client<Transport>,
{ hash }: GetUserOperationReceiptParameters,
) {
const receipt = await client.request(
{
method: 'eth_getUserOperationReceipt',
params: [hash],
},
{ dedupe: true },
)
if (!receipt) throw new UserOperationReceiptNotFoundError({ hash })
return formatUserOperationReceipt(receipt)
}

View File

@@ -0,0 +1,730 @@
import type { Address, Narrow } from 'abitype'
import {
type ParseAccountErrorType,
parseAccount,
} from '../../../accounts/utils/parseAccount.js'
import { prepareAuthorization } from '../../../actions/index.js'
import {
type EstimateFeesPerGasErrorType,
estimateFeesPerGas,
} from '../../../actions/public/estimateFeesPerGas.js'
import { getChainId as getChainId_ } from '../../../actions/public/getChainId.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import { AccountNotFoundError } from '../../../errors/account.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { SignedAuthorization } from '../../../types/authorization.js'
import type { Call, Calls } from '../../../types/calls.js'
import type { Chain } from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
import type { StateOverride } from '../../../types/stateOverride.js'
import type {
Assign,
OneOf,
Prettify,
UnionOmit,
} from '../../../types/utils.js'
import {
type EncodeFunctionDataErrorType,
encodeFunctionData,
} from '../../../utils/abi/encodeFunctionData.js'
import { type ConcatErrorType, concat } from '../../../utils/data/concat.js'
import { getAction } from '../../../utils/getAction.js'
import type { SmartAccount } from '../../accounts/types.js'
import type { BundlerClient } from '../../clients/createBundlerClient.js'
import type { PaymasterActions } from '../../clients/decorators/paymaster.js'
import type {
DeriveSmartAccount,
GetSmartAccountParameter,
} from '../../types/account.js'
import type {
DeriveEntryPointVersion,
EntryPointVersion,
} from '../../types/entryPointVersion.js'
import type {
UserOperation,
UserOperationRequest,
} from '../../types/userOperation.js'
import {
type GetPaymasterDataErrorType,
getPaymasterData as getPaymasterData_,
} from '../paymaster/getPaymasterData.js'
import {
type GetPaymasterStubDataErrorType,
getPaymasterStubData as getPaymasterStubData_,
} from '../paymaster/getPaymasterStubData.js'
import {
type EstimateUserOperationGasParameters,
estimateUserOperationGas,
} from './estimateUserOperationGas.js'
const defaultParameters = [
'factory',
'fees',
'gas',
'paymaster',
'nonce',
'signature',
'authorization',
] as const
export type PrepareUserOperationParameterType =
| 'factory'
| 'fees'
| 'gas'
| 'paymaster'
| 'nonce'
| 'signature'
| 'authorization'
type FactoryProperties<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> =
| (entryPointVersion extends '0.9'
? {
factory: UserOperation['factory']
factoryData: UserOperation['factoryData']
}
: never)
| (entryPointVersion extends '0.8'
? {
factory: UserOperation['factory']
factoryData: UserOperation['factoryData']
}
: never)
| (entryPointVersion extends '0.7'
? {
factory: UserOperation['factory']
factoryData: UserOperation['factoryData']
}
: never)
| (entryPointVersion extends '0.6'
? {
initCode: UserOperation['initCode']
}
: never)
type GasProperties<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> =
| (entryPointVersion extends '0.9'
? {
callGasLimit: UserOperation['callGasLimit']
preVerificationGas: UserOperation['preVerificationGas']
verificationGasLimit: UserOperation['verificationGasLimit']
paymasterPostOpGasLimit: UserOperation['paymasterPostOpGasLimit']
paymasterVerificationGasLimit: UserOperation['paymasterVerificationGasLimit']
}
: never)
| (entryPointVersion extends '0.8'
? {
callGasLimit: UserOperation['callGasLimit']
preVerificationGas: UserOperation['preVerificationGas']
verificationGasLimit: UserOperation['verificationGasLimit']
paymasterPostOpGasLimit: UserOperation['paymasterPostOpGasLimit']
paymasterVerificationGasLimit: UserOperation['paymasterVerificationGasLimit']
}
: never)
| (entryPointVersion extends '0.7'
? {
callGasLimit: UserOperation['callGasLimit']
preVerificationGas: UserOperation['preVerificationGas']
verificationGasLimit: UserOperation['verificationGasLimit']
paymasterPostOpGasLimit: UserOperation['paymasterPostOpGasLimit']
paymasterVerificationGasLimit: UserOperation['paymasterVerificationGasLimit']
}
: never)
| (entryPointVersion extends '0.6'
? {
callGasLimit: UserOperation['callGasLimit']
preVerificationGas: UserOperation['preVerificationGas']
verificationGasLimit: UserOperation['verificationGasLimit']
}
: never)
type FeeProperties = {
maxFeePerGas: UserOperation['maxFeePerGas']
maxPriorityFeePerGas: UserOperation['maxPriorityFeePerGas']
}
type NonceProperties = {
nonce: UserOperation['nonce']
}
type PaymasterProperties<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> =
| (entryPointVersion extends '0.9'
? {
paymaster: UserOperation['paymaster']
paymasterData: UserOperation['paymasterData']
paymasterPostOpGasLimit: UserOperation['paymasterPostOpGasLimit']
paymasterVerificationGasLimit: UserOperation['paymasterVerificationGasLimit']
}
: never)
| (entryPointVersion extends '0.8'
? {
paymaster: UserOperation['paymaster']
paymasterData: UserOperation['paymasterData']
paymasterPostOpGasLimit: UserOperation['paymasterPostOpGasLimit']
paymasterVerificationGasLimit: UserOperation['paymasterVerificationGasLimit']
}
: never)
| (entryPointVersion extends '0.7'
? {
paymaster: UserOperation['paymaster']
paymasterData: UserOperation['paymasterData']
paymasterPostOpGasLimit: UserOperation['paymasterPostOpGasLimit']
paymasterVerificationGasLimit: UserOperation['paymasterVerificationGasLimit']
}
: never)
| (entryPointVersion extends '0.6'
? {
paymasterAndData: UserOperation['paymasterAndData']
}
: never)
type SignatureProperties = {
signature: UserOperation['signature']
}
type AuthorizationProperties = {
authorization: UserOperation['authorization']
}
export type PrepareUserOperationRequest<
account extends SmartAccount | undefined = SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = SmartAccount | undefined,
calls extends readonly unknown[] = readonly unknown[],
//
_derivedAccount extends SmartAccount | undefined = DeriveSmartAccount<
account,
accountOverride
>,
_derivedVersion extends
EntryPointVersion = DeriveEntryPointVersion<_derivedAccount>,
> = Assign<
UserOperationRequest<_derivedVersion>,
OneOf<{ calls: Calls<Narrow<calls>> } | { callData: Hex }> & {
/** Data to append to the end of User Operation calldata. */
dataSuffix?: Hex | undefined
parameters?: readonly PrepareUserOperationParameterType[] | undefined
paymaster?:
| Address
| true
| {
/** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */
getPaymasterData?: PaymasterActions['getPaymasterData'] | undefined
/** Retrieves paymaster-related User Operation properties to be used for gas estimation. */
getPaymasterStubData?:
| PaymasterActions['getPaymasterStubData']
| undefined
}
| undefined
/** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */
paymasterContext?: unknown | undefined
/** State overrides for the User Operation call. */
stateOverride?: StateOverride | undefined
}
>
export type PrepareUserOperationParameters<
account extends SmartAccount | undefined = SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = SmartAccount | undefined,
calls extends readonly unknown[] = readonly unknown[],
request extends PrepareUserOperationRequest<
account,
accountOverride,
calls
> = PrepareUserOperationRequest<account, accountOverride, calls>,
> = request & GetSmartAccountParameter<account, accountOverride>
export type PrepareUserOperationReturnType<
account extends SmartAccount | undefined = SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = SmartAccount | undefined,
calls extends readonly unknown[] = readonly unknown[],
request extends PrepareUserOperationRequest<
account,
accountOverride,
calls
> = PrepareUserOperationRequest<account, accountOverride, calls>,
//
_parameters extends
PrepareUserOperationParameterType = request['parameters'] extends readonly PrepareUserOperationParameterType[]
? request['parameters'][number]
: (typeof defaultParameters)[number],
_derivedAccount extends SmartAccount | undefined = DeriveSmartAccount<
account,
accountOverride
>,
_derivedVersion extends
EntryPointVersion = DeriveEntryPointVersion<_derivedAccount>,
> = Prettify<
UnionOmit<request, 'calls' | 'parameters'> & {
callData: Hex
paymasterAndData: _derivedVersion extends '0.6' ? Hex : undefined
sender: UserOperation['sender']
} & (Extract<_parameters, 'authorization'> extends never
? {}
: AuthorizationProperties) &
(Extract<_parameters, 'factory'> extends never
? {}
: FactoryProperties<_derivedVersion>) &
(Extract<_parameters, 'nonce'> extends never ? {} : NonceProperties) &
(Extract<_parameters, 'fees'> extends never ? {} : FeeProperties) &
(Extract<_parameters, 'gas'> extends never
? {}
: GasProperties<_derivedVersion>) &
(Extract<_parameters, 'paymaster'> extends never
? {}
: PaymasterProperties<_derivedVersion>) &
(Extract<_parameters, 'signature'> extends never ? {} : SignatureProperties)
>
export type PrepareUserOperationErrorType =
| ParseAccountErrorType
| GetPaymasterStubDataErrorType
| GetPaymasterDataErrorType
| EncodeFunctionDataErrorType
| ConcatErrorType
| EstimateFeesPerGasErrorType
| ErrorType
/**
* Prepares a User Operation and fills in missing properties.
*
* - Docs: https://viem.sh/actions/bundler/prepareUserOperation
*
* @param args - {@link PrepareUserOperationParameters}
* @returns The User Operation. {@link PrepareUserOperationReturnType}
*
* @example
* import { createBundlerClient, http } from 'viem'
* import { toSmartAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { prepareUserOperation } from 'viem/actions'
*
* const account = await toSmartAccount({ ... })
*
* const client = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const request = await prepareUserOperation(client, {
* account,
* calls: [{ to: '0x...', value: parseEther('1') }],
* })
*/
export async function prepareUserOperation<
account extends SmartAccount | undefined,
const calls extends readonly unknown[],
const request extends PrepareUserOperationRequest<
account,
accountOverride,
calls
>,
accountOverride extends SmartAccount | undefined = undefined,
>(
client: Client<Transport, Chain | undefined, account>,
parameters_: PrepareUserOperationParameters<
account,
accountOverride,
calls,
request
>,
): Promise<
PrepareUserOperationReturnType<account, accountOverride, calls, request>
> {
const parameters = parameters_ as PrepareUserOperationParameters
const {
account: account_ = client.account,
dataSuffix = typeof client.dataSuffix === 'string'
? client.dataSuffix
: client.dataSuffix?.value,
parameters: properties = defaultParameters,
stateOverride,
} = parameters
////////////////////////////////////////////////////////////////////////////////
// Assert that an Account is defined.
////////////////////////////////////////////////////////////////////////////////
if (!account_) throw new AccountNotFoundError()
const account = parseAccount(account_)
////////////////////////////////////////////////////////////////////////////////
// Declare typed Bundler Client.
////////////////////////////////////////////////////////////////////////////////
const bundlerClient = client as unknown as BundlerClient
////////////////////////////////////////////////////////////////////////////////
// Declare Paymaster properties.
////////////////////////////////////////////////////////////////////////////////
const paymaster = parameters.paymaster ?? bundlerClient?.paymaster
const paymasterAddress = typeof paymaster === 'string' ? paymaster : undefined
const { getPaymasterStubData, getPaymasterData } = (() => {
// If `paymaster: true`, we will assume the Bundler Client supports Paymaster Actions.
if (paymaster === true)
return {
getPaymasterStubData: (parameters: any) =>
getAction(
bundlerClient,
getPaymasterStubData_,
'getPaymasterStubData',
)(parameters),
getPaymasterData: (parameters: any) =>
getAction(
bundlerClient,
getPaymasterData_,
'getPaymasterData',
)(parameters),
}
// If Actions are passed to `paymaster` (via Paymaster Client or directly), we will use them.
if (typeof paymaster === 'object') {
const { getPaymasterStubData, getPaymasterData } = paymaster
return {
getPaymasterStubData: (getPaymasterData && getPaymasterStubData
? getPaymasterStubData
: getPaymasterData) as typeof getPaymasterStubData,
getPaymasterData:
getPaymasterData && getPaymasterStubData
? getPaymasterData
: undefined,
}
}
// No Paymaster functions.
return {
getPaymasterStubData: undefined,
getPaymasterData: undefined,
}
})()
const paymasterContext = parameters.paymasterContext
? parameters.paymasterContext
: bundlerClient?.paymasterContext
////////////////////////////////////////////////////////////////////////////////
// Set up the User Operation request.
////////////////////////////////////////////////////////////////////////////////
let request = {
...parameters,
paymaster: paymasterAddress,
sender: account.address,
} as PrepareUserOperationRequest
////////////////////////////////////////////////////////////////////////////////
// Concurrently prepare properties required to fill the User Operation.
////////////////////////////////////////////////////////////////////////////////
const [callData, factory, fees, nonce, authorization] = await Promise.all([
(async () => {
if (parameters.calls)
return account.encodeCalls(
parameters.calls.map((call_) => {
const call = call_ as Call
if (call.abi)
return {
data: encodeFunctionData(call),
to: call.to,
value: call.value,
} as Call
return call as Call
}),
)
return parameters.callData
})(),
(async () => {
if (!properties.includes('factory')) return undefined
if (parameters.initCode) return { initCode: parameters.initCode }
if (parameters.factory && parameters.factoryData) {
return {
factory: parameters.factory,
factoryData: parameters.factoryData,
}
}
const { factory, factoryData } = await account.getFactoryArgs()
if (account.entryPoint.version === '0.6')
return {
initCode:
factory && factoryData ? concat([factory, factoryData]) : undefined,
}
return {
factory,
factoryData,
}
})(),
(async () => {
if (!properties.includes('fees')) return undefined
// If we have sufficient properties for fees, return them.
if (
typeof parameters.maxFeePerGas === 'bigint' &&
typeof parameters.maxPriorityFeePerGas === 'bigint'
)
return request
// If the Bundler Client has a `estimateFeesPerGas` hook, run it.
if (bundlerClient?.userOperation?.estimateFeesPerGas) {
const fees = await bundlerClient.userOperation.estimateFeesPerGas({
account,
bundlerClient,
userOperation: request as UserOperation,
})
return {
...request,
...fees,
}
}
// Otherwise, we will need to estimate the fees to fill the fee properties.
try {
const client_ = bundlerClient.client ?? client
const fees = await getAction(
client_,
estimateFeesPerGas,
'estimateFeesPerGas',
)({
chain: client_.chain,
type: 'eip1559',
})
return {
maxFeePerGas:
typeof parameters.maxFeePerGas === 'bigint'
? parameters.maxFeePerGas
: BigInt(
// Bundlers unfortunately have strict rules on fee prechecks we will need to set a generous buffer.
2n * fees.maxFeePerGas,
),
maxPriorityFeePerGas:
typeof parameters.maxPriorityFeePerGas === 'bigint'
? parameters.maxPriorityFeePerGas
: BigInt(
// Bundlers unfortunately have strict rules on fee prechecks we will need to set a generous buffer.
2n * fees.maxPriorityFeePerGas,
),
}
} catch {
return undefined
}
})(),
(async () => {
if (!properties.includes('nonce')) return undefined
if (typeof parameters.nonce === 'bigint') return parameters.nonce
return account.getNonce()
})(),
(async () => {
if (!properties.includes('authorization')) return undefined
if (typeof parameters.authorization === 'object')
return parameters.authorization
if (account.authorization && !(await account.isDeployed())) {
const authorization = await prepareAuthorization(
account.client,
account.authorization,
)
return {
...authorization,
r: '0xfffffffffffffffffffffffffffffff000000000000000000000000000000000',
s: '0x7aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
yParity: 1,
} satisfies SignedAuthorization
}
return undefined
})(),
])
////////////////////////////////////////////////////////////////////////////////
// Fill User Operation with the prepared properties from above.
////////////////////////////////////////////////////////////////////////////////
if (typeof callData !== 'undefined')
request.callData = dataSuffix ? concat([callData, dataSuffix]) : callData
if (typeof factory !== 'undefined')
request = { ...request, ...(factory as any) }
if (typeof fees !== 'undefined') request = { ...request, ...(fees as any) }
if (typeof nonce !== 'undefined') request.nonce = nonce
if (typeof authorization !== 'undefined')
request.authorization = authorization
////////////////////////////////////////////////////////////////////////////////
// Fill User Operation with the `signature` property.
////////////////////////////////////////////////////////////////////////////////
if (properties.includes('signature')) {
if (typeof parameters.signature !== 'undefined')
request.signature = parameters.signature
else
request.signature = await account.getStubSignature(
request as UserOperation,
)
}
////////////////////////////////////////////////////////////////////////////////
// `initCode` is required to be filled with EntryPoint 0.6.
////////////////////////////////////////////////////////////////////////////////
// If no `initCode` is provided, we use an empty bytes string.
if (account.entryPoint.version === '0.6' && !request.initCode)
request.initCode = '0x'
////////////////////////////////////////////////////////////////////////////////
// Fill User Operation with paymaster-related properties for **gas estimation**.
////////////////////////////////////////////////////////////////////////////////
let chainId: number | undefined
async function getChainId(): Promise<number> {
if (chainId) return chainId
if (client.chain) return client.chain.id
const chainId_ = await getAction(client, getChainId_, 'getChainId')({})
chainId = chainId_
return chainId
}
// If the User Operation is intended to be sponsored, we will need to fill the paymaster-related
// User Operation properties required to estimate the User Operation gas.
let isPaymasterPopulated = false
if (
properties.includes('paymaster') &&
getPaymasterStubData &&
!paymasterAddress &&
!parameters.paymasterAndData
) {
const {
isFinal = false,
sponsor: _,
...paymasterArgs
} = await getPaymasterStubData({
chainId: await getChainId(),
entryPointAddress: account.entryPoint.address,
context: paymasterContext,
...(request as UserOperation),
})
isPaymasterPopulated = isFinal
request = {
...request,
...paymasterArgs,
} as PrepareUserOperationRequest
}
////////////////////////////////////////////////////////////////////////////////
// `paymasterAndData` is required to be filled with EntryPoint 0.6.
////////////////////////////////////////////////////////////////////////////////
// If no `paymasterAndData` is provided, we use an empty bytes string.
if (account.entryPoint.version === '0.6' && !request.paymasterAndData)
request.paymasterAndData = '0x'
////////////////////////////////////////////////////////////////////////////////
// Fill User Operation with gas-related properties.
////////////////////////////////////////////////////////////////////////////////
if (properties.includes('gas')) {
// If the Account has opinionated gas estimation logic, run the `estimateGas` hook and
// fill the request with the prepared gas properties.
if (account.userOperation?.estimateGas) {
const gas = await account.userOperation.estimateGas(
request as UserOperation,
)
request = {
...request,
...gas,
} as PrepareUserOperationRequest
}
// If not all the gas properties are already populated, we will need to estimate the gas
// to fill the gas properties.
if (
typeof request.callGasLimit === 'undefined' ||
typeof request.preVerificationGas === 'undefined' ||
typeof request.verificationGasLimit === 'undefined' ||
(request.paymaster &&
typeof request.paymasterPostOpGasLimit === 'undefined') ||
(request.paymaster &&
typeof request.paymasterVerificationGasLimit === 'undefined')
) {
const gas = await getAction(
bundlerClient,
estimateUserOperationGas,
'estimateUserOperationGas',
)({
account,
// Some Bundlers fail if nullish gas values are provided for gas estimation :')
// so we will need to set a default zeroish value.
callGasLimit: 0n,
preVerificationGas: 0n,
verificationGasLimit: 0n,
stateOverride,
...(request.paymaster
? {
paymasterPostOpGasLimit: 0n,
paymasterVerificationGasLimit: 0n,
}
: {}),
...request,
} as EstimateUserOperationGasParameters)
request = {
...request,
callGasLimit: request.callGasLimit ?? gas.callGasLimit,
preVerificationGas:
request.preVerificationGas ?? gas.preVerificationGas,
verificationGasLimit:
request.verificationGasLimit ?? gas.verificationGasLimit,
paymasterPostOpGasLimit:
request.paymasterPostOpGasLimit ?? gas.paymasterPostOpGasLimit,
paymasterVerificationGasLimit:
request.paymasterVerificationGasLimit ??
gas.paymasterVerificationGasLimit,
} as PrepareUserOperationRequest
}
}
////////////////////////////////////////////////////////////////////////////////
// Fill User Operation with paymaster-related properties for **sending** the User Operation.
////////////////////////////////////////////////////////////////////////////////
// If the User Operation is intended to be sponsored, we will need to fill the paymaster-related
// User Operation properties required to send the User Operation.
if (
properties.includes('paymaster') &&
getPaymasterData &&
!paymasterAddress &&
!parameters.paymasterAndData &&
!isPaymasterPopulated
) {
// Retrieve paymaster-related User Operation properties to be used for **sending** the User Operation.
const paymaster = await getPaymasterData({
chainId: await getChainId(),
entryPointAddress: account.entryPoint.address,
context: paymasterContext,
...(request as UserOperation),
})
request = {
...request,
...paymaster,
} as PrepareUserOperationRequest
}
////////////////////////////////////////////////////////////////////////////////
// Remove redundant properties that do not conform to the User Operation schema.
////////////////////////////////////////////////////////////////////////////////
delete request.calls
delete request.parameters
delete request.paymasterContext
if (typeof request.paymaster !== 'string') delete request.paymaster
////////////////////////////////////////////////////////////////////////////////
return request as unknown as PrepareUserOperationReturnType<
account,
accountOverride,
calls,
request
>
}

View File

@@ -0,0 +1,166 @@
import type { Address, Narrow } from 'abitype'
import { parseAccount } from '../../../accounts/utils/parseAccount.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import { AccountNotFoundError } from '../../../errors/account.js'
import type { BaseError } from '../../../errors/base.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Calls } from '../../../types/calls.js'
import type { Chain } from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
import type { Assign, MaybeRequired, OneOf } from '../../../types/utils.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
import { getAction } from '../../../utils/getAction.js'
import type { SmartAccount } from '../../accounts/types.js'
import type { PaymasterActions } from '../../clients/decorators/paymaster.js'
import type {
DeriveSmartAccount,
GetSmartAccountParameter,
} from '../../types/account.js'
import type {
DeriveEntryPointVersion,
EntryPointVersion,
} from '../../types/entryPointVersion.js'
import type {
UserOperation,
UserOperationRequest,
} from '../../types/userOperation.js'
import { getUserOperationError } from '../../utils/errors/getUserOperationError.js'
import {
type FormatUserOperationRequestErrorType,
formatUserOperationRequest,
} from '../../utils/formatters/userOperationRequest.js'
import {
type PrepareUserOperationErrorType,
type PrepareUserOperationParameters,
prepareUserOperation,
} from './prepareUserOperation.js'
export type SendUserOperationParameters<
account extends SmartAccount | undefined = SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = SmartAccount | undefined,
calls extends readonly unknown[] = readonly unknown[],
//
_derivedAccount extends SmartAccount | undefined = DeriveSmartAccount<
account,
accountOverride
>,
_derivedVersion extends
EntryPointVersion = DeriveEntryPointVersion<_derivedAccount>,
> = GetSmartAccountParameter<account, accountOverride, false> &
(
| UserOperation // Accept a full-formed User Operation.
| Assign<
// Accept a partially-formed User Operation (UserOperationRequest) to be filled.
UserOperationRequest<_derivedVersion>,
OneOf<{ calls: Calls<Narrow<calls>> } | { callData: Hex }> & {
/** Data to append to the end of User Operation calldata. */
dataSuffix?: Hex | undefined
paymaster?:
| Address
| true
| {
/** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */
getPaymasterData?:
| PaymasterActions['getPaymasterData']
| undefined
/** Retrieves paymaster-related User Operation properties to be used for gas estimation. */
getPaymasterStubData?:
| PaymasterActions['getPaymasterStubData']
| undefined
}
| undefined
/** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */
paymasterContext?: unknown | undefined
}
>
) &
// Allow the EntryPoint address to be overridden, if no Account is provided, it will need to be required.
MaybeRequired<
{ entryPointAddress?: Address },
_derivedAccount extends undefined ? true : false
>
export type SendUserOperationReturnType = Hex
export type SendUserOperationErrorType =
| FormatUserOperationRequestErrorType
| PrepareUserOperationErrorType
| RequestErrorType
| ErrorType
/**
* Broadcasts a User Operation to the Bundler.
*
* - Docs: https://viem.sh/actions/bundler/sendUserOperation
*
* @param client - Client to use
* @param parameters - {@link SendUserOperationParameters}
* @returns The User Operation hash. {@link SendUserOperationReturnType}
*
* @example
* import { createBundlerClient, http, parseEther } from 'viem'
* import { mainnet } from 'viem/chains'
* import { toSmartAccount } from 'viem/accounts'
* import { sendUserOperation } from 'viem/actions'
*
* const account = await toSmartAccount({ ... })
*
* const bundlerClient = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const values = await sendUserOperation(bundlerClient, {
* account,
* calls: [{ to: '0x...', value: parseEther('1') }],
* })
*/
export async function sendUserOperation<
const calls extends readonly unknown[],
account extends SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = undefined,
>(
client: Client<Transport, Chain | undefined, account>,
parameters: SendUserOperationParameters<account, accountOverride, calls>,
) {
const { account: account_ = client.account, entryPointAddress } = parameters
if (!account_ && !parameters.sender) throw new AccountNotFoundError()
const account = account_ ? parseAccount(account_) : undefined
const request = account
? await getAction(
client,
prepareUserOperation,
'prepareUserOperation',
)(parameters as unknown as PrepareUserOperationParameters)
: parameters
const signature = (parameters.signature ||
(await account?.signUserOperation?.(request as UserOperation)))!
const rpcParameters = formatUserOperationRequest({
...request,
signature,
} as UserOperation)
try {
return await client.request(
{
method: 'eth_sendUserOperation',
params: [
rpcParameters,
(entryPointAddress ?? account?.entryPoint?.address)!,
],
},
{ retryCount: 0 },
)
} catch (error) {
const calls = (parameters as any).calls
throw getUserOperationError(error as BaseError, {
...(request as UserOperation),
...(calls ? { calls } : {}),
signature,
})
}
}

View File

@@ -0,0 +1,145 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Hash } from '../../../types/misc.js'
import type { Prettify } from '../../../types/utils.js'
import { getAction } from '../../../utils/getAction.js'
import { type ObserveErrorType, observe } from '../../../utils/observe.js'
import { type PollErrorType, poll } from '../../../utils/poll.js'
import { stringify } from '../../../utils/stringify.js'
import {
WaitForUserOperationReceiptTimeoutError,
type WaitForUserOperationReceiptTimeoutErrorType,
} from '../../errors/userOperation.js'
import type { UserOperationReceipt } from '../../types/userOperation.js'
import {
type GetUserOperationReceiptErrorType,
getUserOperationReceipt,
} from './getUserOperationReceipt.js'
export type WaitForUserOperationReceiptParameters = {
/** The hash of the User Operation. */
hash: Hash
/**
* Polling frequency (in ms). Defaults to the client's pollingInterval config.
* @default client.pollingInterval
*/
pollingInterval?: number | undefined
/**
* The number of times to retry.
* @default 6
*/
retryCount?: number | undefined
/** Optional timeout (in ms) to wait before stopping polling. */
timeout?: number | undefined
}
export type WaitForUserOperationReceiptReturnType =
Prettify<UserOperationReceipt>
export type WaitForUserOperationReceiptErrorType =
| WaitForUserOperationReceiptTimeoutErrorType
| PollErrorType
| ObserveErrorType
| ErrorType
/**
* Waits for the User Operation to be included on a [Block](https://viem.sh/docs/glossary/terms#block) (one confirmation), and then returns the User Operation receipt.
*
* - Docs: https://viem.sh/docs/actions/bundler/waitForUserOperationReceipt
*
* @param client - Client to use
* @param parameters - {@link WaitForUserOperationReceiptParameters}
* @returns The receipt. {@link WaitForUserOperationReceiptReturnType}
*
* @example
* import { createBundlerClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { waitForUserOperationReceipt } from 'viem/actions'
*
* const client = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const receipt = await waitForUserOperationReceipt(client, {
* hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
* })
*/
export function waitForUserOperationReceipt(
client: Client<Transport>,
parameters: WaitForUserOperationReceiptParameters,
): Promise<WaitForUserOperationReceiptReturnType> {
const {
hash,
pollingInterval = client.pollingInterval,
retryCount,
timeout = 120_000,
} = parameters
let count = 0
const observerId = stringify([
'waitForUserOperationReceipt',
client.uid,
hash,
])
return new Promise((resolve, reject) => {
const unobserve = observe(observerId, { resolve, reject }, (emit) => {
const done = (fn: () => void) => {
unpoll()
fn()
unobserve()
}
const timeoutId = timeout
? setTimeout(
() =>
done(() =>
emit.reject(
new WaitForUserOperationReceiptTimeoutError({ hash }),
),
),
timeout,
)
: undefined
const unpoll = poll(
async () => {
if (retryCount && count >= retryCount) {
clearTimeout(timeoutId)
done(() =>
emit.reject(
new WaitForUserOperationReceiptTimeoutError({ hash }),
),
)
}
try {
const receipt = await getAction(
client,
getUserOperationReceipt,
'getUserOperationReceipt',
)({ hash })
clearTimeout(timeoutId)
done(() => emit.resolve(receipt))
} catch (err) {
const error = err as GetUserOperationReceiptErrorType
if (error.name !== 'UserOperationReceiptNotFoundError') {
clearTimeout(timeoutId)
done(() => emit.reject(error))
}
}
count++
},
{
emitOnBegin: true,
interval: pollingInterval,
},
)
return unpoll
})
})
}

View File

@@ -0,0 +1,161 @@
import type { Address } from 'abitype'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Hex } from '../../../types/misc.js'
import type { OneOf, PartialBy, Prettify } from '../../../types/utils.js'
import { hexToBigInt } from '../../../utils/encoding/fromHex.js'
import { numberToHex } from '../../../utils/encoding/toHex.js'
import type { UserOperation } from '../../types/userOperation.js'
import {
type FormatUserOperationRequestErrorType,
formatUserOperationRequest,
} from '../../utils/formatters/userOperationRequest.js'
export type GetPaymasterDataParameters = OneOf<
| PartialBy<
Pick<
UserOperation<'0.6'>,
| 'callData'
| 'callGasLimit'
| 'initCode'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'sender'
| 'preVerificationGas'
| 'verificationGasLimit'
>,
| 'callGasLimit'
| 'initCode'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'preVerificationGas'
| 'verificationGasLimit'
>
| PartialBy<
Pick<
UserOperation<'0.7'>,
| 'callData'
| 'callGasLimit'
| 'factory'
| 'factoryData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'sender'
| 'preVerificationGas'
| 'verificationGasLimit'
| 'paymasterPostOpGasLimit'
| 'paymasterVerificationGasLimit'
>,
| 'callGasLimit'
| 'factory'
| 'factoryData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'preVerificationGas'
| 'verificationGasLimit'
>
| PartialBy<
Pick<
UserOperation<'0.8'>,
| 'callData'
| 'callGasLimit'
| 'factory'
| 'factoryData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'sender'
| 'preVerificationGas'
| 'verificationGasLimit'
| 'paymasterPostOpGasLimit'
| 'paymasterVerificationGasLimit'
>,
| 'callGasLimit'
| 'factory'
| 'factoryData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'preVerificationGas'
| 'verificationGasLimit'
>
> & {
context?: unknown | undefined
chainId: number
entryPointAddress: Address
}
export type GetPaymasterDataReturnType = Prettify<
OneOf<
| { paymasterAndData: Hex }
| {
paymaster: Address
paymasterData: Hex
paymasterPostOpGasLimit?: bigint | undefined
paymasterVerificationGasLimit?: bigint | undefined
}
>
>
export type GetPaymasterDataErrorType =
| FormatUserOperationRequestErrorType
| ErrorType
/**
* Retrieves paymaster-related User Operation properties to be used for sending the User Operation.
*
* - Docs: https://viem.sh/account-abstraction/actions/paymaster/getPaymasterData
*
* @param client - Client to use
* @param parameters - {@link GetPaymasterDataParameters}
* @returns Paymaster-related User Operation properties. {@link GetPaymasterDataReturnType}
*
* @example
* import { http } from 'viem'
* import { createPaymasterClient, getPaymasterData } from 'viem/account-abstraction'
*
* const paymasterClient = createPaymasterClient({
* transport: http('https://...'),
* })
*
* const userOperation = { ... }
*
* const values = await getPaymasterData(paymasterClient, {
* chainId: 1,
* entryPointAddress: '0x...',
* ...userOperation,
* })
*/
export async function getPaymasterData(
client: Client<Transport>,
parameters: GetPaymasterDataParameters,
): Promise<GetPaymasterDataReturnType> {
const { chainId, entryPointAddress, context, ...userOperation } = parameters
const request = formatUserOperationRequest(userOperation)
const { paymasterPostOpGasLimit, paymasterVerificationGasLimit, ...rest } =
await client.request({
method: 'pm_getPaymasterData',
params: [
{
...request,
callGasLimit: request.callGasLimit ?? '0x0',
verificationGasLimit: request.verificationGasLimit ?? '0x0',
preVerificationGas: request.preVerificationGas ?? '0x0',
},
entryPointAddress,
numberToHex(chainId),
context,
],
})
return {
...rest,
...(paymasterPostOpGasLimit && {
paymasterPostOpGasLimit: hexToBigInt(paymasterPostOpGasLimit),
}),
...(paymasterVerificationGasLimit && {
paymasterVerificationGasLimit: hexToBigInt(paymasterVerificationGasLimit),
}),
} as unknown as GetPaymasterDataReturnType
}

View File

@@ -0,0 +1,138 @@
import type { Address } from 'abitype'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Hex } from '../../../types/misc.js'
import type { OneOf, PartialBy, Prettify } from '../../../types/utils.js'
import { hexToBigInt } from '../../../utils/encoding/fromHex.js'
import { numberToHex } from '../../../utils/encoding/toHex.js'
import type { UserOperation } from '../../types/userOperation.js'
import {
type FormatUserOperationRequestErrorType,
formatUserOperationRequest,
} from '../../utils/formatters/userOperationRequest.js'
export type GetPaymasterStubDataParameters = OneOf<
| PartialBy<
Pick<
UserOperation<'0.6'>,
| 'callData'
| 'callGasLimit'
| 'initCode'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'sender'
| 'preVerificationGas'
| 'verificationGasLimit'
>,
| 'callGasLimit'
| 'initCode'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'preVerificationGas'
| 'verificationGasLimit'
>
| PartialBy<
Pick<
UserOperation<'0.7'>,
| 'callData'
| 'callGasLimit'
| 'factory'
| 'factoryData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'sender'
| 'preVerificationGas'
| 'verificationGasLimit'
>,
| 'callGasLimit'
| 'factory'
| 'factoryData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'preVerificationGas'
| 'verificationGasLimit'
>
> & {
context?: unknown | undefined
chainId: number
entryPointAddress: Address
}
export type GetPaymasterStubDataReturnType = Prettify<
OneOf<
| { paymasterAndData: Hex }
| {
paymaster: Address
paymasterData: Hex
paymasterVerificationGasLimit?: bigint | undefined
paymasterPostOpGasLimit: bigint
}
> & {
sponsor?: { name: string; icon?: string | undefined } | undefined
isFinal?: boolean | undefined
}
>
export type GetPaymasterStubDataErrorType =
| FormatUserOperationRequestErrorType
| ErrorType
/**
* Retrieves paymaster-related User Operation properties to be used for gas estimation.
*
* - Docs: https://viem.sh/account-abstraction/actions/paymaster/getPaymasterStubData
*
* @param client - Client to use
* @param parameters - {@link GetPaymasterStubDataParameters}
* @returns Paymaster-related User Operation properties. {@link GetPaymasterStubDataReturnType}
*
* @example
* import { http } from 'viem'
* import { createPaymasterClient, getPaymasterStubData } from 'viem/account-abstraction'
*
* const paymasterClient = createPaymasterClient({
* transport: http('https://...'),
* })
*
* const userOperation = { ... }
*
* const values = await getPaymasterStubData(paymasterClient, {
* chainId: 1,
* entryPointAddress: '0x...',
* ...userOperation,
* })
*/
export async function getPaymasterStubData(
client: Client<Transport>,
parameters: GetPaymasterStubDataParameters,
): Promise<GetPaymasterStubDataReturnType> {
const { chainId, entryPointAddress, context, ...userOperation } = parameters
const request = formatUserOperationRequest(userOperation)
const { paymasterPostOpGasLimit, paymasterVerificationGasLimit, ...rest } =
await client.request({
method: 'pm_getPaymasterStubData',
params: [
{
...request,
callGasLimit: request.callGasLimit ?? '0x0',
verificationGasLimit: request.verificationGasLimit ?? '0x0',
preVerificationGas: request.preVerificationGas ?? '0x0',
},
entryPointAddress,
numberToHex(chainId),
context,
],
})
return {
...rest,
...(paymasterPostOpGasLimit && {
paymasterPostOpGasLimit: hexToBigInt(paymasterPostOpGasLimit),
}),
...(paymasterVerificationGasLimit && {
paymasterVerificationGasLimit: hexToBigInt(paymasterVerificationGasLimit),
}),
} as unknown as GetPaymasterStubDataReturnType
}

View File

@@ -0,0 +1,165 @@
import type { EstimateFeesPerGasReturnType } from '../../actions/public/estimateFeesPerGas.js'
import {
type Client,
type ClientConfig,
type CreateClientErrorType,
createClient,
} from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Chain } from '../../types/chain.js'
import type { BundlerRpcSchema, RpcSchema } from '../../types/eip1193.js'
import type { Hex } from '../../types/misc.js'
import type { Prettify } from '../../types/utils.js'
import type { SmartAccount } from '../accounts/types.js'
import type { UserOperationRequest } from '../types/userOperation.js'
import { type BundlerActions, bundlerActions } from './decorators/bundler.js'
import type { PaymasterActions } from './decorators/paymaster.js'
export type BundlerClientConfig<
transport extends Transport = Transport,
chain extends Chain | undefined = Chain | undefined,
account extends SmartAccount | undefined = SmartAccount | undefined,
client extends Client | undefined = Client | undefined,
rpcSchema extends RpcSchema | undefined = undefined,
> = Prettify<
Pick<
ClientConfig<transport, chain, account, rpcSchema>,
| 'account'
| 'cacheTime'
| 'chain'
| 'key'
| 'name'
| 'pollingInterval'
| 'rpcSchema'
| 'transport'
>
> & {
/** Client that points to an Execution RPC URL. */
client?: client | Client | undefined
/** Data to append to the end of User Operation calldata. */
dataSuffix?: Hex | undefined
/** Paymaster configuration. */
paymaster?:
| true
| {
/** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */
getPaymasterData?: PaymasterActions['getPaymasterData'] | undefined
/** Retrieves paymaster-related User Operation properties to be used for gas estimation. */
getPaymasterStubData?:
| PaymasterActions['getPaymasterStubData']
| undefined
}
| undefined
/** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */
paymasterContext?: unknown
/** User Operation configuration. */
userOperation?:
| {
/** Prepares fee properties for the User Operation request. */
estimateFeesPerGas?:
| ((parameters: {
account: account | SmartAccount
bundlerClient: Client
userOperation: UserOperationRequest
}) => Promise<EstimateFeesPerGasReturnType<'eip1559'>>)
| undefined
}
| undefined
}
export type BundlerClient<
transport extends Transport = Transport,
chain extends Chain | undefined = Chain | undefined,
account extends SmartAccount | undefined = SmartAccount | undefined,
client extends Client | undefined = Client | undefined,
rpcSchema extends RpcSchema | undefined = undefined,
> = Prettify<
Client<
transport,
chain extends Chain
? chain
: client extends Client<any, infer chain>
? chain
: undefined,
account,
rpcSchema extends RpcSchema
? [...BundlerRpcSchema, ...rpcSchema]
: BundlerRpcSchema,
BundlerActions<account>
>
> & {
client: client
dataSuffix: Hex | undefined
paymaster: BundlerClientConfig['paymaster'] | undefined
paymasterContext: BundlerClientConfig['paymasterContext'] | undefined
userOperation: BundlerClientConfig['userOperation'] | undefined
}
export type CreateBundlerClientErrorType = CreateClientErrorType | ErrorType
/**
* Creates a Bundler Client with a given [Transport](https://viem.sh/docs/clients/intro) configured for a [Chain](https://viem.sh/docs/clients/chains).
*
* - Docs: https://viem.sh/account-abstraction/clients/bundler
*
* @param config - {@link BundlerClientConfig}
* @returns A Bundler Client. {@link BundlerClient}
*
* @example
* import { createPublicClient, http } from 'viem'
* import { createBundlerClient } from 'viem/account-abstraction'
* import { mainnet } from 'viem/chains'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
*
* const bundlerClient = createBundlerClient({
* client,
* transport: http('https://public.pimlico.io/v2/1/rpc'),
* })
*/
export function createBundlerClient<
transport extends Transport,
chain extends Chain | undefined = undefined,
account extends SmartAccount | undefined = undefined,
client extends Client | undefined = undefined,
rpcSchema extends RpcSchema | undefined = undefined,
>(
parameters: BundlerClientConfig<transport, chain, account, client, rpcSchema>,
): BundlerClient<transport, chain, account, client, rpcSchema>
export function createBundlerClient(
parameters: BundlerClientConfig,
): BundlerClient {
const {
client: client_,
dataSuffix,
key = 'bundler',
name = 'Bundler Client',
paymaster,
paymasterContext,
transport,
userOperation,
} = parameters
const client = Object.assign(
createClient({
...parameters,
chain: parameters.chain ?? client_?.chain,
key,
name,
transport,
type: 'bundlerClient',
}),
{
client: client_,
dataSuffix: dataSuffix ?? client_?.dataSuffix,
paymaster,
paymasterContext,
userOperation,
},
)
return client.extend(bundlerActions) as any
}

View File

@@ -0,0 +1,78 @@
import {
type Client,
type ClientConfig,
type CreateClientErrorType,
createClient,
} from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { PaymasterRpcSchema, RpcSchema } from '../../types/eip1193.js'
import type { Prettify } from '../../types/utils.js'
import {
type PaymasterActions,
paymasterActions,
} from './decorators/paymaster.js'
export type PaymasterClientConfig<
transport extends Transport = Transport,
rpcSchema extends RpcSchema | undefined = undefined,
> = Prettify<
Pick<
ClientConfig<transport, undefined, undefined, rpcSchema>,
'cacheTime' | 'key' | 'name' | 'pollingInterval' | 'rpcSchema' | 'transport'
>
>
export type PaymasterClient<
transport extends Transport = Transport,
rpcSchema extends RpcSchema | undefined = undefined,
> = Prettify<
Client<
transport,
undefined,
undefined,
rpcSchema extends RpcSchema
? [...PaymasterRpcSchema, ...rpcSchema]
: PaymasterRpcSchema,
PaymasterActions
>
>
export type CreatePaymasterClientErrorType = CreateClientErrorType | ErrorType
/**
* Creates a Paymaster Client.
*
* - Docs: https://viem.sh/account-abstraction/clients/paymaster
*
* @param config - {@link PaymasterClientConfig}
* @returns A Paymaster Client. {@link PaymasterClient}
*
* @example
* import { http } from 'viem'
* import { createPaymasterClient } from 'viem/account-abstraction'
*
* const paymasterClient = createPaymasterClient({
* transport: http('https://...'),
* })
*/
export function createPaymasterClient<
transport extends Transport,
rpcSchema extends RpcSchema | undefined = undefined,
>(
parameters: PaymasterClientConfig<transport, rpcSchema>,
): PaymasterClient<transport, rpcSchema>
export function createPaymasterClient(
parameters: PaymasterClientConfig,
): PaymasterClient {
const { key = 'bundler', name = 'Bundler Client', transport } = parameters
const client = createClient({
...parameters,
key,
name,
transport,
type: 'PaymasterClient',
})
return client.extend(paymasterActions)
}

View File

@@ -0,0 +1,296 @@
import {
type GetChainIdReturnType,
getChainId,
} from '../../../actions/public/getChainId.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Chain } from '../../../types/chain.js'
import type { SmartAccount } from '../../accounts/types.js'
import {
type EstimateUserOperationGasParameters,
type EstimateUserOperationGasReturnType,
estimateUserOperationGas,
} from '../../actions/bundler/estimateUserOperationGas.js'
import {
type GetSupportedEntryPointsReturnType,
getSupportedEntryPoints,
} from '../../actions/bundler/getSupportedEntryPoints.js'
import {
type GetUserOperationParameters,
type GetUserOperationReturnType,
getUserOperation,
} from '../../actions/bundler/getUserOperation.js'
import {
type GetUserOperationReceiptParameters,
type GetUserOperationReceiptReturnType,
getUserOperationReceipt,
} from '../../actions/bundler/getUserOperationReceipt.js'
import {
type PrepareUserOperationParameters,
type PrepareUserOperationRequest,
type PrepareUserOperationReturnType,
prepareUserOperation,
} from '../../actions/bundler/prepareUserOperation.js'
import {
type SendUserOperationParameters,
type SendUserOperationReturnType,
sendUserOperation,
} from '../../actions/bundler/sendUserOperation.js'
import {
type WaitForUserOperationReceiptParameters,
type WaitForUserOperationReceiptReturnType,
waitForUserOperationReceipt,
} from '../../actions/bundler/waitForUserOperationReceipt.js'
export type BundlerActions<
account extends SmartAccount | undefined = SmartAccount | undefined,
> = {
/**
* Returns an estimate of gas values necessary to execute the User Operation.
*
* - Docs: https://viem.sh/actions/bundler/estimateUserOperationGas
*
* @param client - Client to use
* @param parameters - {@link EstimateUserOperationGasParameters}
* @returns The gas estimate (in wei). {@link EstimateUserOperationGasReturnType}
*
* @example
* import { createBundlerClient, http, parseEther } from 'viem'
* import { mainnet } from 'viem/chains'
* import { toSmartAccount } from 'viem/accounts'
*
* const account = await toSmartAccount({ ... })
*
* const bundlerClient = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const values = await bundlerClient.estimateUserOperationGas({
* account,
* calls: [{ to: '0x...', value: parseEther('1') }],
* })
*/
estimateUserOperationGas: <
const calls extends readonly unknown[],
accountOverride extends SmartAccount | undefined = undefined,
>(
parameters: EstimateUserOperationGasParameters<
account,
accountOverride,
calls
>,
) => Promise<EstimateUserOperationGasReturnType<account, accountOverride>>
/**
* Returns the chain ID associated with the bundler.
*
* - Docs: https://viem.sh/docs/actions/public/getChainId
* - JSON-RPC Methods: [`eth_chainId`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_chainid)
*
* @returns The current chain ID. {@link GetChainIdReturnType}
*
* @example
* import { http } from 'viem'
* import { createBundlerClient, mainnet } from 'viem/chains'
*
* const client = createPublicClient({
* chain: mainnet,
* transport: http(),
* })
* const chainId = await client.getChainId()
* // 1
*/
getChainId: () => Promise<GetChainIdReturnType>
/**
* Returns the EntryPoints that the bundler supports.
*
* - Docs: https://viem.sh/actions/bundler/getSupportedEntryPoints
*
* @param client - Client to use
* @param parameters - {@link GetSupportedEntryPointsParameters}
* @returns Supported Entry Points. {@link GetSupportedEntryPointsReturnType}
*
* @example
* import { createBundlerClient, http, parseEther } from 'viem'
* import { mainnet } from 'viem/chains'
*
* const bundlerClient = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const addresses = await bundlerClient.getSupportedEntryPoints()
*/
getSupportedEntryPoints: () => Promise<GetSupportedEntryPointsReturnType>
/**
* Returns the information about a User Operation given a hash.
*
* - Docs: https://viem.sh/docs/actions/bundler/getUserOperation
*
* @param client - Client to use
* @param parameters - {@link GetUserOperationParameters}
* @returns The receipt. {@link GetUserOperationReturnType}
*
* @example
* import { createBundlerClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
*
* const client = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const receipt = await client.getUserOperation({
* hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
* })
*/
getUserOperation: (
parameters: GetUserOperationParameters,
) => Promise<GetUserOperationReturnType>
/**
* Returns the User Operation Receipt given a User Operation hash.
*
* - Docs: https://viem.sh/docs/actions/bundler/getUserOperationReceipt
*
* @param client - Client to use
* @param parameters - {@link GetUserOperationReceiptParameters}
* @returns The receipt. {@link GetUserOperationReceiptReturnType}
*
* @example
* import { createBundlerClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
*
* const client = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const receipt = await client.getUserOperationReceipt({
* hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
* })
*/
getUserOperationReceipt: (
parameters: GetUserOperationReceiptParameters,
) => Promise<GetUserOperationReceiptReturnType>
/**
* Prepares a User Operation and fills in missing properties.
*
* - Docs: https://viem.sh/actions/bundler/prepareUserOperation
*
* @param args - {@link PrepareUserOperationParameters}
* @returns The User Operation. {@link PrepareUserOperationReturnType}
*
* @example
* import { createBundlerClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { toSmartAccount } from 'viem/accounts'
*
* const account = await toSmartAccount({ ... })
*
* const client = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const request = await client.prepareUserOperation({
* account,
* calls: [{ to: '0x...', value: parseEther('1') }],
* })
*/
prepareUserOperation: <
const calls extends readonly unknown[],
const request extends PrepareUserOperationRequest<
account,
accountOverride,
calls
>,
accountOverride extends SmartAccount | undefined = undefined,
>(
parameters: PrepareUserOperationParameters<
account,
accountOverride,
calls,
request
>,
) => Promise<
PrepareUserOperationReturnType<account, accountOverride, calls, request>
>
/**
* Broadcasts a User Operation to the Bundler.
*
* - Docs: https://viem.sh/actions/bundler/sendUserOperation
*
* @param client - Client to use
* @param parameters - {@link SendUserOperationParameters}
* @returns The User Operation hash. {@link SendUserOperationReturnType}
*
* @example
* import { createBundlerClient, http, parseEther } from 'viem'
* import { mainnet } from 'viem/chains'
* import { toSmartAccount } from 'viem/accounts'
*
* const account = toSmartAccount({ ... })
*
* const bundlerClient = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const values = await bundlerClient.sendUserOperation({
* account,
* calls: [{ to: '0x...', value: parseEther('1') }],
* })
*/
sendUserOperation: <
const calls extends readonly unknown[],
accountOverride extends SmartAccount | undefined = undefined,
>(
parameters: SendUserOperationParameters<account, accountOverride, calls>,
) => Promise<SendUserOperationReturnType>
/**
* Waits for the User Operation to be included on a [Block](https://viem.sh/docs/glossary/terms#block) (one confirmation), and then returns the User Operation receipt.
*
* - Docs: https://viem.sh/docs/actions/bundler/waitForUserOperationReceipt
*
* @param client - Client to use
* @param parameters - {@link WaitForUserOperationReceiptParameters}
* @returns The receipt. {@link WaitForUserOperationReceiptReturnType}
*
* @example
* import { createBundlerClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
*
* const client = createBundlerClient({
* chain: mainnet,
* transport: http(),
* })
*
* const receipt = await client.waitForUserOperationReceipt({
* hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
* })
*/
waitForUserOperationReceipt: (
parameters: WaitForUserOperationReceiptParameters,
) => Promise<WaitForUserOperationReceiptReturnType>
}
export function bundlerActions<
transport extends Transport = Transport,
chain extends Chain | undefined = Chain | undefined,
account extends SmartAccount | undefined = SmartAccount | undefined,
>(client: Client<transport, chain, account>): BundlerActions<account> {
return {
estimateUserOperationGas: (parameters) =>
estimateUserOperationGas(client, parameters),
getChainId: () => getChainId(client),
getSupportedEntryPoints: () => getSupportedEntryPoints(client),
getUserOperation: (parameters) => getUserOperation(client, parameters),
getUserOperationReceipt: (parameters) =>
getUserOperationReceipt(client, parameters),
prepareUserOperation: (parameters) =>
prepareUserOperation(client, parameters),
sendUserOperation: (parameters) => sendUserOperation(client, parameters),
waitForUserOperationReceipt: (parameters) =>
waitForUserOperationReceipt(client, parameters),
}
}

View File

@@ -0,0 +1,81 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import {
type GetPaymasterDataParameters,
type GetPaymasterDataReturnType,
getPaymasterData,
} from '../../actions/paymaster/getPaymasterData.js'
import {
type GetPaymasterStubDataParameters,
type GetPaymasterStubDataReturnType,
getPaymasterStubData,
} from '../../actions/paymaster/getPaymasterStubData.js'
export type PaymasterActions = {
/**
* Retrieves paymaster-related User Operation properties to be used for sending the User Operation.
*
* - Docs: https://viem.sh/account-abstraction/actions/paymaster/getPaymasterData
*
* @param client - Client to use
* @param parameters - {@link GetPaymasterDataParameters}
* @returns Paymaster-related User Operation properties. {@link GetPaymasterDataReturnType}
*
* @example
* import { http } from 'viem'
* import { createPaymasterClient } from 'viem/account-abstraction'
*
* const paymasterClient = createPaymasterClient({
* transport: http('https://...'),
* })
*
* const userOperation = { ... }
*
* const values = await paymasterClient.getPaymasterData({
* chainId: 1,
* entryPointAddress: '0x...',
* ...userOperation,
* })
*/
getPaymasterData: (
parameters: GetPaymasterDataParameters,
) => Promise<GetPaymasterDataReturnType>
/**
* Retrieves paymaster-related User Operation properties to be used for gas estimation.
*
* - Docs: https://viem.sh/account-abstraction/actions/paymaster/getPaymasterStubData
*
* @param client - Client to use
* @param parameters - {@link GetPaymasterStubDataParameters}
* @returns Paymaster-related User Operation properties. {@link GetPaymasterStubDataReturnType}
*
* @example
* import { http } from 'viem'
* import { createPaymasterClient } from 'viem/account-abstraction'
*
* const paymasterClient = createPaymasterClient({
* transport: http('https://...'),
* })
*
* const userOperation = { ... }
*
* const values = await paymasterClient.getPaymasterStubData({
* chainId: 1,
* entryPointAddress: '0x...',
* ...userOperation,
* })
*/
getPaymasterStubData: (
parameters: GetPaymasterStubDataParameters,
) => Promise<GetPaymasterStubDataReturnType>
}
export function paymasterActions<transport extends Transport = Transport>(
client: Client<transport>,
): PaymasterActions {
return {
getPaymasterData: (parameters) => getPaymasterData(client, parameters),
getPaymasterStubData: (parameters) =>
getPaymasterStubData(client, parameters),
}
}

2965
node_modules/viem/account-abstraction/constants/abis.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
export const entryPoint06Address =
'0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789' as const
export const entryPoint07Address =
'0x0000000071727De22E5E9d8BAf0edAc6f37da032' as const
export const entryPoint08Address =
'0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108' as const
export const entryPoint09Address =
'0x433709009B8330FDa32311DF1C2AFA402eD8D009' as const

778
node_modules/viem/account-abstraction/errors/bundler.ts generated vendored Normal file
View File

@@ -0,0 +1,778 @@
import type { Address } from 'abitype'
import { BaseError } from '../../errors/base.js'
import type { Hex } from '../../types/misc.js'
export type AccountNotDeployedErrorType = AccountNotDeployedError & {
name: 'AccountNotDeployedError'
}
export class AccountNotDeployedError extends BaseError {
static message = /aa20/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Smart Account is not deployed.', {
cause,
metaMessages: [
'This could arise when:',
'- No `factory`/`factoryData` or `initCode` properties are provided for Smart Account deployment.',
'- An incorrect `sender` address is provided.',
],
name: 'AccountNotDeployedError',
})
}
}
export type ExecutionRevertedErrorType = ExecutionRevertedError & {
code: -32521
name: 'ExecutionRevertedError'
}
export class ExecutionRevertedError extends BaseError {
static code = -32521
static message = /execution reverted/
data?: { revertData?: Hex } | undefined
constructor({
cause,
data,
message,
}: {
cause?: BaseError | undefined
data?: { revertData?: Hex } | undefined
message?: string | undefined
} = {}) {
const reason = message
?.replace('execution reverted: ', '')
?.replace('execution reverted', '')
super(
`Execution reverted ${
reason ? `with reason: ${reason}` : 'for an unknown reason'
}.`,
{
cause,
name: 'ExecutionRevertedError',
},
)
this.data = data
}
}
export type FailedToSendToBeneficiaryErrorType =
FailedToSendToBeneficiaryError & {
name: 'FailedToSendToBeneficiaryError'
}
export class FailedToSendToBeneficiaryError extends BaseError {
static message = /aa91/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Failed to send funds to beneficiary.', {
cause,
name: 'FailedToSendToBeneficiaryError',
})
}
}
export type GasValuesOverflowErrorType = GasValuesOverflowError & {
name: 'GasValuesOverflowError'
}
export class GasValuesOverflowError extends BaseError {
static message = /aa94/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Gas value overflowed.', {
cause,
metaMessages: [
'This could arise when:',
'- one of the gas values exceeded 2**120 (uint120)',
].filter(Boolean) as string[],
name: 'GasValuesOverflowError',
})
}
}
export type HandleOpsOutOfGasErrorType = HandleOpsOutOfGasError & {
name: 'HandleOpsOutOfGasError'
}
export class HandleOpsOutOfGasError extends BaseError {
static message = /aa95/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super(
'The `handleOps` function was called by the Bundler with a gas limit too low.',
{
cause,
name: 'HandleOpsOutOfGasError',
},
)
}
}
export type InitCodeFailedErrorType = InitCodeFailedError & {
name: 'InitCodeFailedError'
}
export class InitCodeFailedError extends BaseError {
static message = /aa13/
constructor({
cause,
factory,
factoryData,
initCode,
}: {
cause?: BaseError | undefined
factory?: Address | undefined
factoryData?: Hex | undefined
initCode?: Hex | undefined
}) {
super('Failed to simulate deployment for Smart Account.', {
cause,
metaMessages: [
'This could arise when:',
'- Invalid `factory`/`factoryData` or `initCode` properties are present',
'- Smart Account deployment execution ran out of gas (low `verificationGasLimit` value)',
'- Smart Account deployment execution reverted with an error\n',
factory && `factory: ${factory}`,
factoryData && `factoryData: ${factoryData}`,
initCode && `initCode: ${initCode}`,
].filter(Boolean) as string[],
name: 'InitCodeFailedError',
})
}
}
export type InitCodeMustCreateSenderErrorType =
InitCodeMustCreateSenderError & {
name: 'InitCodeMustCreateSenderError'
}
export class InitCodeMustCreateSenderError extends BaseError {
static message = /aa15/
constructor({
cause,
factory,
factoryData,
initCode,
}: {
cause?: BaseError | undefined
factory?: Address | undefined
factoryData?: Hex | undefined
initCode?: Hex | undefined
}) {
super(
'Smart Account initialization implementation did not create an account.',
{
cause,
metaMessages: [
'This could arise when:',
'- `factory`/`factoryData` or `initCode` properties are invalid',
'- Smart Account initialization implementation is incorrect\n',
factory && `factory: ${factory}`,
factoryData && `factoryData: ${factoryData}`,
initCode && `initCode: ${initCode}`,
].filter(Boolean) as string[],
name: 'InitCodeMustCreateSenderError',
},
)
}
}
export type InitCodeMustReturnSenderErrorType =
InitCodeMustReturnSenderError & {
name: 'InitCodeMustReturnSenderError'
}
export class InitCodeMustReturnSenderError extends BaseError {
static message = /aa14/
constructor({
cause,
factory,
factoryData,
initCode,
sender,
}: {
cause?: BaseError | undefined
factory?: Address | undefined
factoryData?: Hex | undefined
initCode?: Hex | undefined
sender?: Address | undefined
}) {
super(
'Smart Account initialization implementation does not return the expected sender.',
{
cause,
metaMessages: [
'This could arise when:',
'Smart Account initialization implementation does not return a sender address\n',
factory && `factory: ${factory}`,
factoryData && `factoryData: ${factoryData}`,
initCode && `initCode: ${initCode}`,
sender && `sender: ${sender}`,
].filter(Boolean) as string[],
name: 'InitCodeMustReturnSenderError',
},
)
}
}
export type InsufficientPrefundErrorType = InsufficientPrefundError & {
name: 'InsufficientPrefundError'
}
export class InsufficientPrefundError extends BaseError {
static message = /aa21/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super(
'Smart Account does not have sufficient funds to execute the User Operation.',
{
cause,
metaMessages: [
'This could arise when:',
'- the Smart Account does not have sufficient funds to cover the required prefund, or',
'- a Paymaster was not provided',
].filter(Boolean) as string[],
name: 'InsufficientPrefundError',
},
)
}
}
export type InternalCallOnlyErrorType = InternalCallOnlyError & {
name: 'InternalCallOnlyError'
}
export class InternalCallOnlyError extends BaseError {
static message = /aa92/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Bundler attempted to call an invalid function on the EntryPoint.', {
cause,
name: 'InternalCallOnlyError',
})
}
}
export type InvalidAggregatorErrorType = InvalidAggregatorError & {
name: 'InvalidAggregatorError'
}
export class InvalidAggregatorError extends BaseError {
static message = /aa96/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super(
'Bundler used an invalid aggregator for handling aggregated User Operations.',
{
cause,
name: 'InvalidAggregatorError',
},
)
}
}
export type InvalidAccountNonceErrorType = InvalidAccountNonceError & {
name: 'InvalidAccountNonceError'
}
export class InvalidAccountNonceError extends BaseError {
static message = /aa25/
constructor({
cause,
nonce,
}: {
cause?: BaseError | undefined
nonce?: bigint | undefined
}) {
super('Invalid Smart Account nonce used for User Operation.', {
cause,
metaMessages: [nonce && `nonce: ${nonce}`].filter(Boolean) as string[],
name: 'InvalidAccountNonceError',
})
}
}
export type InvalidBeneficiaryErrorType = InvalidBeneficiaryError & {
name: 'InvalidBeneficiaryError'
}
export class InvalidBeneficiaryError extends BaseError {
static message = /aa90/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Bundler has not set a beneficiary address.', {
cause,
name: 'InvalidBeneficiaryError',
})
}
}
export type InvalidFieldsErrorType = InvalidFieldsError & {
name: 'InvalidFieldsError'
}
export class InvalidFieldsError extends BaseError {
static code = -32602
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Invalid fields set on User Operation.', {
cause,
name: 'InvalidFieldsError',
})
}
}
export type InvalidPaymasterAndDataErrorType = InvalidPaymasterAndDataError & {
name: 'InvalidPaymasterAndDataError'
}
export class InvalidPaymasterAndDataError extends BaseError {
static message = /aa93/
constructor({
cause,
paymasterAndData,
}: {
cause?: BaseError | undefined
paymasterAndData?: Hex | undefined
}) {
super('Paymaster properties provided are invalid.', {
cause,
metaMessages: [
'This could arise when:',
'- the `paymasterAndData` property is of an incorrect length\n',
paymasterAndData && `paymasterAndData: ${paymasterAndData}`,
].filter(Boolean) as string[],
name: 'InvalidPaymasterAndDataError',
})
}
}
export type PaymasterDepositTooLowErrorType = PaymasterDepositTooLowError & {
code: -32508
name: 'PaymasterDepositTooLowError'
}
export class PaymasterDepositTooLowError extends BaseError {
static code = -32508
static message = /aa31/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Paymaster deposit for the User Operation is too low.', {
cause,
metaMessages: [
'This could arise when:',
'- the Paymaster has deposited less than the expected amount via the `deposit` function',
].filter(Boolean) as string[],
name: 'PaymasterDepositTooLowError',
})
}
}
export type PaymasterFunctionRevertedErrorType =
PaymasterFunctionRevertedError & {
name: 'PaymasterFunctionRevertedError'
}
export class PaymasterFunctionRevertedError extends BaseError {
static message = /aa33/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('The `validatePaymasterUserOp` function on the Paymaster reverted.', {
cause,
name: 'PaymasterFunctionRevertedError',
})
}
}
export type PaymasterNotDeployedErrorType = PaymasterNotDeployedError & {
name: 'PaymasterNotDeployedError'
}
export class PaymasterNotDeployedError extends BaseError {
static message = /aa30/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('The Paymaster contract has not been deployed.', {
cause,
name: 'PaymasterNotDeployedError',
})
}
}
export type PaymasterRateLimitErrorType = PaymasterRateLimitError & {
code: -32504
name: 'PaymasterRateLimitError'
}
export class PaymasterRateLimitError extends BaseError {
static code = -32504
constructor({ cause }: { cause?: BaseError | undefined }) {
super(
'UserOperation rejected because paymaster (or signature aggregator) is throttled/banned.',
{
cause,
name: 'PaymasterRateLimitError',
},
)
}
}
export type PaymasterStakeTooLowErrorType = PaymasterStakeTooLowError & {
code: -32505
name: 'PaymasterStakeTooLowError'
}
export class PaymasterStakeTooLowError extends BaseError {
static code = -32505
constructor({ cause }: { cause?: BaseError | undefined }) {
super(
'UserOperation rejected because paymaster (or signature aggregator) stake or unstake-delay is too low.',
{
cause,
name: 'PaymasterStakeTooLowError',
},
)
}
}
export type PaymasterPostOpFunctionRevertedErrorType =
PaymasterPostOpFunctionRevertedError & {
name: 'PaymasterPostOpFunctionRevertedError'
}
export class PaymasterPostOpFunctionRevertedError extends BaseError {
static message = /aa50/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Paymaster `postOp` function reverted.', {
cause,
name: 'PaymasterPostOpFunctionRevertedError',
})
}
}
export type SenderAlreadyConstructedErrorType =
SenderAlreadyConstructedError & {
name: 'SenderAlreadyConstructedError'
}
export class SenderAlreadyConstructedError extends BaseError {
static message = /aa10/
constructor({
cause,
factory,
factoryData,
initCode,
}: {
cause?: BaseError | undefined
factory?: Address | undefined
factoryData?: Hex | undefined
initCode?: Hex | undefined
}) {
super('Smart Account has already been deployed.', {
cause,
metaMessages: [
'Remove the following properties and try again:',
factory && '`factory`',
factoryData && '`factoryData`',
initCode && '`initCode`',
].filter(Boolean) as string[],
name: 'SenderAlreadyConstructedError',
})
}
}
export type SignatureCheckFailedErrorType = SignatureCheckFailedError & {
code: -32507
name: 'SignatureCheckFailedError'
}
export class SignatureCheckFailedError extends BaseError {
static code = -32507
constructor({ cause }: { cause?: BaseError | undefined }) {
super(
'UserOperation rejected because account signature check failed (or paymaster signature, if the paymaster uses its data as signature).',
{
cause,
name: 'SignatureCheckFailedError',
},
)
}
}
export type SmartAccountFunctionRevertedErrorType =
SmartAccountFunctionRevertedError & {
name: 'SmartAccountFunctionRevertedError'
}
export class SmartAccountFunctionRevertedError extends BaseError {
static message = /aa23/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('The `validateUserOp` function on the Smart Account reverted.', {
cause,
name: 'SmartAccountFunctionRevertedError',
})
}
}
export type UnsupportedSignatureAggregatorErrorType =
UnsupportedSignatureAggregatorError & {
code: -32506
name: 'UnsupportedSignatureAggregatorError'
}
export class UnsupportedSignatureAggregatorError extends BaseError {
static code = -32506
constructor({ cause }: { cause?: BaseError | undefined }) {
super(
'UserOperation rejected because account specified unsupported signature aggregator.',
{
cause,
name: 'UnsupportedSignatureAggregatorError',
},
)
}
}
export type UserOperationExpiredErrorType = UserOperationExpiredError & {
name: 'UserOperationExpiredError'
}
export class UserOperationExpiredError extends BaseError {
static message = /aa22/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('User Operation expired.', {
cause,
metaMessages: [
'This could arise when:',
'- the `validAfter` or `validUntil` values returned from `validateUserOp` on the Smart Account are not satisfied',
].filter(Boolean) as string[],
name: 'UserOperationExpiredError',
})
}
}
export type UserOperationPaymasterExpiredErrorType =
UserOperationPaymasterExpiredError & {
name: 'UserOperationPaymasterExpiredError'
}
export class UserOperationPaymasterExpiredError extends BaseError {
static message = /aa32/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Paymaster for User Operation expired.', {
cause,
metaMessages: [
'This could arise when:',
'- the `validAfter` or `validUntil` values returned from `validatePaymasterUserOp` on the Paymaster are not satisfied',
].filter(Boolean) as string[],
name: 'UserOperationPaymasterExpiredError',
})
}
}
export type UserOperationSignatureErrorType = UserOperationSignatureError & {
name: 'UserOperationSignatureError'
}
export class UserOperationSignatureError extends BaseError {
static message = /aa24/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Signature provided for the User Operation is invalid.', {
cause,
metaMessages: [
'This could arise when:',
'- the `signature` for the User Operation is incorrectly computed, and unable to be verified by the Smart Account',
].filter(Boolean) as string[],
name: 'UserOperationSignatureError',
})
}
}
export type UserOperationPaymasterSignatureErrorType =
UserOperationPaymasterSignatureError & {
name: 'UserOperationPaymasterSignatureError'
}
export class UserOperationPaymasterSignatureError extends BaseError {
static message = /aa34/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('Signature provided for the User Operation is invalid.', {
cause,
metaMessages: [
'This could arise when:',
'- the `signature` for the User Operation is incorrectly computed, and unable to be verified by the Paymaster',
].filter(Boolean) as string[],
name: 'UserOperationPaymasterSignatureError',
})
}
}
export type UserOperationRejectedByEntryPointErrorType =
UserOperationRejectedByEntryPointError & {
code: -32500
name: 'UserOperationRejectedByEntryPointError'
}
export class UserOperationRejectedByEntryPointError extends BaseError {
static code = -32500
constructor({ cause }: { cause?: BaseError | undefined }) {
super(
"User Operation rejected by EntryPoint's `simulateValidation` during account creation or validation.",
{
cause,
name: 'UserOperationRejectedByEntryPointError',
},
)
}
}
export type UserOperationRejectedByPaymasterErrorType =
UserOperationRejectedByPaymasterError & {
code: -32501
name: 'UserOperationRejectedByPaymasterError'
}
export class UserOperationRejectedByPaymasterError extends BaseError {
static code = -32501
constructor({ cause }: { cause?: BaseError | undefined }) {
super("User Operation rejected by Paymaster's `validatePaymasterUserOp`.", {
cause,
name: 'UserOperationRejectedByPaymasterError',
})
}
}
export type UserOperationRejectedByOpCodeErrorType =
UserOperationRejectedByOpCodeError & {
code: -32502
name: 'UserOperationRejectedByOpCodeError'
}
export class UserOperationRejectedByOpCodeError extends BaseError {
static code = -32502
constructor({ cause }: { cause?: BaseError | undefined }) {
super('User Operation rejected with op code validation error.', {
cause,
name: 'UserOperationRejectedByOpCodeError',
})
}
}
export type UserOperationOutOfTimeRangeErrorType =
UserOperationOutOfTimeRangeError & {
code: -32503
name: 'UserOperationOutOfTimeRangeError'
}
export class UserOperationOutOfTimeRangeError extends BaseError {
static code = -32503
constructor({ cause }: { cause?: BaseError | undefined }) {
super(
'UserOperation out of time-range: either wallet or paymaster returned a time-range, and it is already expired (or will expire soon).',
{
cause,
name: 'UserOperationOutOfTimeRangeError',
},
)
}
}
export type UnknownBundlerErrorType = UnknownBundlerError & {
name: 'UnknownBundlerError'
}
export class UnknownBundlerError extends BaseError {
constructor({ cause }: { cause?: BaseError | undefined }) {
super(
`An error occurred while executing user operation: ${cause?.shortMessage}`,
{
cause,
name: 'UnknownBundlerError',
},
)
}
}
export type VerificationGasLimitExceededErrorType =
VerificationGasLimitExceededError & {
name: 'VerificationGasLimitExceededError'
}
export class VerificationGasLimitExceededError extends BaseError {
static message = /aa40/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('User Operation verification gas limit exceeded.', {
cause,
metaMessages: [
'This could arise when:',
'- the gas used for verification exceeded the `verificationGasLimit`',
].filter(Boolean) as string[],
name: 'VerificationGasLimitExceededError',
})
}
}
export type VerificationGasLimitTooLowErrorType =
VerificationGasLimitTooLowError & {
name: 'VerificationGasLimitTooLowError'
}
export class VerificationGasLimitTooLowError extends BaseError {
static message = /aa41/
constructor({
cause,
}: {
cause?: BaseError | undefined
}) {
super('User Operation verification gas limit is too low.', {
cause,
metaMessages: [
'This could arise when:',
'- the `verificationGasLimit` is too low to verify the User Operation',
].filter(Boolean) as string[],
name: 'VerificationGasLimitTooLowError',
})
}
}

View File

@@ -0,0 +1,111 @@
import { BaseError } from '../../errors/base.js'
import { prettyPrint } from '../../errors/transaction.js'
import type { Hash } from '../../types/misc.js'
import { formatGwei } from '../../utils/index.js'
import type { UserOperation } from '../types/userOperation.js'
export type UserOperationExecutionErrorType = UserOperationExecutionError & {
name: 'UserOperationExecutionError'
}
export class UserOperationExecutionError extends BaseError {
override cause: BaseError
constructor(
cause: BaseError,
{
callData,
callGasLimit,
docsPath,
factory,
factoryData,
initCode,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
paymaster,
paymasterAndData,
paymasterData,
paymasterPostOpGasLimit,
paymasterVerificationGasLimit,
preVerificationGas,
sender,
signature,
verificationGasLimit,
}: UserOperation & {
docsPath?: string | undefined
},
) {
const prettyArgs = prettyPrint({
callData,
callGasLimit,
factory,
factoryData,
initCode,
maxFeePerGas:
typeof maxFeePerGas !== 'undefined' &&
`${formatGwei(maxFeePerGas)} gwei`,
maxPriorityFeePerGas:
typeof maxPriorityFeePerGas !== 'undefined' &&
`${formatGwei(maxPriorityFeePerGas)} gwei`,
nonce,
paymaster,
paymasterAndData,
paymasterData,
paymasterPostOpGasLimit,
paymasterVerificationGasLimit,
preVerificationGas,
sender,
signature,
verificationGasLimit,
})
super(cause.shortMessage, {
cause,
docsPath,
metaMessages: [
...(cause.metaMessages ? [...cause.metaMessages, ' '] : []),
'Request Arguments:',
prettyArgs,
].filter(Boolean) as string[],
name: 'UserOperationExecutionError',
})
this.cause = cause
}
}
export type UserOperationReceiptNotFoundErrorType =
UserOperationReceiptNotFoundError & {
name: 'UserOperationReceiptNotFoundError'
}
export class UserOperationReceiptNotFoundError extends BaseError {
constructor({ hash }: { hash: Hash }) {
super(
`User Operation receipt with hash "${hash}" could not be found. The User Operation may not have been processed yet.`,
{ name: 'UserOperationReceiptNotFoundError' },
)
}
}
export type UserOperationNotFoundErrorType = UserOperationNotFoundError & {
name: 'UserOperationNotFoundError'
}
export class UserOperationNotFoundError extends BaseError {
constructor({ hash }: { hash: Hash }) {
super(`User Operation with hash "${hash}" could not be found.`, {
name: 'UserOperationNotFoundError',
})
}
}
export type WaitForUserOperationReceiptTimeoutErrorType =
WaitForUserOperationReceiptTimeoutError & {
name: 'WaitForUserOperationReceiptTimeoutError'
}
export class WaitForUserOperationReceiptTimeoutError extends BaseError {
constructor({ hash }: { hash: Hash }) {
super(
`Timed out while waiting for User Operation with hash "${hash}" to be confirmed.`,
{ name: 'WaitForUserOperationReceiptTimeoutError' },
)
}
}

265
node_modules/viem/account-abstraction/index.ts generated vendored Normal file
View File

@@ -0,0 +1,265 @@
// biome-ignore lint/performance/noBarrelFile: entrypoint
export {
type CreateWebAuthnCredentialParameters,
type CreateWebAuthnCredentialReturnType,
createWebAuthnCredential,
type P256Credential,
} from './accounts/createWebAuthnCredential.js'
export {
type CoinbaseSmartAccountImplementation,
type ToCoinbaseSmartAccountParameters,
type ToCoinbaseSmartAccountReturnType,
toCoinbaseSmartAccount,
} from './accounts/implementations/toCoinbaseSmartAccount.js'
export {
type Simple7702SmartAccountImplementation,
type ToSimple7702SmartAccountParameters,
type ToSimple7702SmartAccountReturnType,
toSimple7702SmartAccount,
} from './accounts/implementations/toSimple7702SmartAccount.js'
export {
type SoladySmartAccountImplementation,
type ToSoladySmartAccountParameters,
type ToSoladySmartAccountReturnType,
toSoladySmartAccount,
} from './accounts/implementations/toSoladySmartAccount.js'
export {
type ToSmartAccountParameters,
type ToSmartAccountReturnType,
toSmartAccount,
} from './accounts/toSmartAccount.js'
export {
type ToWebAuthnAccountErrorType,
type ToWebAuthnAccountParameters,
type ToWebAuthnAccountReturnType,
toWebAuthnAccount,
} from './accounts/toWebAuthnAccount.js'
export type {
SmartAccount,
SmartAccountImplementation,
WebAuthnAccount,
WebAuthnSignReturnType,
} from './accounts/types.js'
export {
type EstimateUserOperationGasErrorType,
type EstimateUserOperationGasParameters,
type EstimateUserOperationGasReturnType,
estimateUserOperationGas,
} from './actions/bundler/estimateUserOperationGas.js'
export {
type GetSupportedEntryPointsErrorType,
type GetSupportedEntryPointsReturnType,
getSupportedEntryPoints,
} from './actions/bundler/getSupportedEntryPoints.js'
export {
type GetUserOperationErrorType,
type GetUserOperationParameters,
type GetUserOperationReturnType,
getUserOperation,
} from './actions/bundler/getUserOperation.js'
export {
type GetUserOperationReceiptErrorType,
type GetUserOperationReceiptParameters,
type GetUserOperationReceiptReturnType,
getUserOperationReceipt,
} from './actions/bundler/getUserOperationReceipt.js'
export {
type PrepareUserOperationErrorType,
type PrepareUserOperationParameters,
type PrepareUserOperationParameterType,
type PrepareUserOperationRequest,
type PrepareUserOperationReturnType,
prepareUserOperation,
} from './actions/bundler/prepareUserOperation.js'
export {
type SendUserOperationErrorType,
type SendUserOperationParameters,
type SendUserOperationReturnType,
sendUserOperation,
} from './actions/bundler/sendUserOperation.js'
export {
type WaitForUserOperationReceiptErrorType,
type WaitForUserOperationReceiptParameters,
type WaitForUserOperationReceiptReturnType,
waitForUserOperationReceipt,
} from './actions/bundler/waitForUserOperationReceipt.js'
export {
type GetPaymasterDataErrorType,
type GetPaymasterDataParameters,
type GetPaymasterDataReturnType,
getPaymasterData,
} from './actions/paymaster/getPaymasterData.js'
export {
type GetPaymasterStubDataErrorType,
type GetPaymasterStubDataParameters,
type GetPaymasterStubDataReturnType,
getPaymasterStubData,
} from './actions/paymaster/getPaymasterStubData.js'
export {
type BundlerClient,
type BundlerClientConfig,
type CreateBundlerClientErrorType,
createBundlerClient,
} from './clients/createBundlerClient.js'
export {
type CreatePaymasterClientErrorType,
createPaymasterClient,
type PaymasterClient,
type PaymasterClientConfig,
} from './clients/createPaymasterClient.js'
export {
type BundlerActions,
bundlerActions,
} from './clients/decorators/bundler.js'
export {
type PaymasterActions,
paymasterActions,
} from './clients/decorators/paymaster.js'
export {
entryPoint06Abi,
entryPoint07Abi,
entryPoint08Abi,
entryPoint09Abi,
} from './constants/abis.js'
export {
entryPoint06Address,
entryPoint07Address,
entryPoint08Address,
entryPoint09Address,
} from './constants/address.js'
export {
AccountNotDeployedError,
type AccountNotDeployedErrorType,
FailedToSendToBeneficiaryError,
type FailedToSendToBeneficiaryErrorType,
GasValuesOverflowError,
type GasValuesOverflowErrorType,
HandleOpsOutOfGasError,
type HandleOpsOutOfGasErrorType,
InitCodeFailedError,
type InitCodeFailedErrorType,
InitCodeMustCreateSenderError,
type InitCodeMustCreateSenderErrorType,
InitCodeMustReturnSenderError,
type InitCodeMustReturnSenderErrorType,
InsufficientPrefundError,
type InsufficientPrefundErrorType,
InternalCallOnlyError,
type InternalCallOnlyErrorType,
InvalidAggregatorError,
type InvalidAggregatorErrorType,
InvalidBeneficiaryError,
type InvalidBeneficiaryErrorType,
InvalidPaymasterAndDataError,
type InvalidPaymasterAndDataErrorType,
PaymasterDepositTooLowError,
type PaymasterDepositTooLowErrorType,
PaymasterFunctionRevertedError,
type PaymasterFunctionRevertedErrorType,
PaymasterNotDeployedError,
type PaymasterNotDeployedErrorType,
PaymasterPostOpFunctionRevertedError,
type PaymasterPostOpFunctionRevertedErrorType,
SenderAlreadyConstructedError,
type SenderAlreadyConstructedErrorType,
SmartAccountFunctionRevertedError,
type SmartAccountFunctionRevertedErrorType,
UnknownBundlerError,
type UnknownBundlerErrorType,
UserOperationExpiredError,
type UserOperationExpiredErrorType,
UserOperationPaymasterExpiredError,
type UserOperationPaymasterExpiredErrorType,
UserOperationPaymasterSignatureError,
type UserOperationPaymasterSignatureErrorType,
UserOperationSignatureError,
type UserOperationSignatureErrorType,
VerificationGasLimitExceededError,
type VerificationGasLimitExceededErrorType,
VerificationGasLimitTooLowError,
type VerificationGasLimitTooLowErrorType,
} from './errors/bundler.js'
export {
UserOperationExecutionError,
type UserOperationExecutionErrorType,
UserOperationNotFoundError,
type UserOperationNotFoundErrorType,
UserOperationReceiptNotFoundError,
type UserOperationReceiptNotFoundErrorType,
WaitForUserOperationReceiptTimeoutError,
type WaitForUserOperationReceiptTimeoutErrorType,
} from './errors/userOperation.js'
export type {
DeriveSmartAccount,
GetSmartAccountParameter,
} from './types/account.js'
export type {
DeriveEntryPointVersion,
EntryPointVersion,
GetEntryPointVersionParameter,
} from './types/entryPointVersion.js'
export type {
RpcEstimateUserOperationGasReturnType,
RpcGetUserOperationByHashReturnType,
RpcUserOperation,
RpcUserOperationReceipt,
RpcUserOperationRequest,
} from './types/rpc.js'
export type {
PackedUserOperation,
UserOperation,
UserOperationReceipt,
UserOperationRequest,
} from './types/userOperation.js'
export {
type GetBundlerErrorParameters,
type GetBundlerErrorReturnType,
getBundlerError,
} from './utils/errors/getBundlerError.js'
export {
type GetUserOperationErrorErrorType,
type GetUserOperationErrorParameters,
type GetUserOperationErrorReturnType,
getUserOperationError,
} from './utils/errors/getUserOperationError.js'
export {
type FormatUserOperationErrorType,
formatUserOperation,
} from './utils/formatters/userOperation.js'
export {
type FormatUserOperationGasErrorType,
formatUserOperationGas,
} from './utils/formatters/userOperationGas.js'
export {
type FormatUserOperationReceiptErrorType,
formatUserOperationReceipt,
} from './utils/formatters/userOperationReceipt.js'
export {
type FormatUserOperationRequestErrorType,
formatUserOperationRequest,
} from './utils/formatters/userOperationRequest.js'
export {
type GetInitCodeOptions,
getInitCode,
} from './utils/userOperation/getInitCode.js'
export {
type GetUserOperationHashParameters,
type GetUserOperationHashReturnType,
getUserOperationHash,
} from './utils/userOperation/getUserOperationHash.js'
export {
type GetUserOperationTypedDataParameters,
type GetUserOperationTypedDataReturnType,
getUserOperationTypedData,
} from './utils/userOperation/getUserOperationTypedData.js'
export {
type ToPackedUserOperationOptions,
toPackedUserOperation,
} from './utils/userOperation/toPackedUserOperation.js'
export { toUserOperation } from './utils/userOperation/toUserOperation.js'

6
node_modules/viem/account-abstraction/package.json generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"type": "module",
"types": "../_types/account-abstraction/index.d.ts",
"module": "../_esm/account-abstraction/index.js",
"main": "../_cjs/account-abstraction/index.js"
}

21
node_modules/viem/account-abstraction/types/account.ts generated vendored Normal file
View File

@@ -0,0 +1,21 @@
import type { IsUndefined } from '../../types/utils.js'
import type { SmartAccount } from '../accounts/types.js'
export type DeriveSmartAccount<
account extends SmartAccount | undefined,
accountOverride extends SmartAccount | undefined,
> = accountOverride extends SmartAccount ? accountOverride : account
export type GetSmartAccountParameter<
account extends SmartAccount | undefined = SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = SmartAccount,
required extends boolean = true,
> = IsUndefined<account> extends true
? required extends true
? {
account: IsUndefined<accountOverride> extends true
? SmartAccount
: accountOverride | SmartAccount
}
: { account?: accountOverride | SmartAccount | undefined }
: { account?: accountOverride | SmartAccount | undefined }

View File

@@ -0,0 +1,24 @@
import type { SmartAccount } from '../accounts/types.js'
/** @link https://github.com/eth-infinitism/account-abstraction/releases */
export type EntryPointVersion = '0.6' | '0.7' | '0.8' | '0.9'
export type DeriveEntryPointVersion<account extends SmartAccount | undefined> =
account extends SmartAccount
? account['entryPoint']['version']
: EntryPointVersion
/** @internal */
export type GetEntryPointVersionParameter<
version extends EntryPointVersion | undefined,
versionOverride extends EntryPointVersion | undefined =
| EntryPointVersion
| undefined,
> = {
entryPointVersion?:
| version
| versionOverride
| EntryPointVersion
| null
| undefined
}

32
node_modules/viem/account-abstraction/types/rpc.ts generated vendored Normal file
View File

@@ -0,0 +1,32 @@
import type { Hex } from '../../types/misc.js'
import type { RpcAuthorization } from '../../types/rpc.js'
import type { EntryPointVersion } from './entryPointVersion.js'
import type {
EstimateUserOperationGasReturnType,
GetUserOperationByHashReturnType,
UserOperation,
UserOperationReceipt,
UserOperationRequest,
} from './userOperation.js'
export type RpcEstimateUserOperationGasReturnType<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> = EstimateUserOperationGasReturnType<entryPointVersion, Hex>
export type RpcGetUserOperationByHashReturnType<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> = GetUserOperationByHashReturnType<entryPointVersion, Hex, Hex>
export type RpcUserOperation<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> = UserOperation<entryPointVersion, Hex, Hex> & {
eip7702Auth?: RpcAuthorization
}
export type RpcUserOperationReceipt<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> = UserOperationReceipt<entryPointVersion, Hex, Hex, Hex>
export type RpcUserOperationRequest<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> = UserOperationRequest<entryPointVersion, Hex, Hex>

View File

@@ -0,0 +1,318 @@
import type { Address } from 'abitype'
import type { SignedAuthorization } from '../../types/authorization.js'
import type { Log } from '../../types/log.js'
import type { Hash, Hex } from '../../types/misc.js'
import type { TransactionReceipt } from '../../types/transaction.js'
import type { OneOf, UnionPartialBy } from '../../types/utils.js'
import type { EntryPointVersion } from './entryPointVersion.js'
/** @link https://eips.ethereum.org/EIPS/eip-4337#-eth_estimateuseroperationgas */
export type EstimateUserOperationGasReturnType<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
uint256 = bigint,
> = OneOf<
| (entryPointVersion extends '0.9'
? {
preVerificationGas: uint256
verificationGasLimit: uint256
callGasLimit: uint256
paymasterVerificationGasLimit?: uint256 | undefined
paymasterPostOpGasLimit?: uint256 | undefined
}
: never)
| (entryPointVersion extends '0.8'
? {
preVerificationGas: uint256
verificationGasLimit: uint256
callGasLimit: uint256
paymasterVerificationGasLimit?: uint256 | undefined
paymasterPostOpGasLimit?: uint256 | undefined
}
: never)
| (entryPointVersion extends '0.7'
? {
preVerificationGas: uint256
verificationGasLimit: uint256
callGasLimit: uint256
paymasterVerificationGasLimit?: uint256 | undefined
paymasterPostOpGasLimit?: uint256 | undefined
}
: never)
| (entryPointVersion extends '0.6'
? {
preVerificationGas: uint256
verificationGasLimit: uint256
callGasLimit: uint256
}
: never)
>
/** @link https://eips.ethereum.org/EIPS/eip-4337#-eth_getuseroperationbyhash */
export type GetUserOperationByHashReturnType<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
uint256 = bigint,
uint32 = number,
> = {
blockHash: Hash
blockNumber: uint256
entryPoint: Address
transactionHash: Hash
userOperation: UserOperation<entryPointVersion, uint256, uint32>
}
/** @link https://eips.ethereum.org/EIPS/eip-4337#entrypoint-definition */
export type PackedUserOperation = {
/** Concatenation of {@link UserOperation`verificationGasLimit`} (16 bytes) and {@link UserOperation`callGasLimit`} (16 bytes) */
accountGasLimits: Hex
/** The data to pass to the `sender` during the main execution call. */
callData: Hex
/** Concatenation of {@link UserOperation`factory`} and {@link UserOperation`factoryData`}. */
initCode: Hex
/** Concatenation of {@link UserOperation`maxPriorityFee`} (16 bytes) and {@link UserOperation`maxFeePerGas`} (16 bytes) */
gasFees: Hex
/** Anti-replay parameter. */
nonce: bigint
/** Concatenation of paymaster fields (or empty). */
paymasterAndData: Hex
/** Extra gas to pay the Bundler. */
preVerificationGas: bigint
/** The account making the operation. */
sender: Address
/** Data passed into the account to verify authorization. */
signature: Hex
}
/** @link https://eips.ethereum.org/EIPS/eip-4337#useroperation */
export type UserOperation<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
uint256 = bigint,
uint32 = number,
> = OneOf<
| (entryPointVersion extends '0.9'
? {
/** Authorization data. */
authorization?: SignedAuthorization<uint32> | undefined
/** The data to pass to the `sender` during the main execution call. */
callData: Hex
/** The amount of gas to allocate the main execution call */
callGasLimit: uint256
/** Account factory. Only for new accounts. */
factory?: Address | undefined
/** Data for account factory. */
factoryData?: Hex | undefined
/** Maximum fee per gas. */
maxFeePerGas: uint256
/** Maximum priority fee per gas. */
maxPriorityFeePerGas: uint256
/** Anti-replay parameter. */
nonce: uint256
/** Address of paymaster contract. */
paymaster?: Address | undefined
/** Data for paymaster. */
paymasterData?: Hex | undefined
/** The amount of gas to allocate for the paymaster post-operation code. */
paymasterPostOpGasLimit?: uint256 | undefined
/** Paymaster signature. Can be provided separately for parallelizable signing. */
paymasterSignature?: Hex | undefined
/** The amount of gas to allocate for the paymaster validation code. */
paymasterVerificationGasLimit?: uint256 | undefined
/** Extra gas to pay the Bundler. */
preVerificationGas: uint256
/** The account making the operation. */
sender: Address
/** Data passed into the account to verify authorization. */
signature: Hex
/** The amount of gas to allocate for the verification step. */
verificationGasLimit: uint256
}
: never)
| (entryPointVersion extends '0.8'
? {
/** Authorization data. */
authorization?: SignedAuthorization<uint32> | undefined
/** The data to pass to the `sender` during the main execution call. */
callData: Hex
/** The amount of gas to allocate the main execution call */
callGasLimit: uint256
/** Account factory. Only for new accounts. */
factory?: Address | undefined
/** Data for account factory. */
factoryData?: Hex | undefined
/** Maximum fee per gas. */
maxFeePerGas: uint256
/** Maximum priority fee per gas. */
maxPriorityFeePerGas: uint256
/** Anti-replay parameter. */
nonce: uint256
/** Address of paymaster contract. */
paymaster?: Address | undefined
/** Data for paymaster. */
paymasterData?: Hex | undefined
/** The amount of gas to allocate for the paymaster post-operation code. */
paymasterPostOpGasLimit?: uint256 | undefined
/** The amount of gas to allocate for the paymaster validation code. */
paymasterVerificationGasLimit?: uint256 | undefined
/** Extra gas to pay the Bundler. */
preVerificationGas: uint256
/** The account making the operation. */
sender: Address
/** Data passed into the account to verify authorization. */
signature: Hex
/** The amount of gas to allocate for the verification step. */
verificationGasLimit: uint256
}
: never)
| (entryPointVersion extends '0.7'
? {
/** Authorization data. */
authorization?: SignedAuthorization<uint32> | undefined
/** The data to pass to the `sender` during the main execution call. */
callData: Hex
/** The amount of gas to allocate the main execution call */
callGasLimit: uint256
/** Account factory. Only for new accounts. */
factory?: Address | undefined
/** Data for account factory. */
factoryData?: Hex | undefined
/** Maximum fee per gas. */
maxFeePerGas: uint256
/** Maximum priority fee per gas. */
maxPriorityFeePerGas: uint256
/** Anti-replay parameter. */
nonce: uint256
/** Address of paymaster contract. */
paymaster?: Address | undefined
/** Data for paymaster. */
paymasterData?: Hex | undefined
/** The amount of gas to allocate for the paymaster post-operation code. */
paymasterPostOpGasLimit?: uint256 | undefined
/** The amount of gas to allocate for the paymaster validation code. */
paymasterVerificationGasLimit?: uint256 | undefined
/** Extra gas to pay the Bundler. */
preVerificationGas: uint256
/** The account making the operation. */
sender: Address
/** Data passed into the account to verify authorization. */
signature: Hex
/** The amount of gas to allocate for the verification step. */
verificationGasLimit: uint256
}
: never)
| (entryPointVersion extends '0.6'
? {
/** Authorization data. */
authorization?: SignedAuthorization<uint32> | undefined
/** The data to pass to the `sender` during the main execution call. */
callData: Hex
/** The amount of gas to allocate the main execution call */
callGasLimit: uint256
/** Account init code. Only for new accounts. */
initCode?: Hex | undefined
/** Maximum fee per gas. */
maxFeePerGas: uint256
/** Maximum priority fee per gas. */
maxPriorityFeePerGas: uint256
/** Anti-replay parameter. */
nonce: uint256
/** Paymaster address with calldata. */
paymasterAndData?: Hex | undefined
/** Extra gas to pay the Bundler. */
preVerificationGas: uint256
/** The account making the operation. */
sender: Address
/** Data passed into the account to verify authorization. */
signature: Hex
/** The amount of gas to allocate for the verification step. */
verificationGasLimit: uint256
}
: never)
>
export type UserOperationRequest<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
uint256 = bigint,
uint32 = number,
> = OneOf<
| (entryPointVersion extends '0.9'
? UnionPartialBy<
UserOperation<'0.9', uint256, uint32>,
// We are able to calculate these via `prepareUserOperation`.
| keyof EstimateUserOperationGasReturnType<'0.9'>
| 'callData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'sender'
| 'signature'
>
: never)
| (entryPointVersion extends '0.8'
? UnionPartialBy<
UserOperation<'0.8', uint256, uint32>,
// We are able to calculate these via `prepareUserOperation`.
| keyof EstimateUserOperationGasReturnType<'0.8'>
| 'callData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'sender'
| 'signature'
>
: never)
| (entryPointVersion extends '0.7'
? UnionPartialBy<
UserOperation<'0.7', uint256, uint32>,
// We are able to calculate these via `prepareUserOperation`.
| keyof EstimateUserOperationGasReturnType<'0.7'>
| 'callData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'sender'
| 'signature'
>
: never)
| (entryPointVersion extends '0.6'
? UnionPartialBy<
UserOperation<'0.6', uint256, uint32>,
// We are able to calculate these via `prepareUserOperation`.
| keyof EstimateUserOperationGasReturnType<'0.6'>
| 'callData'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'sender'
| 'signature'
>
: never)
>
/** @link https://eips.ethereum.org/EIPS/eip-4337#-eth_getuseroperationreceipt */
export type UserOperationReceipt<
_entryPointVersion extends EntryPointVersion = EntryPointVersion,
uint256 = bigint,
int32 = number,
status = 'success' | 'reverted',
> = {
/** Actual gas cost. */
actualGasCost: uint256
/** Actual gas used. */
actualGasUsed: uint256
/** Entrypoint address. */
entryPoint: Address
/** Logs emitted during execution. */
logs: Log<uint256, int32, false>[]
/** Anti-replay parameter. */
nonce: uint256
/** Paymaster for the user operation. */
paymaster?: Address | undefined
/** Revert reason, if unsuccessful. */
reason?: string | undefined
/** Transaction receipt of the user operation execution. */
receipt: TransactionReceipt<uint256, int32, status>
sender: Address
/** If the user operation execution was successful. */
success: boolean
/** Hash of the user operation. */
userOpHash: Hash
}

View File

@@ -0,0 +1,310 @@
import type { BaseError } from '../../../errors/base.js'
import type { ExactPartial } from '../../../types/utils.js'
import {
AccountNotDeployedError,
type AccountNotDeployedErrorType,
ExecutionRevertedError,
type ExecutionRevertedErrorType,
FailedToSendToBeneficiaryError,
type FailedToSendToBeneficiaryErrorType,
GasValuesOverflowError,
type GasValuesOverflowErrorType,
HandleOpsOutOfGasError,
type HandleOpsOutOfGasErrorType,
InitCodeFailedError,
type InitCodeFailedErrorType,
InitCodeMustCreateSenderError,
type InitCodeMustCreateSenderErrorType,
InitCodeMustReturnSenderError,
type InitCodeMustReturnSenderErrorType,
InsufficientPrefundError,
type InsufficientPrefundErrorType,
InternalCallOnlyError,
type InternalCallOnlyErrorType,
InvalidAccountNonceError,
type InvalidAccountNonceErrorType,
InvalidAggregatorError,
type InvalidAggregatorErrorType,
InvalidBeneficiaryError,
type InvalidBeneficiaryErrorType,
InvalidFieldsError,
type InvalidFieldsErrorType,
InvalidPaymasterAndDataError,
type InvalidPaymasterAndDataErrorType,
PaymasterDepositTooLowError,
type PaymasterDepositTooLowErrorType,
PaymasterFunctionRevertedError,
type PaymasterFunctionRevertedErrorType,
PaymasterNotDeployedError,
type PaymasterNotDeployedErrorType,
PaymasterPostOpFunctionRevertedError,
type PaymasterPostOpFunctionRevertedErrorType,
PaymasterRateLimitError,
type PaymasterRateLimitErrorType,
PaymasterStakeTooLowError,
type PaymasterStakeTooLowErrorType,
SenderAlreadyConstructedError,
type SenderAlreadyConstructedErrorType,
SignatureCheckFailedError,
type SignatureCheckFailedErrorType,
SmartAccountFunctionRevertedError,
type SmartAccountFunctionRevertedErrorType,
UnknownBundlerError,
type UnknownBundlerErrorType,
UnsupportedSignatureAggregatorError,
type UnsupportedSignatureAggregatorErrorType,
UserOperationExpiredError,
type UserOperationExpiredErrorType,
UserOperationOutOfTimeRangeError,
type UserOperationOutOfTimeRangeErrorType,
UserOperationPaymasterExpiredError,
type UserOperationPaymasterExpiredErrorType,
UserOperationPaymasterSignatureError,
type UserOperationPaymasterSignatureErrorType,
UserOperationRejectedByEntryPointError,
type UserOperationRejectedByEntryPointErrorType,
UserOperationRejectedByOpCodeError,
type UserOperationRejectedByOpCodeErrorType,
UserOperationRejectedByPaymasterError,
type UserOperationRejectedByPaymasterErrorType,
UserOperationSignatureError,
type UserOperationSignatureErrorType,
VerificationGasLimitExceededError,
type VerificationGasLimitExceededErrorType,
VerificationGasLimitTooLowError,
type VerificationGasLimitTooLowErrorType,
} from '../../errors/bundler.js'
import type { UserOperation } from '../../types/userOperation.js'
const bundlerErrors = [
ExecutionRevertedError,
InvalidFieldsError,
PaymasterDepositTooLowError,
PaymasterRateLimitError,
PaymasterStakeTooLowError,
SignatureCheckFailedError,
UnsupportedSignatureAggregatorError,
UserOperationOutOfTimeRangeError,
UserOperationRejectedByEntryPointError,
UserOperationRejectedByPaymasterError,
UserOperationRejectedByOpCodeError,
]
export type GetBundlerErrorParameters = ExactPartial<UserOperation>
export type GetBundlerErrorReturnType =
| AccountNotDeployedErrorType
| ExecutionRevertedErrorType
| FailedToSendToBeneficiaryErrorType
| GasValuesOverflowErrorType
| HandleOpsOutOfGasErrorType
| InitCodeFailedErrorType
| InitCodeMustCreateSenderErrorType
| InitCodeMustReturnSenderErrorType
| InsufficientPrefundErrorType
| InternalCallOnlyErrorType
| InvalidAccountNonceErrorType
| InvalidAggregatorErrorType
| InvalidBeneficiaryErrorType
| InvalidFieldsErrorType
| InvalidPaymasterAndDataErrorType
| PaymasterDepositTooLowErrorType
| PaymasterFunctionRevertedErrorType
| PaymasterNotDeployedErrorType
| PaymasterPostOpFunctionRevertedErrorType
| PaymasterRateLimitErrorType
| PaymasterStakeTooLowErrorType
| SignatureCheckFailedErrorType
| SenderAlreadyConstructedErrorType
| SmartAccountFunctionRevertedErrorType
| UnsupportedSignatureAggregatorErrorType
| UserOperationOutOfTimeRangeErrorType
| UserOperationRejectedByEntryPointErrorType
| UserOperationRejectedByOpCodeErrorType
| UserOperationRejectedByPaymasterErrorType
| UnknownBundlerErrorType
| UserOperationExpiredErrorType
| UserOperationPaymasterExpiredErrorType
| UserOperationPaymasterSignatureErrorType
| UserOperationSignatureErrorType
| VerificationGasLimitExceededErrorType
| VerificationGasLimitTooLowErrorType
export function getBundlerError(
err: BaseError,
args: GetBundlerErrorParameters,
): GetBundlerErrorReturnType {
const message = (err.details || '').toLowerCase()
if (AccountNotDeployedError.message.test(message))
return new AccountNotDeployedError({
cause: err,
}) as any
if (FailedToSendToBeneficiaryError.message.test(message))
return new FailedToSendToBeneficiaryError({
cause: err,
}) as any
if (GasValuesOverflowError.message.test(message))
return new GasValuesOverflowError({
cause: err,
}) as any
if (HandleOpsOutOfGasError.message.test(message))
return new HandleOpsOutOfGasError({
cause: err,
}) as any
if (InitCodeFailedError.message.test(message))
return new InitCodeFailedError({
cause: err,
factory: args.factory,
factoryData: args.factoryData,
initCode: args.initCode,
}) as any
if (InitCodeMustCreateSenderError.message.test(message))
return new InitCodeMustCreateSenderError({
cause: err,
factory: args.factory,
factoryData: args.factoryData,
initCode: args.initCode,
}) as any
if (InitCodeMustReturnSenderError.message.test(message))
return new InitCodeMustReturnSenderError({
cause: err,
factory: args.factory,
factoryData: args.factoryData,
initCode: args.initCode,
sender: args.sender,
}) as any
if (InsufficientPrefundError.message.test(message))
return new InsufficientPrefundError({
cause: err,
}) as any
if (InternalCallOnlyError.message.test(message))
return new InternalCallOnlyError({
cause: err,
}) as any
if (InvalidAccountNonceError.message.test(message))
return new InvalidAccountNonceError({
cause: err,
nonce: args.nonce,
}) as any
if (InvalidAggregatorError.message.test(message))
return new InvalidAggregatorError({
cause: err,
}) as any
if (InvalidBeneficiaryError.message.test(message))
return new InvalidBeneficiaryError({
cause: err,
}) as any
if (InvalidPaymasterAndDataError.message.test(message))
return new InvalidPaymasterAndDataError({
cause: err,
}) as any
if (PaymasterDepositTooLowError.message.test(message))
return new PaymasterDepositTooLowError({
cause: err,
}) as any
if (PaymasterFunctionRevertedError.message.test(message))
return new PaymasterFunctionRevertedError({
cause: err,
}) as any
if (PaymasterNotDeployedError.message.test(message))
return new PaymasterNotDeployedError({
cause: err,
}) as any
if (PaymasterPostOpFunctionRevertedError.message.test(message))
return new PaymasterPostOpFunctionRevertedError({
cause: err,
}) as any
if (SmartAccountFunctionRevertedError.message.test(message))
return new SmartAccountFunctionRevertedError({
cause: err,
}) as any
if (SenderAlreadyConstructedError.message.test(message))
return new SenderAlreadyConstructedError({
cause: err,
factory: args.factory,
factoryData: args.factoryData,
initCode: args.initCode,
}) as any
if (UserOperationExpiredError.message.test(message))
return new UserOperationExpiredError({
cause: err,
}) as any
if (UserOperationPaymasterExpiredError.message.test(message))
return new UserOperationPaymasterExpiredError({
cause: err,
}) as any
if (UserOperationPaymasterSignatureError.message.test(message))
return new UserOperationPaymasterSignatureError({
cause: err,
}) as any
if (UserOperationSignatureError.message.test(message))
return new UserOperationSignatureError({
cause: err,
}) as any
if (VerificationGasLimitExceededError.message.test(message))
return new VerificationGasLimitExceededError({
cause: err,
}) as any
if (VerificationGasLimitTooLowError.message.test(message))
return new VerificationGasLimitTooLowError({
cause: err,
}) as any
const error = err.walk((e) =>
bundlerErrors.some((error) => error.code === (e as { code: number }).code),
) as BaseError & { code: number; data: any }
if (error) {
if (error.code === ExecutionRevertedError.code)
return new ExecutionRevertedError({
cause: err,
data: error.data,
message: error.details,
}) as any
if (error.code === InvalidFieldsError.code)
return new InvalidFieldsError({
cause: err,
}) as any
if (error.code === PaymasterDepositTooLowError.code)
return new PaymasterDepositTooLowError({
cause: err,
}) as any
if (error.code === PaymasterRateLimitError.code)
return new PaymasterRateLimitError({
cause: err,
}) as any
if (error.code === PaymasterStakeTooLowError.code)
return new PaymasterStakeTooLowError({
cause: err,
}) as any
if (error.code === SignatureCheckFailedError.code)
return new SignatureCheckFailedError({
cause: err,
}) as any
if (error.code === UnsupportedSignatureAggregatorError.code)
return new UnsupportedSignatureAggregatorError({
cause: err,
}) as any
if (error.code === UserOperationOutOfTimeRangeError.code)
return new UserOperationOutOfTimeRangeError({
cause: err,
}) as any
if (error.code === UserOperationRejectedByEntryPointError.code)
return new UserOperationRejectedByEntryPointError({
cause: err,
}) as any
if (error.code === UserOperationRejectedByPaymasterError.code)
return new UserOperationRejectedByPaymasterError({
cause: err,
}) as any
if (error.code === UserOperationRejectedByOpCodeError.code)
return new UserOperationRejectedByOpCodeError({
cause: err,
}) as any
}
return new UnknownBundlerError({
cause: err,
}) as any
}

View File

@@ -0,0 +1,147 @@
import type { Abi, Address } from 'abitype'
import { BaseError } from '../../../errors/base.js'
import {
ContractFunctionExecutionError,
ContractFunctionRevertedError,
ContractFunctionZeroDataError,
} from '../../../errors/contract.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Call } from '../../../types/calls.js'
import type { Hex } from '../../../types/misc.js'
import { decodeErrorResult } from '../../../utils/abi/decodeErrorResult.js'
import type { GetContractErrorReturnType } from '../../../utils/errors/getContractError.js'
import { ExecutionRevertedError } from '../../errors/bundler.js'
import {
UserOperationExecutionError,
type UserOperationExecutionErrorType,
} from '../../errors/userOperation.js'
import type { UserOperation } from '../../types/userOperation.js'
import {
type GetBundlerErrorParameters,
getBundlerError,
} from './getBundlerError.js'
type GetNodeErrorReturnType = ErrorType
export type GetUserOperationErrorParameters = UserOperation & {
calls?: readonly unknown[] | undefined
docsPath?: string | undefined
}
export type GetUserOperationErrorReturnType<cause = ErrorType> = Omit<
UserOperationExecutionErrorType,
'cause'
> & { cause: cause | GetNodeErrorReturnType }
export type GetUserOperationErrorErrorType = ErrorType
export function getUserOperationError<err extends ErrorType<string>>(
err: err,
{ calls, docsPath, ...args }: GetUserOperationErrorParameters,
): GetUserOperationErrorReturnType<err> {
const cause = (() => {
const cause = getBundlerError(
err as {} as BaseError,
args as GetBundlerErrorParameters,
)
if (calls && cause instanceof ExecutionRevertedError) {
const revertData = getRevertData(cause)
const contractCalls = calls?.filter(
(call: any) => call.abi,
) as readonly Call[]
if (revertData && contractCalls.length > 0)
return getContractError({ calls: contractCalls, revertData })
}
return cause
})()
return new UserOperationExecutionError(cause, {
docsPath,
...args,
}) as GetUserOperationErrorReturnType<err>
}
/////////////////////////////////////////////////////////////////////////////////
function getRevertData(error: BaseError) {
let revertData: Hex | undefined
error.walk((e) => {
const error = e as any
if (
typeof error.data === 'string' ||
typeof error.data?.revertData === 'string' ||
(!(error instanceof BaseError) && typeof error.message === 'string')
) {
const match = (
error.data?.revertData ||
error.data ||
error.message
).match?.(/(0x[A-Za-z0-9]*)/)
if (match) {
revertData = match[1]
return true
}
}
return false
})
return revertData
}
function getContractError(parameters: {
calls: readonly Call[]
revertData: Hex
}) {
const { calls, revertData } = parameters
const { abi, functionName, args, to } = (() => {
const contractCalls = calls?.filter((call) =>
Boolean(call.abi),
) as readonly Call[]
if (contractCalls.length === 1) return contractCalls[0]
const compatContractCalls = contractCalls.filter((call) => {
try {
return Boolean(
decodeErrorResult({
abi: call.abi,
data: revertData,
}),
)
} catch {
return false
}
})
if (compatContractCalls.length === 1) return compatContractCalls[0]
return {
abi: [],
functionName: contractCalls.reduce(
(acc, call) => `${acc ? `${acc} | ` : ''}${call.functionName}`,
'',
),
args: undefined,
to: undefined,
}
})() as {
abi: Abi
functionName: string
args: unknown[]
to: Address
}
const cause = (() => {
if (revertData === '0x')
return new ContractFunctionZeroDataError({ functionName })
return new ContractFunctionRevertedError({
abi,
data: revertData,
functionName,
})
})()
return new ContractFunctionExecutionError(cause as BaseError, {
abi,
args,
contractAddress: to,
functionName,
}) as GetContractErrorReturnType
}

View File

@@ -0,0 +1,31 @@
import type { ErrorType } from '../../../errors/utils.js'
import type { RpcUserOperation } from '../../types/rpc.js'
import type { UserOperation } from '../../types/userOperation.js'
export type FormatUserOperationErrorType = ErrorType
export function formatUserOperation(parameters: RpcUserOperation) {
const userOperation = { ...parameters } as unknown as UserOperation
if (parameters.callGasLimit)
userOperation.callGasLimit = BigInt(parameters.callGasLimit)
if (parameters.maxFeePerGas)
userOperation.maxFeePerGas = BigInt(parameters.maxFeePerGas)
if (parameters.maxPriorityFeePerGas)
userOperation.maxPriorityFeePerGas = BigInt(parameters.maxPriorityFeePerGas)
if (parameters.nonce) userOperation.nonce = BigInt(parameters.nonce)
if (parameters.paymasterPostOpGasLimit)
userOperation.paymasterPostOpGasLimit = BigInt(
parameters.paymasterPostOpGasLimit,
)
if (parameters.paymasterVerificationGasLimit)
userOperation.paymasterVerificationGasLimit = BigInt(
parameters.paymasterVerificationGasLimit,
)
if (parameters.preVerificationGas)
userOperation.preVerificationGas = BigInt(parameters.preVerificationGas)
if (parameters.verificationGasLimit)
userOperation.verificationGasLimit = BigInt(parameters.verificationGasLimit)
return userOperation
}

View File

@@ -0,0 +1,26 @@
import type { ErrorType } from '../../../errors/utils.js'
import type { RpcEstimateUserOperationGasReturnType } from '../../types/rpc.js'
import type { EstimateUserOperationGasReturnType } from '../../types/userOperation.js'
export type FormatUserOperationGasErrorType = ErrorType
export function formatUserOperationGas(
parameters: RpcEstimateUserOperationGasReturnType,
): EstimateUserOperationGasReturnType {
const gas = {} as EstimateUserOperationGasReturnType
if (parameters.callGasLimit)
gas.callGasLimit = BigInt(parameters.callGasLimit)
if (parameters.preVerificationGas)
gas.preVerificationGas = BigInt(parameters.preVerificationGas)
if (parameters.verificationGasLimit)
gas.verificationGasLimit = BigInt(parameters.verificationGasLimit)
if (parameters.paymasterPostOpGasLimit)
gas.paymasterPostOpGasLimit = BigInt(parameters.paymasterPostOpGasLimit)
if (parameters.paymasterVerificationGasLimit)
gas.paymasterVerificationGasLimit = BigInt(
parameters.paymasterVerificationGasLimit,
)
return gas
}

View File

@@ -0,0 +1,24 @@
import type { ErrorType } from '../../../errors/utils.js'
import { formatLog } from '../../../utils/formatters/log.js'
import { formatTransactionReceipt } from '../../../utils/formatters/transactionReceipt.js'
import type { RpcUserOperationReceipt } from '../../types/rpc.js'
import type { UserOperationReceipt } from '../../types/userOperation.js'
export type FormatUserOperationReceiptErrorType = ErrorType
export function formatUserOperationReceipt(
parameters: RpcUserOperationReceipt,
) {
const receipt = { ...parameters } as unknown as UserOperationReceipt
if (parameters.actualGasCost)
receipt.actualGasCost = BigInt(parameters.actualGasCost)
if (parameters.actualGasUsed)
receipt.actualGasUsed = BigInt(parameters.actualGasUsed)
if (parameters.logs)
receipt.logs = parameters.logs.map((log) => formatLog(log)) as any
if (parameters.receipt)
receipt.receipt = formatTransactionReceipt(receipt.receipt as any)
return receipt
}

View File

@@ -0,0 +1,76 @@
import type { ErrorType } from '../../../errors/utils.js'
import type { SignedAuthorization } from '../../../types/authorization.js'
import type { ExactPartial } from '../../../types/utils.js'
import { numberToHex } from '../../../utils/encoding/toHex.js'
import { pad } from '../../../utils/index.js'
import type { RpcUserOperation } from '../../types/rpc.js'
import type { UserOperation } from '../../types/userOperation.js'
export type FormatUserOperationRequestErrorType = ErrorType
export function formatUserOperationRequest(
request: ExactPartial<UserOperation>,
) {
const rpcRequest = {} as RpcUserOperation
if (typeof request.callData !== 'undefined')
rpcRequest.callData = request.callData
if (typeof request.callGasLimit !== 'undefined')
rpcRequest.callGasLimit = numberToHex(request.callGasLimit)
if (typeof request.factory !== 'undefined')
rpcRequest.factory = request.factory
if (typeof request.factoryData !== 'undefined')
rpcRequest.factoryData = request.factoryData
if (typeof request.initCode !== 'undefined')
rpcRequest.initCode = request.initCode
if (typeof request.maxFeePerGas !== 'undefined')
rpcRequest.maxFeePerGas = numberToHex(request.maxFeePerGas)
if (typeof request.maxPriorityFeePerGas !== 'undefined')
rpcRequest.maxPriorityFeePerGas = numberToHex(request.maxPriorityFeePerGas)
if (typeof request.nonce !== 'undefined')
rpcRequest.nonce = numberToHex(request.nonce)
if (typeof request.paymaster !== 'undefined')
rpcRequest.paymaster = request.paymaster
if (typeof request.paymasterAndData !== 'undefined')
rpcRequest.paymasterAndData = request.paymasterAndData || '0x'
if (typeof request.paymasterData !== 'undefined')
rpcRequest.paymasterData = request.paymasterData
if (typeof request.paymasterPostOpGasLimit !== 'undefined')
rpcRequest.paymasterPostOpGasLimit = numberToHex(
request.paymasterPostOpGasLimit,
)
if (typeof request.paymasterSignature !== 'undefined')
rpcRequest.paymasterSignature = request.paymasterSignature
if (typeof request.paymasterVerificationGasLimit !== 'undefined')
rpcRequest.paymasterVerificationGasLimit = numberToHex(
request.paymasterVerificationGasLimit,
)
if (typeof request.preVerificationGas !== 'undefined')
rpcRequest.preVerificationGas = numberToHex(request.preVerificationGas)
if (typeof request.sender !== 'undefined') rpcRequest.sender = request.sender
if (typeof request.signature !== 'undefined')
rpcRequest.signature = request.signature
if (typeof request.verificationGasLimit !== 'undefined')
rpcRequest.verificationGasLimit = numberToHex(request.verificationGasLimit)
if (typeof request.authorization !== 'undefined')
rpcRequest.eip7702Auth = formatAuthorization(request.authorization)
return rpcRequest
}
function formatAuthorization(authorization: SignedAuthorization) {
return {
address: authorization.address,
chainId: numberToHex(authorization.chainId),
nonce: numberToHex(authorization.nonce),
r: authorization.r
? numberToHex(BigInt(authorization.r), { size: 32 })
: pad('0x', { size: 32 }),
s: authorization.s
? numberToHex(BigInt(authorization.s), { size: 32 })
: pad('0x', { size: 32 }),
yParity: authorization.yParity
? numberToHex(authorization.yParity, { size: 1 })
: pad('0x', { size: 32 }),
}
}

View File

@@ -0,0 +1,28 @@
import { concat } from '../../../utils/data/concat.js'
import type { UserOperation } from '../../types/userOperation.js'
export type GetInitCodeOptions = {
/** Prepare the init code for hashing. */
forHash?: boolean | undefined
}
export function getInitCode(
userOperation: Pick<
UserOperation,
'authorization' | 'factory' | 'factoryData'
>,
options: GetInitCodeOptions = {},
) {
const { forHash } = options
const { authorization, factory, factoryData } = userOperation
if (
forHash &&
(factory === '0x7702' ||
factory === '0x7702000000000000000000000000000000000000')
) {
if (!authorization) return '0x7702000000000000000000000000000000000000'
return concat([authorization.address, factoryData ?? '0x'])
}
if (!factory) return '0x'
return concat([factory, factoryData ?? '0x'])
}

View File

@@ -0,0 +1,130 @@
import type { Address } from 'abitype'
import type { Hash, Hex } from '../../../types/misc.js'
import { encodeAbiParameters } from '../../../utils/abi/encodeAbiParameters.js'
import { keccak256 } from '../../../utils/hash/keccak256.js'
import { hashTypedData } from '../../../utils/signature/hashTypedData.js'
import type { EntryPointVersion } from '../../types/entryPointVersion.js'
import type { UserOperation } from '../../types/userOperation.js'
import { getInitCode } from './getInitCode.js'
import { getUserOperationTypedData } from './getUserOperationTypedData.js'
import { toPackedUserOperation } from './toPackedUserOperation.js'
export type GetUserOperationHashParameters<
entryPointVersion extends EntryPointVersion = EntryPointVersion,
> = {
chainId: number
entryPointAddress: Address
entryPointVersion: entryPointVersion | EntryPointVersion
userOperation: UserOperation<entryPointVersion>
}
export type GetUserOperationHashReturnType = Hash
export function getUserOperationHash<
entryPointVersion extends EntryPointVersion,
>(
parameters: GetUserOperationHashParameters<entryPointVersion>,
): GetUserOperationHashReturnType {
const { chainId, entryPointAddress, entryPointVersion } = parameters
const userOperation = parameters.userOperation as UserOperation
const {
authorization,
callData = '0x',
callGasLimit,
maxFeePerGas,
maxPriorityFeePerGas,
nonce,
paymasterAndData = '0x',
preVerificationGas,
sender,
verificationGasLimit,
} = userOperation
if (entryPointVersion === '0.8' || entryPointVersion === '0.9')
return hashTypedData(
getUserOperationTypedData({
chainId,
entryPointAddress,
userOperation,
}),
)
const packedUserOp = (() => {
if (entryPointVersion === '0.6') {
const factory = userOperation.initCode?.slice(0, 42) as Hex
const factoryData = userOperation.initCode?.slice(42) as Hex | undefined
const initCode = getInitCode(
{
authorization,
factory,
factoryData,
},
{ forHash: true },
)
return encodeAbiParameters(
[
{ type: 'address' },
{ type: 'uint256' },
{ type: 'bytes32' },
{ type: 'bytes32' },
{ type: 'uint256' },
{ type: 'uint256' },
{ type: 'uint256' },
{ type: 'uint256' },
{ type: 'uint256' },
{ type: 'bytes32' },
],
[
sender,
nonce,
keccak256(initCode),
keccak256(callData),
callGasLimit,
verificationGasLimit,
preVerificationGas,
maxFeePerGas,
maxPriorityFeePerGas,
keccak256(paymasterAndData),
],
)
}
if (entryPointVersion === '0.7') {
const packedUserOp = toPackedUserOperation(userOperation, {
forHash: true,
})
return encodeAbiParameters(
[
{ type: 'address' },
{ type: 'uint256' },
{ type: 'bytes32' },
{ type: 'bytes32' },
{ type: 'bytes32' },
{ type: 'uint256' },
{ type: 'bytes32' },
{ type: 'bytes32' },
],
[
packedUserOp.sender,
packedUserOp.nonce,
keccak256(packedUserOp.initCode),
keccak256(packedUserOp.callData),
packedUserOp.accountGasLimits,
packedUserOp.preVerificationGas,
packedUserOp.gasFees,
keccak256(packedUserOp.paymasterAndData),
],
)
}
throw new Error(`entryPointVersion "${entryPointVersion}" not supported.`)
})()
return keccak256(
encodeAbiParameters(
[{ type: 'bytes32' }, { type: 'address' }, { type: 'uint256' }],
[keccak256(packedUserOp), entryPointAddress, BigInt(chainId)],
),
)
}

View File

@@ -0,0 +1,49 @@
import type { Address } from 'abitype'
import type { TypedDataDefinition } from '../../../types/typedData.js'
import type { UserOperation } from '../../types/userOperation.js'
import { toPackedUserOperation } from './toPackedUserOperation.js'
export type GetUserOperationTypedDataParameters = {
chainId: number
entryPointAddress: Address
userOperation: UserOperation<'0.8'> | UserOperation<'0.9'>
}
export type GetUserOperationTypedDataReturnType = TypedDataDefinition<
typeof types,
'PackedUserOperation'
>
const types = {
PackedUserOperation: [
{ 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' },
],
} as const
export function getUserOperationTypedData(
parameters: GetUserOperationTypedDataParameters,
): GetUserOperationTypedDataReturnType {
const { chainId, entryPointAddress, userOperation } = parameters
const packedUserOp = toPackedUserOperation(userOperation, { forHash: true })
return {
types,
primaryType: 'PackedUserOperation',
domain: {
name: 'ERC4337',
version: '1',
chainId,
verifyingContract: entryPointAddress,
},
message: packedUserOp,
}
}

View File

@@ -0,0 +1,88 @@
import { concat } from '../../../utils/data/concat.js'
import { pad } from '../../../utils/data/pad.js'
import { size } from '../../../utils/data/size.js'
import { numberToHex } from '../../../utils/index.js'
import type {
PackedUserOperation,
UserOperation,
} from '../../types/userOperation.js'
import { getInitCode } from './getInitCode.js'
/** Magic suffix for paymaster signature encoding (keccak256("PaymasterSignature")[:8]) */
const paymasterSignatureMagic = '0x22e325a297439656' as const
export type ToPackedUserOperationOptions = {
/** Prepare the packed user operation for hashing. */
forHash?: boolean | undefined
}
export function toPackedUserOperation(
userOperation: UserOperation,
options: ToPackedUserOperationOptions = {},
): PackedUserOperation {
const {
callGasLimit,
callData,
maxPriorityFeePerGas,
maxFeePerGas,
paymaster,
paymasterData,
paymasterPostOpGasLimit,
paymasterSignature,
paymasterVerificationGasLimit,
sender,
signature = '0x',
verificationGasLimit,
} = userOperation as UserOperation & { paymasterSignature?: string }
const accountGasLimits = concat([
pad(numberToHex(verificationGasLimit || 0n), { size: 16 }),
pad(numberToHex(callGasLimit || 0n), { size: 16 }),
])
const initCode = getInitCode(userOperation, options)
const gasFees = concat([
pad(numberToHex(maxPriorityFeePerGas || 0n), { size: 16 }),
pad(numberToHex(maxFeePerGas || 0n), { size: 16 }),
])
const nonce = userOperation.nonce ?? 0n
// For v0.9, paymasterSignature can be provided separately and appended after paymasterData.
// The encoding uses a magic suffix and length prefix as per ERC-4337 spec:
// - forHash: just append the magic (signature is not part of hash)
// - !forHash: append signature + length (2 bytes) + magic
const paymasterAndData = paymaster
? concat([
paymaster,
pad(numberToHex(paymasterVerificationGasLimit || 0n), {
size: 16,
}),
pad(numberToHex(paymasterPostOpGasLimit || 0n), {
size: 16,
}),
paymasterData || '0x',
...(paymasterSignature
? options.forHash
? [paymasterSignatureMagic]
: [
paymasterSignature as `0x${string}`,
pad(numberToHex(size(paymasterSignature)), { size: 2 }),
paymasterSignatureMagic,
]
: []),
])
: '0x'
const preVerificationGas = userOperation.preVerificationGas ?? 0n
return {
accountGasLimits,
callData,
initCode,
gasFees,
nonce,
paymasterAndData,
preVerificationGas,
sender,
signature,
}
}

View File

@@ -0,0 +1,3 @@
import { UserOperation } from 'ox/erc4337'
export const toUserOperation = UserOperation.from