@pafi-dev/issuer 0.5.30 → 0.5.32

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.d.cts CHANGED
@@ -369,6 +369,23 @@ declare class RelayError extends Error {
369
369
  constructor(code: RelayErrorCode, message: string, cause?: unknown);
370
370
  }
371
371
 
372
+ /**
373
+ * Optional config that lets `RelayService` auto-quote the operator fee
374
+ * + auto-resolve the recipient when callers don't pass `feeAmount` /
375
+ * `feeRecipient`. When both `provider` + `chainId` are set, callers can
376
+ * omit the fee params entirely; the service will:
377
+ *
378
+ * 1. Resolve `feeRecipient = getContractAddresses(chainId).pafiFeeRecipient`
379
+ * 2. If `feeAmount` is undefined → run `quoteOperatorFeePt(...)` against
380
+ * Chainlink + V4 subgraph to compute the PT amount.
381
+ *
382
+ * When unset, the legacy "caller passes feeAmount + feeRecipient or no
383
+ * fee" behavior applies, so existing integrations keep working.
384
+ */
385
+ interface RelayServiceConfig {
386
+ provider?: PublicClient;
387
+ chainId?: number;
388
+ }
372
389
  /**
373
390
  * Builds unsigned `PartialUserOperation` payloads for the v1.4 sponsored
374
391
  * flow. The service is stateless and HTTP-client-free:
@@ -385,6 +402,26 @@ declare class RelayError extends Error {
385
402
  * concerns moved to the Bundler + Paymaster in v1.4.
386
403
  */
387
404
  declare class RelayService {
405
+ private readonly provider;
406
+ private readonly chainId;
407
+ constructor(config?: RelayServiceConfig);
408
+ /**
409
+ * Resolve the fee recipient + amount applied to the next UserOp:
410
+ *
411
+ * - If caller passed an explicit `feeRecipient`, use it (testing
412
+ * only — sponsor-relayer's L1 will reject any non-canonical
413
+ * recipient with `INSUFFICIENT_FEE`). Otherwise, default to
414
+ * `getContractAddresses(chainId).pafiFeeRecipient` when the
415
+ * service has a `chainId` configured.
416
+ * - If caller passed `feeAmount`, use it. Otherwise, when the
417
+ * service has both `provider` + `chainId`, auto-quote via
418
+ * `quoteOperatorFeePt`.
419
+ * - When the service is unconfigured AND caller passed nothing,
420
+ * return `{ feeAmount: 0n, feeRecipient: undefined }` — legacy
421
+ * "no fee" behavior, caller must opt in for the gas-reimbursement
422
+ * transfer to be added to the batch.
423
+ */
424
+ private resolveFee;
388
425
  /**
389
426
  * Build an unsigned UserOp for Scenario 1 (Mint) — sig-gated
390
427
  * `PointToken.mint(to, amount, deadline, minterSig)`.
@@ -401,7 +438,7 @@ declare class RelayService {
401
438
  * burnerSig)`. Caller provides a pre-signed `BurnRequest` + sig
402
439
  * bytes (typically from `PTRedeemHandler`).
403
440
  */
404
- prepareBurn(params: PrepareBurnParams): PartialUserOperation;
441
+ prepareBurn(params: PrepareBurnParams): Promise<PartialUserOperation>;
405
442
  }
406
443
  /**
407
444
  * v1.4 — sig-gated `PointToken.mint(to, amount, deadline, minterSig)`.
package/dist/index.d.ts CHANGED
@@ -369,6 +369,23 @@ declare class RelayError extends Error {
369
369
  constructor(code: RelayErrorCode, message: string, cause?: unknown);
370
370
  }
371
371
 
372
+ /**
373
+ * Optional config that lets `RelayService` auto-quote the operator fee
374
+ * + auto-resolve the recipient when callers don't pass `feeAmount` /
375
+ * `feeRecipient`. When both `provider` + `chainId` are set, callers can
376
+ * omit the fee params entirely; the service will:
377
+ *
378
+ * 1. Resolve `feeRecipient = getContractAddresses(chainId).pafiFeeRecipient`
379
+ * 2. If `feeAmount` is undefined → run `quoteOperatorFeePt(...)` against
380
+ * Chainlink + V4 subgraph to compute the PT amount.
381
+ *
382
+ * When unset, the legacy "caller passes feeAmount + feeRecipient or no
383
+ * fee" behavior applies, so existing integrations keep working.
384
+ */
385
+ interface RelayServiceConfig {
386
+ provider?: PublicClient;
387
+ chainId?: number;
388
+ }
372
389
  /**
373
390
  * Builds unsigned `PartialUserOperation` payloads for the v1.4 sponsored
374
391
  * flow. The service is stateless and HTTP-client-free:
@@ -385,6 +402,26 @@ declare class RelayError extends Error {
385
402
  * concerns moved to the Bundler + Paymaster in v1.4.
386
403
  */
