permissionless
Version:
A utility library for working with ERC-4337
94 lines • 3.83 kB
JavaScript
import { concat, concatHex, decodeAbiParameters, encodeAbiParameters, encodePacked } from "viem";
import { toOwner } from "../../utils/index.js";
import { EIP712_SAFE_OPERATION_TYPE_V06, EIP712_SAFE_OPERATION_TYPE_V07, getDefaultAddresses, getPaymasterAndData } from "./toSafeSmartAccount.js";
export async function signUserOperation(parameters) {
const { chainId, entryPoint, validAfter = 0, validUntil = 0, safe4337ModuleAddress: _safe4337ModuleAddress, version, owners, signatures: existingSignatures, account, ...userOperation } = parameters;
const { safe4337ModuleAddress } = getDefaultAddresses(version, entryPoint.version, {
safe4337ModuleAddress: _safe4337ModuleAddress
});
const message = {
safe: userOperation.sender,
callData: userOperation.callData,
nonce: userOperation.nonce,
initCode: userOperation.initCode ?? "0x",
maxFeePerGas: userOperation.maxFeePerGas,
maxPriorityFeePerGas: userOperation.maxPriorityFeePerGas,
preVerificationGas: userOperation.preVerificationGas,
verificationGasLimit: userOperation.verificationGasLimit,
callGasLimit: userOperation.callGasLimit,
paymasterAndData: userOperation.paymasterAndData ?? "0x",
validAfter: validAfter,
validUntil: validUntil,
entryPoint: entryPoint.address
};
if ("initCode" in userOperation) {
message.paymasterAndData = userOperation.paymasterAndData ?? "0x";
}
if ("factory" in userOperation) {
if (userOperation.factory && userOperation.factoryData) {
message.initCode = concatHex([
userOperation.factory,
userOperation.factoryData
]);
}
if (!userOperation.sender) {
throw new Error("Sender is required");
}
message.paymasterAndData = getPaymasterAndData({
...userOperation,
sender: userOperation.sender
});
}
const localOwners = [
await toOwner({
owner: account
})
];
let unPackedSignatures = [];
if (existingSignatures) {
const decoded = decodeAbiParameters([
{
components: [
{ type: "address", name: "signer" },
{ type: "bytes", name: "data" }
],
name: "signatures",
type: "tuple[]"
}
], existingSignatures);
unPackedSignatures = decoded[0];
}
const signatures = [
...unPackedSignatures,
...(await Promise.all(localOwners.map(async (localOwner) => ({
signer: localOwner.address,
data: await localOwner.signTypedData({
domain: {
chainId,
verifyingContract: safe4337ModuleAddress
},
types: entryPoint.version === "0.6"
? EIP712_SAFE_OPERATION_TYPE_V06
: EIP712_SAFE_OPERATION_TYPE_V07,
primaryType: "SafeOp",
message: message
})
}))))
];
if (signatures.length !== owners.length) {
return encodeAbiParameters([
{
components: [
{ type: "address", name: "signer" },
{ type: "bytes", name: "data" }
],
name: "signatures",
type: "tuple[]"
}
], [signatures]);
}
signatures.sort((left, right) => left.signer.toLowerCase().localeCompare(right.signer.toLowerCase()));
const signatureBytes = concat(signatures.map((sig) => sig.data));
return encodePacked(["uint48", "uint48", "bytes"], [validAfter, validUntil, signatureBytes]);
}
//# sourceMappingURL=signUserOperation.js.map