@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
JavaScript
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
};