387
404
  declare class RelayService {
405
+ private readonly provider;
406
+ private readonly chainId;
407
+ constructor(config?: RelayServiceConfig);
408
+ /**
409
+ * Resolve the fee recipient + amount applied to the next UserOp:
410
+ *
411
+ * - If caller passed an explicit `feeRecipient`, use it (testing
412
+ * only — sponsor-relayer's L1 will reject any non-canonical
413
+ * recipient with `INSUFFICIENT_FEE`). Otherwise, default to
414
+ * `getContractAddresses(chainId).pafiFeeRecipient` when the
415
+ * service has a `chainId` configured.
416
+ * - If caller passed `feeAmount`, use it. Otherwise, when the
417
+ * service has both `provider` + `chainId`, auto-quote via
418
+ * `quoteOperatorFeePt`.
419
+ * - When the service is unconfigured AND caller passed nothing,
420
+ * return `{ feeAmount: 0n, feeRecipient: undefined }` — legacy
421
+ * "no fee" behavior, caller must opt in for the gas-reimbursement
422
+ * transfer to be added to the batch.
423
+ */
424
+ private resolveFee;
388
425
  /**
389
426
  * Build an unsigned UserOp for Scenario 1 (Mint) — sig-gated
390
427
  * `PointToken.mint(to, amount, deadline, minterSig)`.
@@ -401,7 +438,7 @@ declare class RelayService {
401
438
  * burnerSig)`. Caller provides a pre-signed `BurnRequest` + sig
402
439
  * bytes (typically from `PTRedeemHandler`).
403
440
  */
404
- prepareBurn(params: PrepareBurnParams): PartialUserOperation;
441
+ prepareBurn(params: PrepareBurnParams): Promise<PartialUserOperation>;
405
442
  }
