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,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