Paymaster reverts userOps for accounts with passkey module

Hello,

I am trying to use the Biconomy Smart Account with a custom module for passkey validation (not mentioned in the docs, but found here). I am not using any SDK and have deployed the PasskeyRegistryModule.sol contract myself.

This is how I populate the userOp:

export const BiconomyInitAbi = [
  {
    inputs: [
      {
        internalType: "address",
        name: "handler",
        type: "address",
      },
      {
        internalType: "address",
        name: "moduleSetupContract",
        type: "address",
      },
      {
        internalType: "bytes",
        name: "moduleSetupData",
        type: "bytes",
      },
    ],
    name: "init",
    outputs: [
      {
        internalType: "address",
        name: "",
        type: "address",
      },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      {
        internalType: "uint256",
        name: "_pubKeyX",
        type: "uint256",
      },
      {
        internalType: "uint256",
        name: "_pubKeyY",
        type: "uint256",
      },
      {
        internalType: "string",
        name: "_keyId",
        type: "string",
      },
    ],
    name: "initForSmartAccount",
    outputs: [
      {
        internalType: "address",
        name: "",
        type: "address",
      },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
];

export const createAccountAbi = [
  {
    inputs: [
      {
        internalType: "address",
        name: "moduleSetupContract",
        type: "address",
      },
      {
        internalType: "bytes",
        name: "moduleSetupData",
        type: "bytes",
      },
      {
        internalType: "uint256",
        name: "index",
        type: "uint256",
      },
    ],
    name: "deployCounterFactualAccount",
    outputs: [
      {
        internalType: "address",
        name: "proxy",
        type: "address",
      },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
];

const getPasskeyInitData = () => {
  return encodeFunctionData({
    abi: BiconomyInitAbi,
    functionName: "initForSmartAccount",
    args: [_pubKeyX, _pubKeyY, _keyId],
  });
};

const getInitCode = () => {
  const passkeyInitData = getPasskeyInitData();
  const createAccountData = encodeFunctionData({
    abi: createAccountAbi,
    functionName: "deployCounterFactualAccount",
    args: [PASSKEY_MODULE_ADDRESS, passkeyInitData, INDEX],
  });
  return concatHex([FACTORY_ADDRESS, createAccountData as Hex]);
};

const getAccountAddress = () => {
  const passkeyInitData = getPasskeyInitData();
  const initialisationData = encodeFunctionData({
    abi: BiconomyInitAbi,
    functionName: "init",
    args: [FALLBACK_HANDLER_ADDRESS, PASSKEY_MODULE_ADDRESS, passkeyInitData],
  });
  const deploymentCode = encodePacked(
    ["bytes", "uint256"],
    [BICONOMY_PROXY_CREATION_CODE, hexToBigInt(ACCOUNT_V2_0_LOGIC)],
  );
  const salt = keccak256(
    encodePacked(
      ["bytes32", "uint256"],
      [keccak256(encodePacked(["bytes"], [initialisationData])), INDEX],
    ),
  );
  return getContractAddress({
    from: FACTORY_ADDRESS,
    salt,
    bytecode: deploymentCode,
    opcode: "CREATE2",
  });
};
const getDummySignature = () => {
  const dynamicPart = PASSKEY_MODULE_ADDRESS.substring(2).padEnd(40, "0");
  return `0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000${dynamicPart}000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000`;
};

I get the “-32500 AA23 reverted (or OOG)” error when trying to send the userOp to the Biconomy Paymaster. I am mostly confused by the dummy signature generation. I found this way to do it in the permissionless.js library, and it works for Biconomy Smart Accounts with the default ECDSA module. Am I doing something wrong, missing any steps, or is it simply not supposed to work this way? Thank you!

1 Like