From a26fc8e3c57070f08ee538940919d74f43f5540a Mon Sep 17 00:00:00 2001 From: AngeloAyranji Date: Tue, 4 Aug 2026 14:37:57 +0300 Subject: [PATCH 1/2] fix(executor): preserve 2-byte 0x7702 EIP-7702 marker when packing initCode --- .../utils/packedUserOpUtils.ts | 13 +++- .../unit/services/packedUserOpUtils.test.ts | 64 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 packages/executor/test/unit/services/packedUserOpUtils.test.ts diff --git a/packages/executor/src/services/EntryPointService/utils/packedUserOpUtils.ts b/packages/executor/src/services/EntryPointService/utils/packedUserOpUtils.ts index 0eef8034..e9076d3d 100644 --- a/packages/executor/src/services/EntryPointService/utils/packedUserOpUtils.ts +++ b/packages/executor/src/services/EntryPointService/utils/packedUserOpUtils.ts @@ -1,4 +1,7 @@ -import { AddressZero } from "@skandha/params/lib/index.js"; +import { + AddressZero, + INITCODE_EIP7702_MARKER, +} from "@skandha/params/lib/index.js"; import { UserOperation, PackedUserOperation, @@ -176,7 +179,13 @@ export function packUserOp(op: UserOperation): PackedUserOperation { op.paymasterData ); } - const factory = pad(op.factory ?? "0x", { dir: "right", size: 20 }); + // The EIP-7702 marker must stay 2 bytes: paymasters (e.g. Pimlico's singleton) + // sign over keccak256(initCode), so padding it to 20 bytes invalidates their + // signature and the op reverts with AA34 at bundle time. + const factory = + op.factory?.toLowerCase() === INITCODE_EIP7702_MARKER + ? INITCODE_EIP7702_MARKER + : pad(op.factory ?? "0x", { dir: "right", size: 20 }); return { sender: op.sender, nonce: BigInt(op.nonce), diff --git a/packages/executor/test/unit/services/packedUserOpUtils.test.ts b/packages/executor/test/unit/services/packedUserOpUtils.test.ts new file mode 100644 index 00000000..29a41adf --- /dev/null +++ b/packages/executor/test/unit/services/packedUserOpUtils.test.ts @@ -0,0 +1,64 @@ +import { describe, it, expect } from "vitest"; +import { INITCODE_EIP7702_MARKER } from "@skandha/params/lib"; +import { packUserOp } from "../../../src/services/EntryPointService/utils/packedUserOpUtils"; +import { UserOperation } from "@skandha/types/lib/contracts/UserOperation"; + +const baseOp: UserOperation = { + sender: "0x54E3d8E0F9800440581D6d82C7A85F1167090d98", + nonce: "0x01", + callData: "0x", + callGasLimit: "0x3e93f", + verificationGasLimit: "0x1aaf8", + preVerificationGas: "0x10a2b", + maxFeePerGas: "0xd59f80", + maxPriorityFeePerGas: "0x1e8480", + signature: "0x", +}; + +describe("packUserOp initCode packing", () => { + it("keeps the EIP-7702 marker 0x7702 unpadded so paymaster signatures over keccak256(initCode) stay valid", () => { + // Pimlico's singleton paymaster commits to keccak256(userOp.initCode) of the + // canonical 2-byte marker. Padding it to 20 bytes changes the digest and the + // op reverts with FailedOp("AA34 signature error") at bundle time. + const packed = packUserOp({ + ...baseOp, + factory: INITCODE_EIP7702_MARKER, + factoryData: "0x", + }); + expect(packed.initCode).toEqual(INITCODE_EIP7702_MARKER); + }); + + it("keeps the EIP-7702 marker unpadded regardless of prefix casing", () => { + const packed = packUserOp({ + ...baseOp, + factory: "0X7702" as `0x${string}`, + factoryData: "0x", + }); + expect(packed.initCode.toLowerCase()).toEqual(INITCODE_EIP7702_MARKER); + }); + + it("appends factoryData after the EIP-7702 marker", () => { + const packed = packUserOp({ + ...baseOp, + factory: INITCODE_EIP7702_MARKER, + factoryData: "0xdeadbeef", + }); + expect(packed.initCode).toEqual("0x7702deadbeef"); + }); + + it("still packs a real 20-byte factory address followed by factoryData", () => { + const packed = packUserOp({ + ...baseOp, + factory: "0x5803c076563C85799989d42Fc00292A8aE52fa9E", + factoryData: "0xcafe", + }); + expect(packed.initCode.toLowerCase()).toEqual( + "0x5803c076563c85799989d42fc00292a8ae52fa9ecafe" + ); + }); + + it("packs an empty initCode when there is no factory", () => { + const packed = packUserOp({ ...baseOp }); + expect(packed.initCode).toEqual("0x"); + }); +}); From 47eb24a752e89b7f01cd0a1ed2e2d51b0315d684 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 4 Aug 2026 17:44:47 +0300 Subject: [PATCH 2/2] chore: bump to 4.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30c41c80..46d23c16 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "root", "private": true, - "version": "4.0.2", + "version": "4.0.3", "engines": { "node": ">=18.0.0" },