@pafi-dev/issuer 0.11.0 → 0.12.0

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
@@ -1080,7 +1080,23 @@ interface ApiConfigResponse {
1080
1080
  * a swap; the pool is only discoverable when the hook matches.
1081
1081
  */
1082
1082
  pafiHook?: Address;
1083
+ /**
1084
+ * v1.6 — single global MintFeeWrapper that skims a fee on every
1085
+ * sig-gated mint and distributes across the configured recipients.
1086
+ */
1087
+ mintFeeWrapper?: Address;
1083
1088
  };
1089
+ /**
1090
+ * v1.6 — per-PointToken mint fee (in basis points, 0–10000) read from
1091
+ * `MintFeeWrapper.totalFeeBps(pointToken)`. Frontend uses this to show
1092
+ * "you'll receive X PT (Y% mint fee)" before user signs.
1093
+ *
1094
+ * Map keyed by lower-cased PointToken address (JSON-friendly) so the
1095
+ * FE can look up the fee without iterating. Omitted (or with zero
1096
+ * values) when the chain has no wrapper deployed or recipients are
1097
+ * not configured.
1098
+ */
1099
+ mintFeeBpsByToken?: Record<string, number>;
1084
1100
  /**
1085
1101
  * Absolute URL that the Issuer App opens after a successful claim to
1086
1102
  * let the user swap PT → USDT or deposit into the perp DEX on PAFI
@@ -1331,6 +1347,13 @@ interface IssuerApiHandlersConfig {
1331
1347
  * See [MOBILE_SDK_INTEGRATION.md] "PAFI Web Handoff".
1332
1348
  */
1333
1349
  pafiWebUrl?: string;
1350
+ /**
1351
+ * v1.6 — MintFeeWrapper address used to read per-PointToken fee
1352
+ * basis points for `/config`. When omitted, the response will not
1353
+ * include `mintFeeBpsByToken` and FE must assume zero fee. Wrapper
1354
+ * is shared across PointTokens; per-token recipients live inside it.
1355
+ */
1356
+ mintFeeWrapperAddress?: Address;
1334
1357
  /** Required by `handleGasFee`; omit to disable the endpoint. */
1335
1358
  feeManager?: FeeManager;
1336
1359
  /** Required by `handlePools`; omit to disable the endpoint. */
