UNPKG

@owlprotocol/contracts-account-abstraction

Version:

ERC4337 Account Abstraction support for deploying relevant smart contracts and interacting with UserOps.

207 lines (206 loc) 6.81 kB
import { bytesToHex, concatHex, encodeAbiParameters, getAddress, numberToHex, pad, slice, zeroAddress } from "viem"; function toPackedUserOperation(userOp) { const accountGasLimits = packAccountGasLimits(userOp.verificationGasLimit, userOp.callGasLimit); const gasFees = packAccountGasLimits(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas); let paymasterAndData = "0x"; if (userOp.paymaster && userOp.paymaster !== zeroAddress && userOp.paymasterVerificationGasLimit && userOp.paymasterPostOpGasLimit && userOp.paymasterData) { paymasterAndData = packPaymasterData( userOp.paymaster, userOp.paymasterVerificationGasLimit, userOp.paymasterPostOpGasLimit, userOp.paymasterData ); } const initCode = userOp.factory && userOp.factory != zeroAddress && userOp.factoryData ? concatHex([userOp.factory, userOp.factoryData]) : "0x"; return { sender: userOp.sender, nonce: BigInt(userOp.nonce), callData: userOp.callData, accountGasLimits, initCode, preVerificationGas: BigInt(userOp.preVerificationGas), gasFees, paymasterAndData, signature: userOp.signature }; } function toUserOperationEncoded(packedUserOperation) { const { factory, factoryData } = unpackInitCode(packedUserOperation.initCode); const { callGasLimit, verificationGasLimit } = unpackAccountGasLimits(packedUserOperation.accountGasLimits); const { maxFeePerGas, maxPriorityFeePerGas } = unpackGasLimits(packedUserOperation.gasFees); const { paymaster, paymasterVerificationGasLimit, paymasterPostOpGasLimit, paymasterData } = unpackPaymasterAndData( packedUserOperation.paymasterAndData ); return { sender: packedUserOperation.sender, nonce: numberToHex(packedUserOperation.nonce), factory: factory ?? zeroAddress, factoryData: factoryData ?? "0x", callData: packedUserOperation.callData, callGasLimit: numberToHex(callGasLimit), verificationGasLimit: numberToHex(verificationGasLimit), preVerificationGas: numberToHex(packedUserOperation.preVerificationGas), maxFeePerGas: numberToHex(maxFeePerGas), maxPriorityFeePerGas: numberToHex(maxPriorityFeePerGas), paymaster: paymaster ?? zeroAddress, paymasterVerificationGasLimit: numberToHex(paymasterVerificationGasLimit ?? 0n), paymasterPostOpGasLimit: numberToHex(paymasterPostOpGasLimit ?? 0n), paymasterData: paymasterData ?? "0x", signature: packedUserOperation.signature }; } function packedUserOperationToRandomDataUserOp(packedUserOperation) { return { sender: packedUserOperation.sender, nonce: BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), initCode: packedUserOperation.initCode, callData: packedUserOperation.callData, accountGasLimits: bytesToHex(new Uint8Array(32).fill(255)), preVerificationGas: BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), gasFees: bytesToHex(new Uint8Array(32).fill(255)), paymasterAndData: bytesToHex(new Uint8Array(packedUserOperation.paymasterAndData.length).fill(255)), signature: bytesToHex(new Uint8Array(packedUserOperation.signature.length).fill(255)) }; } function packUserOperation(packedUserOperation) { const randomDataUserOp = packedUserOperationToRandomDataUserOp(packedUserOperation); return encodeAbiParameters( [ { 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: "uint256", name: "accountGasLimits", type: "bytes32" }, { internalType: "uint256", name: "preVerificationGas", type: "uint256" }, { internalType: "uint256", name: "gasFees", type: "bytes32" }, { internalType: "bytes", name: "paymasterAndData", type: "bytes" }, { internalType: "bytes", name: "signature", type: "bytes" } ], [ randomDataUserOp.sender, randomDataUserOp.nonce, // need non zero bytes to get better estimations for preVerificationGas packedUserOperation.initCode, packedUserOperation.callData, randomDataUserOp.accountGasLimits, // need non zero bytes to get better estimations for preVerificationGas randomDataUserOp.preVerificationGas, // need non zero bytes to get better estimations for preVerificationGas randomDataUserOp.gasFees, // need non zero bytes to get better estimations for preVerificationGas randomDataUserOp.paymasterAndData, randomDataUserOp.signature ] ); } function packAccountGasLimits(verificationGasLimit, callGasLimit) { return concatHex([ pad(verificationGasLimit, { dir: "left", size: 16 }), pad(callGasLimit, { dir: "left", size: 16 }) ]); } function unpackInitCode(initCode) { if (initCode === "0x") { return { factory: null, factoryData: null }; } return { factory: getAddress(slice(initCode, 0, 20)), factoryData: slice(initCode, 20) }; } function unpackGasLimits(gasLimits) { return { maxPriorityFeePerGas: BigInt(slice(gasLimits, 0, 16)), maxFeePerGas: BigInt(slice(gasLimits, 16)) }; } function unpackPaymasterAndData(paymasterAndData) { if (paymasterAndData === "0x") { return { paymaster: null, paymasterVerificationGasLimit: null, paymasterPostOpGasLimit: null, paymasterData: null }; } return { paymaster: getAddress(slice(paymasterAndData, 0, 20)), paymasterVerificationGasLimit: BigInt(slice(paymasterAndData, 20, 36)), paymasterPostOpGasLimit: BigInt(slice(paymasterAndData, 36, 52)), paymasterData: slice(paymasterAndData, 52) }; } function packPaymasterData(paymaster, paymasterVerificationGasLimit, postOpGasLimit, paymasterData) { return concatHex([ pad(paymaster, { dir: "left", size: 20 }), pad(paymasterVerificationGasLimit, { dir: "left", size: 16 }), pad(postOpGasLimit, { dir: "left", size: 16 }), paymasterData ]); } function unpackAccountGasLimits(accountGasLimits) { return { verificationGasLimit: parseInt(accountGasLimits.slice(2, 34), 16), callGasLimit: parseInt(accountGasLimits.slice(34), 16) }; } export { packAccountGasLimits, packPaymasterData, packUserOperation, packedUserOperationToRandomDataUserOp, toPackedUserOperation, toUserOperationEncoded, unpackAccountGasLimits, unpackGasLimits, unpackInitCode, unpackPaymasterAndData };