406
443
  /**
407
444
  * v1.4 — sig-gated `PointToken.mint(to, amount, deadline, minterSig)`.
package/dist/index.js CHANGED
@@ -388,9 +388,48 @@ import {
388
388
  import {
389
389
  POINT_TOKEN_V2_ABI,
390
390
  buildPartialUserOperation,
391
- signMintRequest
391
+ signMintRequest,
392
+ getContractAddresses,
393
+ quoteOperatorFeePt
392
394
  } from "@pafi-dev/core";
393
395
  var RelayService = class {
396
+ provider;
397
+ chainId;
398
+ constructor(config = {}) {
399
+ this.provider = config.provider;
400
+ this.chainId = config.chainId;
401
+ }
402
+ /**
403
+ * Resolve the fee recipient + amount applied to the next UserOp:
404
+ *
405
+ * - If caller passed an explicit `feeRecipient`, use it (testing
406
+ * only — sponsor-relayer's L1 will reject any non-canonical
407
+ * recipient with `INSUFFICIENT_FEE`). Otherwise, default to
408
+ * `getContractAddresses(chainId).pafiFeeRecipient` when the
409
+ * service has a `chainId` configured.
410
+ * - If caller passed `feeAmount`, use it. Otherwise, when the
411
+ * service has both `provider` + `chainId`, auto-quote via
412
+ * `quoteOperatorFeePt`.
413
+ * - When the service is unconfigured AND caller passed nothing,
414
+ * return `{ feeAmount: 0n, feeRecipient: undefined }` — legacy
415
+ * "no fee" behavior, caller must opt in for the gas-reimbursement
416
+ * transfer to be added to the batch.
417
+ */
418
+ async resolveFee(params) {
419
+ const feeRecipient = params.feeRecipient ?? (this.chainId !== void 0 ? getContractAddresses(this.chainId).pafiFeeRecipient : void 0);
420
+ if (params.feeAmount !== void 0) {
421
+ return { feeAmount: params.feeAmount, feeRecipient };
422
+ }
423
+ if (this.provider && this.chainId !== void 0) {
424
+ const feeAmount = await quoteOperatorFeePt({
425
+ provider: this.provider,
426
+ chainId: this.chainId,
427
+ pointTokenAddress: params.pointTokenAddress
428
+ });
429
+ return { feeAmount, feeRecipient };
430
+ }
431
+ return { feeAmount: 0n, feeRecipient };
432
+ }
394
433
  /**
395
434
  * Build an unsigned UserOp for Scenario 1 (Mint) — sig-gated
396
435
  * `PointToken.mint(to, amount, deadline, minterSig)`.
@@ -475,14 +514,19 @@ var RelayService = class {
475
514
  data: mintCallData
476
515
  }
477
516
  ];
478
- if (params.feeAmount && params.feeAmount > 0n) {
479
- if (!params.feeRecipient) {
517
+ const { feeAmount, feeRecipient } = await this.resolveFee({
518
+ feeAmount: params.feeAmount,
519
+ feeRecipient: params.feeRecipient,
520
+ pointTokenAddress: params.pointTokenAddress
521
+ });
522
+ if (feeAmount > 0n) {
523
+ if (!feeRecipient) {
480
524
  throw new RelayError(
481
525
  "ENCODE_FAILED",
482
- "prepareMint: feeRecipient required when feeAmount > 0"
526
+ "prepareMint: feeRecipient could not be resolved \u2014 pass `feeRecipient` explicitly or construct RelayService with a `chainId`."
483
527
  );
484
528
  }
485
- if (params.feeRecipient === "0x0000000000000000000000000000000000000000") {
529
+ if (feeRecipient === "0x0000000000000000000000000000000000000000") {
486
530
  throw new RelayError(
487
531
  "ENCODE_FAILED",
488
532
  "prepareMint: feeRecipient must not be zero address"
@@ -494,7 +538,7 @@ var RelayService = class {
494
538
  data: encodeFunctionData({
495
539
  abi: erc20Abi,
496
540
  functionName: "transfer",
497
- args: [params.feeRecipient, params.feeAmount]
541
+ args: [feeRecipient, feeAmount]
498
542
  })
499
543
  });
500
544
  }
@@ -520,7 +564,7 @@ var RelayService = class {
520
564
  * burnerSig)`. Caller provides a pre-signed `BurnRequest` + sig
521
565
  * bytes (typically from `PTRedeemHandler`).
522
566
  */