@@ -1376,6 +1399,14 @@ declare class IssuerApiHandlers {
1376
1399
  private readonly poolsProvider?;
1377
1400
  private readonly redemption?;
1378
1401
  private readonly rateLimiter;
1402
+ private readonly mintFeeWrapperAddress?;
1403
+ /**
1404
+ * Per-token feeBps cache. Refreshed on /config when stale. feeBps
1405
+ * changes only when ops calls `wrapper.setRecipients`, so 5-min TTL
1406
+ * is more than safe for FE display purposes.
1407
+ */
1408
+ private readonly feeBpsCache;
1409
+ private static readonly FEE_BPS_TTL_MS;
1379
1410
  constructor(config: IssuerApiHandlersConfig);
1380
1411
  /**
1381
1412
  * `GET /auth/nonce`
@@ -1402,6 +1433,13 @@ declare class IssuerApiHandlers {
1402
1433
  * needs to build EIP-712 messages and interact with on-chain.
1403
1434
  */
1404
1435
  handleConfig(chainId: number): Promise<ApiConfigResponse>;
1436
+ /**
1437
+ * Read `totalFeeBps(pointToken)` for every supported PointToken from
1438
+ * the wrapper. Cached per-token for 5 minutes. Returns `undefined`
1439
+ * (caller drops the field) if every read fails — FE must treat
1440
+ * "field missing" as "fee unknown, do not display".
1441
+ */
1442
+ private resolveFeeBpsByToken;
1405
1443
  /** `GET /gas-fee` — quoted in USDT (6-decimal base units). */
1406
1444
  handleGasFee(): Promise<ApiGasFeeResponse>;
1407
1445
  /** `POST /auth/logout` */
package/dist/index.d.ts CHANGED
@@ -1080,7 +1080,23 @@ interface ApiConfigResponse {
1080
1080
  * a swap; the pool is only discoverable when the hook matches.
1081
1081
  */
1082
1082
  pafiHook?: Address;
1083
+ /**
1084
+ * v1.6 — single global MintFeeWrapper that skims a fee on every
1085
+ * sig-gated mint and distributes across the configured recipients.
1086
+ */
1087
+ mintFeeWrapper?: Address;
1083
1088
  };
1089
+ /**
1090
+ * v1.6 — per-PointToken mint fee (in basis points, 0–10000) read from
1091
+ * `MintFeeWrapper.totalFeeBps(pointToken)`. Frontend uses this to show
1092
+ * "you'll receive X PT (Y% mint fee)" before user signs.
1093
+ *
1094
+ * Map keyed by lower-cased PointToken address (JSON-friendly) so the
1095
+ * FE can look up the fee without iterating. Omitted (or with zero
1096
+ * values) when the chain has no wrapper deployed or recipients are
1097
+ * not configured.
1098
+ */
1099
+ mintFeeBpsByToken?: Record<string, number>;
1084
1100
  /**
1085
1101
  * Absolute URL that the Issuer App opens after a successful claim to
1086
1102
  * let the user swap PT → USDT or deposit into the perp DEX on PAFI
@@ -1331,6 +1347,13 @@ interface IssuerApiHandlersConfig {
1331
1347
  * See [MOBILE_SDK_INTEGRATION.md] "PAFI Web Handoff".
1332
1348
  */
1333
1349
  pafiWebUrl?: string;
1350
+ /**
1351
+ * v1.6 — MintFeeWrapper address used to read per-PointToken fee
1352
+ * basis points for `/config`. When omitted, the response will not
1353
+ * include `mintFeeBpsByToken` and FE must assume zero fee. Wrapper
1354
+ * is shared across PointTokens; per-token recipients live inside it.
1355
+ */
1356
+ mintFeeWrapperAddress?: Address;
1334
1357
  /** Required by `handleGasFee`; omit to disable the endpoint. */
1335
1358
  feeManager?: FeeManager;
1336
1359
  /** Required by `handlePools`; omit to disable the endpoint. */
@@ -1376,6 +1399,14 @@ declare class IssuerApiHandlers {
1376
1399
  private readonly poolsProvider?;
1377
1400
  private readonly redemption?;
1378
1401
  private readonly rateLimiter;
1402
+ private readonly mintFeeWrapperAddress?;
1403
+ /**
1404
+ * Per-token feeBps cache. Refreshed on /config when stale. feeBps
1405
+ * changes only when ops calls `wrapper.setRecipients`, so 5-min TTL
1406
+ * is more than safe for FE display purposes.
1407
+ */
1408
+ private readonly feeBpsCache;
1409
+ private static readonly FEE_BPS_TTL_MS;
1379
1410
  constructor(config: IssuerApiHandlersConfig);
1380
1411
  /**
1381
1412
  * `GET /auth/nonce`
@@ -1402,6 +1433,13 @@ declare class IssuerApiHandlers {
1402
1433
  * needs to build EIP-712 messages and interact with on-chain.
1403
1434
  */
1404
1435
  handleConfig(chainId: number): Promise<ApiConfigResponse>;
1436
+ /**
1437
+ * Read `totalFeeBps(pointToken)` for every supported PointToken from
1438
+ * the wrapper. Cached per-token for 5 minutes. Returns `undefined`
1439
+ * (caller drops the field) if every read fails — FE must treat
1440
+ * "field missing" as "fee unknown, do not display".
1441
+ */
1442
+ private resolveFeeBpsByToken;
1405
1443
  /** `GET /gas-fee` — quoted in USDT (6-decimal base units). */
1406
1444
  handleGasFee(): Promise<ApiGasFeeResponse>;
1407
1445
  /** `POST /auth/logout` */
package/dist/index.js CHANGED
@@ -1340,12 +1340,13 @@ var BurnIndexer = class {
1340
1340
  // src/api/handlers.ts
1341
1341
  import { getAddress as getAddress5 } from "viem";
1342
1342
  import {
1343
+ getMintFeeBps,
1343
1344
  getMintRequestNonce,
1344
1345
  getPointTokenBalance,
1345
1346
  getReceiverConsentNonce,
1346
1347
  isMinter
1347
1348
  } from "@pafi-dev/core";
1348
- var IssuerApiHandlers = class {
1349
+ var IssuerApiHandlers = class _IssuerApiHandlers {
1349
1350
  authService;
1350
1351
  ledger;
1351
1352
  provider;
@@ -1361,6 +1362,14 @@ var IssuerApiHandlers = class {
1361
1362
  poolsProvider;
1362
1363
  redemption;
1363
1364
  rateLimiter;
1365
+ mintFeeWrapperAddress;
1366
+ /**
1367
+ * Per-token feeBps cache. Refreshed on /config when stale. feeBps
1368
+ * changes only when ops calls `wrapper.setRecipients`, so 5-min TTL
1369
+ * is more than safe for FE display purposes.
1370
+ */
1371
+ feeBpsCache = /* @__PURE__ */ new Map();
1372
+ static FEE_BPS_TTL_MS = 5 * 60 * 1e3;
1364
1373
  constructor(config) {
1365
1374
  this.authService = config.authService;
1366
1375
  this.ledger = config.ledger;
@@ -1380,6 +1389,9 @@ var IssuerApiHandlers = class {
1380
1389
  if (config.feeManager) this.feeManager = config.feeManager;
1381
1390
  if (config.poolsProvider) this.poolsProvider = config.poolsProvider;
1382
1391
  if (config.redemption) this.redemption = config.redemption;
1392
+ if (config.mintFeeWrapperAddress) {
1393
+ this.mintFeeWrapperAddress = getAddress5(config.mintFeeWrapperAddress);
1394
+ }
1383
1395
  }
1384
1396
  // =========================================================================
1385
1397
  // Public handlers (no auth required)
@@ -1479,13 +1491,58 @@ var IssuerApiHandlers = class {
1479
1491
  ...this.contracts,
1480
1492
  pointTokens: Array.from(this.supportedTokens)
1481
1493
  };
1494
+ if (this.mintFeeWrapperAddress) {
1495
+ contracts.mintFeeWrapper = this.mintFeeWrapperAddress;
1496
+ }
1482
1497
  const response = {
1483
1498
  chainId: this.chainId,
1484
1499
  contracts
1485
1500
  };
1501
+ if (this.mintFeeWrapperAddress) {
1502
+ const byToken = await this.resolveFeeBpsByToken(
1503
+ this.mintFeeWrapperAddress
1504
+ );
1505
+ if (byToken) response.mintFeeBpsByToken = byToken;
1506
+ }
1486
1507
  if (this.pafiWebUrl) response.pafiWebUrl = this.pafiWebUrl;
1487
1508
  return response;
1488
1509
  }
1510
+ /**
1511
+ * Read `totalFeeBps(pointToken)` for every supported PointToken from
1512
+ * the wrapper. Cached per-token for 5 minutes. Returns `undefined`
1513
+ * (caller drops the field) if every read fails — FE must treat
1514
+ * "field missing" as "fee unknown, do not display".
1515
+ */
1516
+ async resolveFeeBpsByToken(wrapper) {
1517
+ const now = Date.now();
1518
+ const out = {};
1519
+ let anyFresh = false;
1520
+ await Promise.all(
1521
+ Array.from(this.supportedTokens).map(async (token) => {
1522
+ const cached = this.feeBpsCache.get(token);
1523
+ if (cached && cached.expiresAt > now) {
1524
+ out[token.toLowerCase()] = cached.value;
1525
+ anyFresh = true;
1526
+ return;
1527
+ }
1528
+ try {
1529
+ const bps = await getMintFeeBps(this.provider, wrapper, token);
1530
+ this.feeBpsCache.set(token, {
1531
+ value: bps,
1532
+ expiresAt: now + _IssuerApiHandlers.FEE_BPS_TTL_MS
1533
+ });
1534
+ out[token.toLowerCase()] = bps;
1535
+ anyFresh = true;
1536
+ } catch {
1537
+ if (cached) {
1538
+ out[token.toLowerCase()] = cached.value;
1539
+ anyFresh = true;
1540
+ }
1541
+ }
1542
+ })
1543
+ );
1544
+ return anyFresh ? out : void 0;
1545
+ }
1489
1546
  /** `GET /gas-fee` — quoted in USDT (6-decimal base units). */
1490
1547
  async handleGasFee() {
1491
1548
  if (!this.feeManager) {
@@ -4231,6 +4288,9 @@ function createIssuerService(config) {
4231
4288
  pafiHook: chainAddresses.pafiHook,
4232
4289
  ...config.contracts
4233
4290
  };
4291
+ if (resolvedWrapperAddress !== void 0) {
4292
+ resolvedContracts.mintFeeWrapper = resolvedWrapperAddress;
4293
+ }
4234
4294
  let redemption;
4235
4295
  if (config.redemption) {
4236
4296
  const policyConfig = {
@@ -4261,6 +4321,9 @@ function createIssuerService(config) {
4261
4321
  if (feeManager) handlersConfig.feeManager = feeManager;
4262
4322
  if (config.poolsProvider) handlersConfig.poolsProvider = config.poolsProvider;
4263
4323
  if (redemption) handlersConfig.redemption = redemption;
4324
+ if (resolvedWrapperAddress !== void 0) {
4325
+ handlersConfig.mintFeeWrapperAddress = resolvedWrapperAddress;
4326
+ }
4264
4327
  const handlers = new IssuerApiHandlers(handlersConfig);
4265
4328
  if (config.indexer?.autoStart) {
4266
4329
  for (const idx of indexers.values()) {
@@ -4478,7 +4541,7 @@ var MemoryRedemptionHistoryStore = class {
4478
4541
  };
4479
4542
 
4480
4543
  // src/index.ts
4481
- var PAFI_ISSUER_SDK_VERSION = true ? "0.11.0" : "dev";
4544
+ var PAFI_ISSUER_SDK_VERSION = true ? "0.12.0" : "dev";
4482
4545
  export {
4483
4546
  AdapterMisconfiguredError,
4484
4547
  AuthError,