permissionless
Version:
A utility library for working with ERC-4337
192 lines • 7.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getWebAuthnSignature = exports.concatSignatures = void 0;
exports.signUserOperation = signUserOperation;
const viem_1 = require("viem");
const index_js_1 = require("../../utils/index.js");
const ox_js_1 = require("../../utils/ox.js");
const toSafeSmartAccount_js_1 = require("./toSafeSmartAccount.js");
const concatSignatures = (signatures) => {
signatures.sort((left, right) => left.signer.toLowerCase().localeCompare(right.signer.toLowerCase()));
const SIGNATURE_LENGTH_BYTES = 65;
let signatureBytes = "0x";
let dynamicBytes = "";
for (const sig of signatures) {
if (sig.dynamic) {
const dynamicPartPosition = (signatures.length * SIGNATURE_LENGTH_BYTES +
dynamicBytes.length / 2)
.toString(16)
.padStart(64, "0");
const dynamicPartLength = (sig.data.slice(2).length / 2)
.toString(16)
.padStart(64, "0");
const staticSignature = `${sig.signer.slice(2).padStart(64, "0")}${dynamicPartPosition}00`;
const dynamicPartWithLength = `${dynamicPartLength}${sig.data.slice(2)}`;
signatureBytes += staticSignature;
dynamicBytes += dynamicPartWithLength;
}
else {
signatureBytes += sig.data.slice(2);
}
}
signatureBytes += dynamicBytes;
return signatureBytes;
};
exports.concatSignatures = concatSignatures;
const getWebAuthnSignature = async ({ owner, hash }) => {
const { signature: signatureData, webauthn } = await owner.sign({
hash
});
const { Signature } = await (0, ox_js_1.getOxExports)();
const signature = Signature.fromHex(signatureData);
const match = webauthn.clientDataJSON.match(/^\{"type":"webauthn.get","challenge":"[A-Za-z0-9\-_]{43}",(.*)\}$/);
if (!match) {
throw new Error("challenge not found in client data JSON");
}
const [, fields] = match;
return (0, viem_1.encodeAbiParameters)([
{ name: "authenticatorData", type: "bytes" },
{ name: "clientDataJSON", type: "string" },
{ name: "signature", type: "uint256[2]" }
], [
webauthn.authenticatorData,
fields,
[BigInt(signature.r), BigInt(signature.s)]
]);
};
exports.getWebAuthnSignature = getWebAuthnSignature;
async function signUserOperation(parameters) {
const { chainId, entryPoint, validAfter = 0, validUntil = 0, safe4337ModuleAddress: _safe4337ModuleAddress, version, owners, signatures: existingSignatures, account, ...userOperation } = parameters;
const { safe4337ModuleAddress } = (0, toSafeSmartAccount_js_1.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 = (0, viem_1.concatHex)([
userOperation.factory,
userOperation.factoryData
]);
}
if (!userOperation.sender) {
throw new Error("Sender is required");
}
message.paymasterAndData = (0, toSafeSmartAccount_js_1.getPaymasterAndData)({
...userOperation,
sender: userOperation.sender
});
}
const localOwner = (0, toSafeSmartAccount_js_1.isWebAuthnAccount)(account)
? account
: await (0, index_js_1.toOwner)({
owner: account
});
const signer = (0, toSafeSmartAccount_js_1.isWebAuthnAccount)(localOwner)
? parameters.safeWebAuthnSharedSignerAddress
: localOwner.address;
if (!signer) {
throw new Error("no signer found");
}
let unPackedSignatures = [];
if (existingSignatures) {
try {
const decoded = (0, viem_1.decodeAbiParameters)([
{
components: [
{ type: "address", name: "signer" },
{ type: "bytes", name: "data" },
{ type: "bool", name: "dynamic" }
],
name: "signatures",
type: "tuple[]"
}
], existingSignatures);
unPackedSignatures = decoded[0];
}
catch {
const decoded = (0, viem_1.decodeAbiParameters)([
{
components: [
{ type: "address", name: "signer" },
{ type: "bytes", name: "data" }
],
name: "signatures",
type: "tuple[]"
}
], existingSignatures);
unPackedSignatures = decoded[0].map((sig) => ({
...sig,
dynamic: false
}));
}
}
const signatures = [
...unPackedSignatures,
{
signer,
dynamic: (0, toSafeSmartAccount_js_1.isWebAuthnAccount)(localOwner),
data: await (async () => {
if ((0, toSafeSmartAccount_js_1.isWebAuthnAccount)(localOwner)) {
const safeHash = (0, viem_1.hashTypedData)({
domain: {
chainId,
verifyingContract: safe4337ModuleAddress
},
types: entryPoint.version === "0.6"
? toSafeSmartAccount_js_1.EIP712_SAFE_OPERATION_TYPE_V06
: toSafeSmartAccount_js_1.EIP712_SAFE_OPERATION_TYPE_V07,
primaryType: "SafeOp",
message: message
});
return (0, exports.getWebAuthnSignature)({
owner: localOwner,
hash: safeHash
});
}
return localOwner.signTypedData({
domain: {
chainId,
verifyingContract: safe4337ModuleAddress
},
types: entryPoint.version === "0.6"
? toSafeSmartAccount_js_1.EIP712_SAFE_OPERATION_TYPE_V06
: toSafeSmartAccount_js_1.EIP712_SAFE_OPERATION_TYPE_V07,
primaryType: "SafeOp",
message: message
});
})()
}
];
if (signatures.length !== owners.length) {
return (0, viem_1.encodeAbiParameters)([
{
components: [
{ type: "address", name: "signer" },
{ type: "bytes", name: "data" },
{ type: "bool", name: "dynamic" }
],
name: "signatures",
type: "tuple[]"
}
], [signatures]);
}
return (0, viem_1.encodePacked)(["uint48", "uint48", "bytes"], [validAfter, validUntil, (0, exports.concatSignatures)(signatures)]);
}
//# sourceMappingURL=signUserOperation.js.map