523
- prepareBurn(params) {
567
+ async prepareBurn(params) {
524
568
  if (!params.pointTokenAddress) {
525
569
  throw new RelayError("ENCODE_FAILED", "prepareBurn: pointTokenAddress required");
526
570
  }
@@ -561,14 +605,19 @@ var RelayService = class {
561
605
  );
562
606
  }
563
607
  const operations = [];
564
- if (params.feeAmount && params.feeAmount > 0n) {
565
- if (!params.feeRecipient) {
608
+ const { feeAmount, feeRecipient } = await this.resolveFee({
609
+ feeAmount: params.feeAmount,
610
+ feeRecipient: params.feeRecipient,
611
+ pointTokenAddress: params.pointTokenAddress
612
+ });
613
+ if (feeAmount > 0n) {
614
+ if (!feeRecipient) {
566
615
  throw new RelayError(
567
616
  "ENCODE_FAILED",
568
- "prepareBurn: feeRecipient required when feeAmount > 0"
617
+ "prepareBurn: feeRecipient could not be resolved \u2014 pass `feeRecipient` explicitly or construct RelayService with a `chainId`."
569
618
  );
570
619
  }
571
- if (params.feeRecipient === "0x0000000000000000000000000000000000000000") {
620
+ if (feeRecipient === "0x0000000000000000000000000000000000000000") {
572
621
  throw new RelayError(
573
622
  "ENCODE_FAILED",
574
623
  "prepareBurn: feeRecipient must not be zero address"
@@ -580,7 +629,7 @@ var RelayService = class {
580
629
  data: encodeFunctionData({
581
630
  abi: erc20Abi,
582
631
  functionName: "transfer",
583
- args: [params.feeRecipient, params.feeAmount]
632
+ args: [feeRecipient, feeAmount]
584
633
  })
585
634
  });
586
635
  }
@@ -1420,7 +1469,7 @@ var PTRedeemHandler = class {
1420
1469
  this.redeemLockDurationMs,
1421
1470
  this.pointTokenAddress
1422
1471
  );
1423
- const sponsoredUserOp = this.relayService.prepareBurn({
1472
+ const sponsoredUserOp = await this.relayService.prepareBurn({
1424
1473
  mode: "burnWithSig",
1425
1474
  userAddress: request.userAddress,
1426
1475
  aaNonce: request.aaNonce,
@@ -1454,15 +1503,19 @@ var PTRedeemHandler = class {
1454
1503
  this.redeemLockDurationMs,
1455
1504
  this.pointTokenAddress
1456
1505
  );
1457
- const fallbackUserOp = this.relayService.prepareBurn({
1506
+ const fallbackUserOp = await this.relayService.prepareBurn({
1458
1507
  mode: "burnWithSig",
1459
1508
  userAddress: request.userAddress,
1460
1509
  aaNonce: request.aaNonce,
1461
1510
  pointTokenAddress: this.pointTokenAddress,
1462
1511
  batchExecutorAddress: this.batchExecutorAddress,
1463
1512
  burnRequest: fallbackBurnRequest,
1464
- burnerSignature: fallbackSig
1465
- // No feeAmount/feeRecipient — fallback is fee-free.
1513
+ burnerSignature: fallbackSig,
1514
+ // Explicit 0n — fallback is fee-free regardless of how
1515
+ // RelayService is configured. Without this, an
1516
+ // auto-quoting RelayService would try to quote a fee here
1517
+ // and re-add the PT.transfer we're trying to strip.
1518
+ feeAmount: 0n
1466
1519
  });
1467
1520
  fallback = {
1468
1521
  lockId: fallbackLockId,
@@ -2164,7 +2217,7 @@ var PafiBackendClient = class {
2164
2217
 
2165
2218
  // src/config.ts
2166
2219
  import { getAddress as getAddress8 } from "viem";
2167
- import { getContractAddresses } from "@pafi-dev/core";
2220
+ import { getContractAddresses as getContractAddresses2 } from "@pafi-dev/core";
2168
2221
  function createIssuerService(config) {
2169
2222
  if (!config.provider) {
2170
2223
  throw new Error("createIssuerService: provider is required");
@@ -2198,7 +2251,10 @@ function createIssuerService(config) {
2198
2251
  authServiceConfig.jwtExpiresIn = config.auth.jwtExpiresIn;
2199
2252
  }
2200
2253
  const authService = new AuthService(authServiceConfig);
2201
- const relayService = new RelayService();
2254
+ const relayService = new RelayService({
2255
+ provider: config.provider,
2256
+ chainId: config.chainId
2257
+ });
2202
2258
  let feeManager;
2203
2259
  if (config.fee) {
2204
2260
  feeManager = new FeeManager({
@@ -2231,7 +2287,7 @@ function createIssuerService(config) {
2231
2287
  indexers.set(tokenAddress, new PointIndexer(indexerConfig));
2232
2288
  }
2233
2289
  const firstIndexer = indexers.get(tokenAddresses[0]);
2234
- const chainAddresses = getContractAddresses(config.chainId);
2290
+ const chainAddresses = getContractAddresses2(config.chainId);
2235
2291
  const resolvedContracts = {
2236
2292
  batchExecutor: chainAddresses.batchExecutor,
2237
2293
  usdt: chainAddresses.usdt,
@@ -2326,7 +2382,7 @@ import { getAddress as getAddress9 } from "viem";
2326
2382
  import {
2327
2383
  POINT_TOKEN_V2_ABI as POINT_TOKEN_V2_ABI3,
2328
2384
  issuerRegistryGetIssuerFlatAbi,
2329
- getContractAddresses as getContractAddresses2
2385
+ getContractAddresses as getContractAddresses3
2330
2386
  } from "@pafi-dev/core";
2331
2387
 
2332
2388
  // src/issuer-state/types.ts
@@ -2358,7 +2414,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
2358
2414
  * `CONTRACT_ADDRESSES` map for the given chain.
2359
2415
  */
2360
2416
  static forChain(provider, chainId) {
2361
- const { issuerRegistry } = getContractAddresses2(chainId);
2417
+ const { issuerRegistry } = getContractAddresses3(chainId);
2362
2418
  return new _IssuerStateValidator(provider, issuerRegistry);
2363
2419
  }
2364
2420
  /**