@pafi-dev/issuer 0.7.6 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2283,15 +2283,18 @@ import {
2283
2283
  ENTRY_POINT_V08 as ENTRY_POINT_V082,
2284
2284
  buildDelegationUserOp,
2285
2285
  buildEip7702Authorization,
2286
+ computeUserOpHash as computeUserOpHash2,
2287
+ buildUserOpTypedData as buildUserOpTypedData2,
2286
2288
  getContractAddresses as getContractAddresses5,
2287
2289
  serializeUserOpToJsonRpc as serializeUserOpToJsonRpc2
2288
2290
  } from "@pafi-dev/core";
2291
+ import { getAddress as getAddress9 } from "viem";
2289
2292
  var DEFAULT_DELEGATE_GAS = {
2290
2293
  callGasLimit: 100000n,
2291
2294
  verificationGasLimit: 150000n,
2292
2295
  preVerificationGas: 50000n
2293
2296
  };
2294
- async function handleDelegateSubmit(params) {
2297
+ async function handleDelegatePrepare(params) {
2295
2298
  const { batchExecutor } = getContractAddresses5(params.chainId);
2296
2299
  const partial = buildDelegationUserOp({
2297
2300
  userAddress: params.userAddress,
@@ -2328,39 +2331,76 @@ async function handleDelegateSubmit(params) {
2328
2331
  onWarning: params.onWarning
2329
2332
  });
2330
2333
  const merged = {
2331
- ...userOp,
2332
- ...paymasterFields ?? {}
2334
+ sender: userOp.sender,
2335
+ nonce: userOp.nonce,
2336
+ callData: userOp.callData,
2337
+ callGasLimit: paymasterFields?.callGasLimit ?? userOp.callGasLimit,
2338
+ verificationGasLimit: paymasterFields?.verificationGasLimit ?? userOp.verificationGasLimit,
2339
+ preVerificationGas: paymasterFields?.preVerificationGas ?? userOp.preVerificationGas,
2340
+ maxFeePerGas: paymasterFields?.maxFeePerGas ?? userOp.maxFeePerGas,
2341
+ maxPriorityFeePerGas: paymasterFields?.maxPriorityFeePerGas ?? userOp.maxPriorityFeePerGas,
2342
+ paymaster: paymasterFields?.paymaster,
2343
+ paymasterVerificationGasLimit: paymasterFields?.paymasterVerificationGasLimit,
2344
+ paymasterPostOpGasLimit: paymasterFields?.paymasterPostOpGasLimit,
2345
+ paymasterData: paymasterFields?.paymasterData
2333
2346
  };
2334
- const userOpJson = serializeUserOpToJsonRpc2(
2347
+ const userOpHash = computeUserOpHash2(merged, params.chainId);
2348
+ const typed = buildUserOpTypedData2(merged, params.chainId);
2349
+ await params.store.save(
2350
+ params.lockId,
2335
2351
  {
2336
2352
  sender: merged.sender,
2337
- nonce: merged.nonce,
2353
+ nonce: merged.nonce.toString(10),
2338
2354
  callData: merged.callData,
2339
- callGasLimit: merged.callGasLimit,
2340
- verificationGasLimit: merged.verificationGasLimit,
2341
- preVerificationGas: merged.preVerificationGas,
2342
- maxFeePerGas: merged.maxFeePerGas,
2343
- maxPriorityFeePerGas: merged.maxPriorityFeePerGas,
2344
- paymaster: paymasterFields?.paymaster,
2345
- paymasterVerificationGasLimit: paymasterFields?.paymasterVerificationGasLimit,
2346
- paymasterPostOpGasLimit: paymasterFields?.paymasterPostOpGasLimit,
2347
- paymasterData: paymasterFields?.paymasterData
2355
+ callGasLimit: merged.callGasLimit.toString(10),
2356
+ verificationGasLimit: merged.verificationGasLimit.toString(10),
2357
+ preVerificationGas: merged.preVerificationGas.toString(10),
2358
+ maxFeePerGas: merged.maxFeePerGas.toString(10),
2359
+ maxPriorityFeePerGas: merged.maxPriorityFeePerGas.toString(10),
2360
+ ...merged.paymaster ? { paymaster: merged.paymaster } : {},
2361
+ ...merged.paymasterVerificationGasLimit ? {
2362
+ paymasterVerificationGasLimit: merged.paymasterVerificationGasLimit.toString(10)
2363
+ } : {},
2364
+ ...merged.paymasterPostOpGasLimit ? {
2365
+ paymasterPostOpGasLimit: merged.paymasterPostOpGasLimit.toString(10)
2366
+ } : {},
2367
+ ...merged.paymasterData ? { paymasterData: merged.paymasterData } : {},
2368
+ chainId: params.chainId,
2369
+ userOpHash,
2370
+ eip7702Auth: authorization
2348
2371
  },
2349
- // Delegation UserOp is submitted unsigned — the EIP-7702 authorization
2350
- // is the user's "consent"; no separate AA signature is needed.
2351
- "0x"
2372
+ params.ttlSeconds
2352
2373
  );
2374
+ return {
2375
+ lockId: params.lockId,
2376
+ userOpHash,
2377
+ typedData: serializeUserOpTypedData(typed),
2378
+ expiresInSeconds: params.ttlSeconds,
2379
+ isSponsored: !!paymasterFields
2380
+ };
2381
+ }
2382
+ async function handleDelegateSubmit(params) {
2383
+ const entry = await params.store.get(params.lockId);
2384
+ if (!entry) {
2385
+ throw new PendingUserOpNotFoundError(params.lockId);
2386
+ }
2387
+ if (getAddress9(entry.sender) !== getAddress9(params.authenticatedAddress)) {
2388
+ throw new PendingUserOpForbiddenError(params.lockId);
2389
+ }
2390
+ if (!entry.eip7702Auth) {
2391
+ throw new Error(
2392
+ `delegate entry ${params.lockId} missing eip7702Auth \u2014 prepare step did not run correctly`
2393
+ );
2394
+ }
2395
+ const userOpJson = serializeEntryToJsonRpc(entry, params.userOpSig, "sponsored");
2353
2396
  const result = await relayUserOp({
2354
2397
  client: params.pafiBackendClient,
2355
2398
  userOp: userOpJson,
2356
- entryPoint: ENTRY_POINT_V082,
2357
- eip7702Auth: authorization
2399
+ entryPoint: params.entryPoint ?? ENTRY_POINT_V082,
2400
+ eip7702Auth: entry.eip7702Auth
2358
2401
  });
2359
- return {
2360
- userOpHash: result.userOpHash,
2361
- isSponsored: !!paymasterFields,
2362
- authorization
2363
- };
2402
+ await params.store.delete(params.lockId);
2403
+ return { userOpHash: result.userOpHash };
2364
2404
  }
2365
2405
 
2366
2406
  // src/api/errorMapper.ts
@@ -2389,10 +2429,10 @@ function createSdkErrorMapper(factories) {
2389
2429
  }
2390
2430
 
2391
2431
  // src/api/issuerApiAdapter.ts
2392
- import { getAddress as getAddress9 } from "viem";
2432
+ import { randomUUID } from "crypto";
2433
+ import { getAddress as getAddress10 } from "viem";
2393
2434
  import {
2394
2435
  buildAndSignSponsorAuth,
2395
- computeAuthorizationHash,
2396
2436
  decodeBatchExecuteCalls as decodeBatchExecuteCalls3,
2397
2437
  encodeBatchExecute,
2398
2438
  ENTRY_POINT_V08 as ENTRY_POINT_V083,
@@ -2455,7 +2495,7 @@ var IssuerApiAdapter = class {
2455
2495
  async pools(authenticatedAddress, chainId, pointTokenAddress) {
2456
2496
  const result = await this.cfg.issuerService.api.handlePools(
2457
2497
  authenticatedAddress,
2458
- { chainId, pointTokenAddress: getAddress9(pointTokenAddress) }
2498
+ { chainId, pointTokenAddress: getAddress10(pointTokenAddress) }
2459
2499
  );
2460
2500
  return { pools: result.pools };
2461
2501
  }
@@ -2464,8 +2504,8 @@ var IssuerApiAdapter = class {
2464
2504
  authenticatedAddress,
2465
2505
  {
2466
2506
  chainId,
2467
- userAddress: getAddress9(userAddress),
2468
- pointTokenAddress: getAddress9(pointTokenAddress)
2507
+ userAddress: getAddress10(userAddress),
2508
+ pointTokenAddress: getAddress10(pointTokenAddress)
2469
2509
  }
2470
2510
  );
2471
2511
  return {
@@ -2487,7 +2527,7 @@ var IssuerApiAdapter = class {
2487
2527
  "ptClaimHandler",
2488
2528
  "claim"
2489
2529
  );
2490
- const pointTokenAddress = getAddress9(input.pointTokenAddress);
2530
+ const pointTokenAddress = getAddress10(input.pointTokenAddress);
2491
2531
  const result = await ptClaimHandler.handle({
2492
2532
  authenticatedAddress: input.authenticatedAddress,
2493
2533
  userAddress: input.authenticatedAddress,
@@ -2582,7 +2622,7 @@ var IssuerApiAdapter = class {
2582
2622
  "ptClaimHandler",
2583
2623
  "claimPrepare"
2584
2624
  );
2585
- const pointTokenAddress = getAddress9(input.pointTokenAddress);
2625
+ const pointTokenAddress = getAddress10(input.pointTokenAddress);
2586
2626
  const claimResult = await ptClaimHandler.handle({
2587
2627
  authenticatedAddress: input.authenticatedAddress,
2588
2628
  userAddress: input.authenticatedAddress,
@@ -2628,7 +2668,7 @@ var IssuerApiAdapter = class {
2628
2668
  }
2629
2669
  async redeemPrepare(input) {
2630
2670
  this.assertRedeemHandler();
2631
- const pointTokenAddress = getAddress9(input.pointTokenAddress);
2671
+ const pointTokenAddress = getAddress10(input.pointTokenAddress);
2632
2672
  const redeemResponse = await this.cfg.ptRedeemHandler.handle({
2633
2673
  userAddress: input.authenticatedAddress,
2634
2674
  authenticatedAddress: input.authenticatedAddress,
@@ -2693,45 +2733,73 @@ var IssuerApiAdapter = class {
2693
2733
  // ------------------------------ Delegate endpoints -----------------------
2694
2734
  async delegateStatus(authenticatedAddress, chainId) {
2695
2735
  const { batchExecutor } = getContractAddresses6(chainId);
2696
- const code = await this.cfg.provider.getCode({
2697
- address: authenticatedAddress
2698
- });
2699
- return {
2700
- isDelegated: parseEip7702DelegatedAddress2(code) !== null,
2701
- batchExecutorAddress: batchExecutor
2702
- };
2703
- }
2704
- async delegatePrepare(authenticatedAddress, chainId) {
2705
- const { batchExecutor } = getContractAddresses6(chainId);
2706
- const accountNonce = BigInt(
2707
- await this.cfg.provider.getTransactionCount({
2708
- address: authenticatedAddress
2736
+ const [code, nonce] = await Promise.all([
2737
+ this.cfg.provider.getCode({ address: authenticatedAddress }),
2738
+ this.cfg.provider.getTransactionCount({
2739
+ address: authenticatedAddress,
2740
+ blockTag: "pending"
2709
2741
  })
2710
- );
2711
- const authorizationHash = computeAuthorizationHash(
2712
- chainId,
2713
- batchExecutor,
2714
- accountNonce
2715
- );
2742
+ ]);
2716
2743
  return {
2717
- authorizationHash,
2718
- delegationNonce: accountNonce.toString(),
2744
+ isDelegated: parseEip7702DelegatedAddress2(code) !== null,
2719
2745
  batchExecutorAddress: batchExecutor,
2746
+ delegationNonce: nonce.toString(),
2720
2747
  chainId
2721
2748
  };
2722
2749
  }
2723
- async delegateSubmit(input) {
2750
+ /**
2751
+ * Build the delegation-anchor UserOp + obtain paymaster sponsorship
2752
+ * + persist as a pending entry. Mobile must:
2753
+ *
2754
+ * 1. Sign EIP-7702 authorization LOCALLY (Privy `signAuthorization`
2755
+ * with `{contractAddress: batchExecutorAddress, chainId,
2756
+ * nonce: delegationNonce}`) → 65-byte authSig hex.
2757
+ * 2. POST `/delegate/prepare` with `{ chainId, delegationNonce,
2758
+ * authSig }` → this method.
2759
+ * 3. Sign returned `userOpHash` LOCALLY (`signTypedData(typedData)`).
2760
+ * 4. POST `/delegate/submit` with `{ lockId, userOpSig }`.
2761
+ *
2762
+ * v0.7.7 — replaces single-shot delegateSubmit that tried to relay
2763
+ * a UserOp with empty `signature: "0x"` (Simple7702Account's
2764
+ * validateUserOp reverts `ECDSAInvalidSignatureLength` 0xfce698f7).
2765
+ */
2766
+ async delegatePrepare(authenticatedAddress, input) {
2767
+ const { batchExecutor } = getContractAddresses6(input.chainId);
2724
2768
  const fees = await this.cfg.provider.estimateFeesPerGas();
2725
- const result = await handleDelegateSubmit({
2726
- userAddress: input.authenticatedAddress,
2769
+ const lockId = randomUUID();
2770
+ const result = await handleDelegatePrepare({
2771
+ userAddress: authenticatedAddress,
2727
2772
  chainId: input.chainId,
2728
2773
  delegationNonce: input.delegationNonce,
2729
2774
  aaNonce: input.aaNonce,
2730
2775
  authSig: input.authSig,
2731
2776
  fees,
2777
+ lockId,
2778
+ store: this.cfg.pendingUserOpStore,
2779
+ ttlSeconds: 15 * 60,
2780
+ // 15min — match claim/redeem mobile lock duration
2732
2781
  pafiBackendClient: this.cfg.pafiBackendClient,
2733
2782
  onWarning: this.cfg.onWarning
2734
2783
  });
2784
+ return {
2785
+ lockId: result.lockId,
2786
+ userOpHash: result.userOpHash,
2787
+ typedData: result.typedData,
2788
+ expiresInSeconds: result.expiresInSeconds,
2789
+ isSponsored: result.isSponsored,
2790
+ delegationNonce: input.delegationNonce.toString(),
2791
+ batchExecutorAddress: batchExecutor,
2792
+ chainId: input.chainId
2793
+ };
2794
+ }
2795
+ async delegateSubmit(input) {
2796
+ const result = await handleDelegateSubmit({
2797
+ lockId: input.lockId,
2798
+ authenticatedAddress: input.authenticatedAddress,
2799
+ userOpSig: input.userOpSig,
2800
+ store: this.cfg.pendingUserOpStore,
2801
+ pafiBackendClient: this.cfg.pafiBackendClient
2802
+ });
2735
2803
  return { userOpHash: result.userOpHash };
2736
2804
  }
2737
2805
  // ------------------------------ Internal helpers -------------------------
@@ -3372,7 +3440,7 @@ var PafiBackendClient = class {
3372
3440
  };
3373
3441
 
3374
3442
  // src/config.ts
3375
- import { getAddress as getAddress10 } from "viem";
3443
+ import { getAddress as getAddress11 } from "viem";
3376
3444
  import { getContractAddresses as getContractAddresses7 } from "@pafi-dev/core";
3377
3445
  function createIssuerService(config) {
3378
3446
  if (!config.provider) {
@@ -3393,7 +3461,7 @@ function createIssuerService(config) {
3393
3461
  "createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
3394
3462
  );
3395
3463
  }
3396
- const tokenAddresses = rawAddresses.map((a) => getAddress10(a));
3464
+ const tokenAddresses = rawAddresses.map((a) => getAddress11(a));
3397
3465
  const ledger = config.ledger;
3398
3466
  const sessionStore = config.sessionStore ?? new MemorySessionStore();
3399
3467
  const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
@@ -3482,7 +3550,7 @@ function createIssuerService(config) {
3482
3550
  }
3483
3551
 
3484
3552
  // src/issuer-state/validator.ts
3485
- import { getAddress as getAddress11 } from "viem";
3553
+ import { getAddress as getAddress12 } from "viem";
3486
3554
  import {
3487
3555
  POINT_TOKEN_V2_ABI as POINT_TOKEN_V2_ABI3,
3488
3556
  issuerRegistryGetIssuerFlatAbi,
@@ -3513,7 +3581,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
3513
3581
  */
3514
3582
  invalidate(pointToken) {
3515
3583
  if (pointToken) {
3516
- const key = getAddress11(pointToken);
3584
+ const key = getAddress12(pointToken);
3517
3585
  this.pointTokenIssuerCache.delete(key);
3518
3586
  this.stateCache.delete(key);
3519
3587
  this.inflight.delete(key);
@@ -3528,7 +3596,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
3528
3596
  * The issuer field is set at `initialize()` and never changes.
3529
3597
  */
3530
3598
  async getIssuerAddressForPointToken(pointToken) {
3531
- const key = getAddress11(pointToken);
3599
+ const key = getAddress12(pointToken);
3532
3600
  const cached = this.pointTokenIssuerCache.get(key);
3533
3601
  if (cached) return cached;
3534
3602
  const issuer = await this.provider.readContract({
@@ -3536,15 +3604,15 @@ var IssuerStateValidator = class _IssuerStateValidator {
3536
3604
  abi: POINT_TOKEN_V2_ABI3,
3537
3605
  functionName: "issuer"
3538
3606
  });
3539
- this.pointTokenIssuerCache.set(key, getAddress11(issuer));
3540
- return getAddress11(issuer);
3607
+ this.pointTokenIssuerCache.set(key, getAddress12(issuer));
3608
+ return getAddress12(issuer);
3541
3609
  }
3542
3610
  /**
3543
3611
  * Read registry record + totalSupply, with 30s cache and in-flight
3544
3612
  * deduplication. Does NOT throw on inactive/missing — returns raw state.
3545
3613
  */
3546
3614
  async getIssuerState(pointToken) {
3547
- const tokenAddr = getAddress11(pointToken);
3615
+ const tokenAddr = getAddress12(pointToken);
3548
3616
  const now = Date.now();
3549
3617
  const cached = this.stateCache.get(tokenAddr);
3550
3618
  if (cached && cached.expiresAt > now) return cached.value;
@@ -3642,7 +3710,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
3642
3710
  };
3643
3711
 
3644
3712
  // src/index.ts
3645
- var PAFI_ISSUER_SDK_VERSION = true ? "0.7.6" : "dev";
3713
+ var PAFI_ISSUER_SDK_VERSION = true ? "0.7.8" : "dev";
3646
3714
  export {
3647
3715
  AdapterMisconfiguredError,
3648
3716
  AuthError,