@pafi-dev/issuer 0.10.1 → 0.11.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
@@ -814,6 +814,22 @@ interface PointIndexerConfig {
814
814
  provider: PublicClient;
815
815
  pointTokenAddress: Address;
816
816
  ledger: IPointLedger;
817
+ /**
818
+ * v1.6 — when set, indexer listens to `MintFeeWrapper.MintWithFee`
819
+ * (filtered by this `pointToken`) instead of `PointToken.Transfer(0x0)`.
820
+ *
821
+ * Required for wrapper-mediated mints because the on-chain Transfer
822
+ * destination is the wrapper itself (not the end user), so a naive
823
+ * `Transfer(0x0 → user)` watch would leave PENDING locks unresolved.
824
+ *
825
+ * `MintWithFee(indexed pointToken, indexed to, grossAmount, netAmount,
826
+ * feeAmount)` carries the actual recipient + the gross amount that
827
+ * matches the off-chain lock.
828
+ *
829
+ * Pass `undefined` (or the dead-zero address) for direct-mint chains
830
+ * without a wrapper — indexer falls back to legacy Transfer mode.
831
+ */
832
+ mintFeeWrapperAddress?: Address;
817
833
  /**
818
834
  * Block to start from on first run. Ignored if the cursor store already
819
835
  * has a value. Defaults to `0n`.
@@ -853,8 +869,20 @@ interface PointIndexerConfig {
853
869
  onTickError?: (err: unknown) => void;
854
870
  }
855
871
  /**
856
- * Watches `PointToken.Transfer(from=0x0 to)` events and finalizes the
857
- * issuer ledger on each confirmed mint.
872
+ * Watches mint events on PointToken and finalizes the issuer ledger when
873
+ * a confirmed mint matches a PENDING lock.
874
+ *
875
+ * **Two modes** — selected at construction by `mintFeeWrapperAddress`:
876
+ *
877
+ * 1. **Wrapper mode (v1.6, recommended for mainnet)**
878
+ * Listens to `MintFeeWrapper.MintWithFee(indexed pointToken, indexed to,
879
+ * grossAmount, netAmount, feeAmount)` filtered by `pointToken`. The
880
+ * `to` field is the actual end-user (not the wrapper), and `grossAmount`
881
+ * matches the off-chain lock's amount.
882
+ *
883
+ * 2. **Direct mode (legacy / chains without wrapper)**
884
+ * Listens to `PointToken.Transfer(from=0x0 → to)` — the original v1.5
885
+ * behavior. Used when the wrapper address is undefined / zero / dead.
858
886
  *
859
887
  * Finalization strategy (per event):
860
888
  * 1. Find a PENDING locked mint request for `to` with matching amount.
@@ -876,6 +904,7 @@ interface PointIndexerConfig {
876
904
  declare class PointIndexer {
877
905
  private readonly provider;
878
906
  private readonly pointTokenAddress;
907
+ private readonly mintFeeWrapperAddress;
879
908
  private readonly ledger;
880
909
  private readonly cursorStore;
881
910
  private readonly startBlock;
@@ -904,7 +933,18 @@ declare class PointIndexer {
904
933
  * engaging `start()`. On completion, the cursor is advanced to `to + 1`.
905
934
  */
906
935
  processBlockRange(from: bigint, to: bigint): Promise<void>;
907
- private decodeMintEvents;
936
+ /**
937
+ * Wrapper mode (v1.6): listen for `MintWithFee` on the wrapper,
938
+ * filtered to events for THIS pointToken only. The event's `to` field
939
+ * is the actual end user, and `grossAmount` matches the lock amount.
940
+ */
941
+ private fetchWrapperMintEvents;
942
+ /**
943
+ * Direct mode (legacy / chains without wrapper): listen for
944
+ * `Transfer(from=0x0 → to)` on the PointToken itself.
945
+ */
946
+ private fetchTransferMintEvents;
947
+ private decodeTransferMintEvents;
908
948
  /**
909
949
  * Finalize a single mint event: match it to a PENDING lock in the
910
950
  * ledger, then call `deductBalance` (which also resolves the lock in
@@ -2238,13 +2278,20 @@ interface PTClaimHandlerConfig {
2238
2278
  signatureDeadlineSeconds?: number;
2239
2279
  now?: () => number;
2240
2280
  /**
2241
- * Optional v1.6+ when set, mints route through `MintFeeWrapper` so a
2242
- * fee is skimmed and split per the registered recipient list. Caller
2243
- * responsibility:
2244
- * - DAO must have called `IssuerRegistry.setMintFeeWrapper(this address)`
2245
- * - The PointToken must have been registered (cascaded from
2246
- * `IssuerRegistry.addIssuer` or owner-only `wrapper.registerToken`)
2247
- * When unset, claims fall back to direct PointToken.mint (no fee skim).
2281
+ * Optional override for the v1.6+ MintFeeWrapper address. By default the
2282
+ * handler auto-resolves the wrapper from the request's `chainId` via
2283
+ * `getContractAddresses(chainId).mintFeeWrapper`. Set this only when:
2284
+ * - testing against a non-canonical wrapper deploy, or
2285
+ * - the SDK's hardcoded address is stale and you can't bump the SDK yet
2286
+ *
2287
+ * Pass the dead-zero address to opt OUT of the wrapper path (force direct
2288
+ * mint with no fee). Pass `undefined` (default) to use SDK's lookup.
2289
+ *
2290
+ * Caller responsibility either way:
2291
+ * - DAO must have called `IssuerRegistry.setMintFeeWrapper(...)` so
2292
+ * `addIssuer` cascades the recipient list.
2293
+ * - The PointToken must have been registered with the wrapper (via
2294
+ * `addIssuer` cascade or owner-only `wrapper.registerToken`).
2248
2295
  */
2249
2296
  mintFeeWrapperAddress?: Address;
2250
2297
  }
@@ -2543,6 +2590,14 @@ interface IssuerServiceConfig {
2543
2590
  * Default: `false` — the caller decides when to begin polling.
2544
2591
  */
2545
2592
  autoStart?: boolean;
2593
+ /**
2594
+ * v1.6 — override the MintFeeWrapper address used by the indexer.
2595
+ * When omitted, the factory auto-resolves from
2596
+ * `getContractAddresses(chainId).mintFeeWrapper`. Pass the
2597
+ * dead-zero address (`0x...dEaD`) to force direct-Transfer mode
2598
+ * (legacy v1.5, useful for local fork tests).
2599
+ */
2600
+ mintFeeWrapperAddress?: Address;
2546
2601
  };
2547
2602
  /**
2548
2603
  * Redemption restriction config. When provided, the SDK fetches the
package/dist/index.d.ts CHANGED
@@ -814,6 +814,22 @@ interface PointIndexerConfig {
814
814
  provider: PublicClient;
815
815
  pointTokenAddress: Address;
816
816
  ledger: IPointLedger;
817
+ /**
818
+ * v1.6 — when set, indexer listens to `MintFeeWrapper.MintWithFee`
819
+ * (filtered by this `pointToken`) instead of `PointToken.Transfer(0x0)`.
820
+ *
821
+ * Required for wrapper-mediated mints because the on-chain Transfer
822
+ * destination is the wrapper itself (not the end user), so a naive
823
+ * `Transfer(0x0 → user)` watch would leave PENDING locks unresolved.
824
+ *
825
+ * `MintWithFee(indexed pointToken, indexed to, grossAmount, netAmount,
826
+ * feeAmount)` carries the actual recipient + the gross amount that
827
+ * matches the off-chain lock.
828
+ *
829
+ * Pass `undefined` (or the dead-zero address) for direct-mint chains
830
+ * without a wrapper — indexer falls back to legacy Transfer mode.
831
+ */
832
+ mintFeeWrapperAddress?: Address;
817
833
  /**
818
834
  * Block to start from on first run. Ignored if the cursor store already
819
835
  * has a value. Defaults to `0n`.
@@ -853,8 +869,20 @@ interface PointIndexerConfig {
853
869
  onTickError?: (err: unknown) => void;
854
870
  }
855
871
  /**
856
- * Watches `PointToken.Transfer(from=0x0 to)` events and finalizes the
857
- * issuer ledger on each confirmed mint.
872
+ * Watches mint events on PointToken and finalizes the issuer ledger when
873
+ * a confirmed mint matches a PENDING lock.
874
+ *
875
+ * **Two modes** — selected at construction by `mintFeeWrapperAddress`:
876
+ *
877
+ * 1. **Wrapper mode (v1.6, recommended for mainnet)**
878
+ * Listens to `MintFeeWrapper.MintWithFee(indexed pointToken, indexed to,
879
+ * grossAmount, netAmount, feeAmount)` filtered by `pointToken`. The
880
+ * `to` field is the actual end-user (not the wrapper), and `grossAmount`
881
+ * matches the off-chain lock's amount.
882
+ *
883
+ * 2. **Direct mode (legacy / chains without wrapper)**
884
+ * Listens to `PointToken.Transfer(from=0x0 → to)` — the original v1.5
885
+ * behavior. Used when the wrapper address is undefined / zero / dead.
858
886
  *
859
887
  * Finalization strategy (per event):
860
888
  * 1. Find a PENDING locked mint request for `to` with matching amount.
@@ -876,6 +904,7 @@ interface PointIndexerConfig {
876
904
  declare class PointIndexer {
877
905
  private readonly provider;
878
906
  private readonly pointTokenAddress;
907
+ private readonly mintFeeWrapperAddress;
879
908
  private readonly ledger;
880
909
  private readonly cursorStore;
881
910
  private readonly startBlock;
@@ -904,7 +933,18 @@ declare class PointIndexer {
904
933
  * engaging `start()`. On completion, the cursor is advanced to `to + 1`.
905
934
  */
906
935
  processBlockRange(from: bigint, to: bigint): Promise<void>;
907
- private decodeMintEvents;
936
+ /**
937
+ * Wrapper mode (v1.6): listen for `MintWithFee` on the wrapper,
938
+ * filtered to events for THIS pointToken only. The event's `to` field
939
+ * is the actual end user, and `grossAmount` matches the lock amount.
940
+ */
941
+ private fetchWrapperMintEvents;
942
+ /**
943
+ * Direct mode (legacy / chains without wrapper): listen for
944
+ * `Transfer(from=0x0 → to)` on the PointToken itself.
945
+ */
946
+ private fetchTransferMintEvents;
947
+ private decodeTransferMintEvents;
908
948
  /**
909
949
  * Finalize a single mint event: match it to a PENDING lock in the
910
950
  * ledger, then call `deductBalance` (which also resolves the lock in
@@ -2238,13 +2278,20 @@ interface PTClaimHandlerConfig {
2238
2278
  signatureDeadlineSeconds?: number;
2239
2279
  now?: () => number;
2240
2280
  /**
2241
- * Optional v1.6+ when set, mints route through `MintFeeWrapper` so a
2242
- * fee is skimmed and split per the registered recipient list. Caller
2243
- * responsibility:
2244
- * - DAO must have called `IssuerRegistry.setMintFeeWrapper(this address)`
2245
- * - The PointToken must have been registered (cascaded from
2246
- * `IssuerRegistry.addIssuer` or owner-only `wrapper.registerToken`)
2247
- * When unset, claims fall back to direct PointToken.mint (no fee skim).
2281
+ * Optional override for the v1.6+ MintFeeWrapper address. By default the
2282
+ * handler auto-resolves the wrapper from the request's `chainId` via
2283
+ * `getContractAddresses(chainId).mintFeeWrapper`. Set this only when:
2284
+ * - testing against a non-canonical wrapper deploy, or
2285
+ * - the SDK's hardcoded address is stale and you can't bump the SDK yet
2286
+ *
2287
+ * Pass the dead-zero address to opt OUT of the wrapper path (force direct
2288
+ * mint with no fee). Pass `undefined` (default) to use SDK's lookup.
2289
+ *
2290
+ * Caller responsibility either way:
2291
+ * - DAO must have called `IssuerRegistry.setMintFeeWrapper(...)` so
2292
+ * `addIssuer` cascades the recipient list.
2293
+ * - The PointToken must have been registered with the wrapper (via
2294
+ * `addIssuer` cascade or owner-only `wrapper.registerToken`).
2248
2295
  */
2249
2296
  mintFeeWrapperAddress?: Address;
2250
2297
  }
@@ -2543,6 +2590,14 @@ interface IssuerServiceConfig {
2543
2590
  * Default: `false` — the caller decides when to begin polling.
2544
2591
  */
2545
2592
  autoStart?: boolean;
2593
+ /**
2594
+ * v1.6 — override the MintFeeWrapper address used by the indexer.
2595
+ * When omitted, the factory auto-resolves from
2596
+ * `getContractAddresses(chainId).mintFeeWrapper`. Pass the
2597
+ * dead-zero address (`0x...dEaD`) to force direct-Transfer mode
2598
+ * (legacy v1.5, useful for local fork tests).
2599
+ */
2600
+ mintFeeWrapperAddress?: Address;
2546
2601
  };
2547
2602
  /**
2548
2603
  * Redemption restriction config. When provided, the SDK fetches the
package/dist/index.js CHANGED
@@ -924,13 +924,23 @@ import { getAddress as getAddress3, parseAbiItem } from "viem";
924
924
  var TRANSFER_EVENT = parseAbiItem(
925
925
  "event Transfer(address indexed from, address indexed to, uint256 value)"
926
926
  );
927
+ var MINT_WITH_FEE_EVENT = parseAbiItem(
928
+ "event MintWithFee(address indexed pointToken, address indexed to, uint256 grossAmount, uint256 netAmount, uint256 feeAmount)"
929
+ );
927
930
  var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
931
+ var DEAD_ADDRESS = "0x000000000000000000000000000000000000dEaD";
928
932
  var DEFAULT_CONFIRMATIONS = 3;
929
933
  var DEFAULT_BATCH_SIZE = 2000n;
930
934
  var DEFAULT_POLL_INTERVAL_MS = 5e3;
935
+ function isNoWrapper(addr) {
936
+ if (!addr) return true;
937
+ const checksummed = getAddress3(addr);
938
+ return checksummed === ZERO_ADDRESS || checksummed === DEAD_ADDRESS;
939
+ }
931
940
  var PointIndexer = class {
932
941
  provider;
933
942
  pointTokenAddress;
943
+ mintFeeWrapperAddress;
934
944
  ledger;
935
945
  cursorStore;
936
946
  startBlock;
@@ -946,7 +956,8 @@ var PointIndexer = class {
946
956
  throw new Error("PointIndexer: pointTokenAddress required");
947
957
  if (!config.ledger) throw new Error("PointIndexer: ledger required");
948
958
  this.provider = config.provider;
949
- this.pointTokenAddress = config.pointTokenAddress;
959
+ this.pointTokenAddress = getAddress3(config.pointTokenAddress);
960
+ this.mintFeeWrapperAddress = isNoWrapper(config.mintFeeWrapperAddress) ? void 0 : getAddress3(config.mintFeeWrapperAddress);
950
961
  this.ledger = config.ledger;
951
962
  this.cursorStore = config.cursorStore ?? new InMemoryCursorStore();
952
963
  this.startBlock = config.fromBlock ?? 0n;
@@ -1029,14 +1040,7 @@ var PointIndexer = class {
1029
1040
  let cursor = from;
1030
1041
  while (cursor <= to) {
1031
1042
  const chunkEnd = cursor + this.batchSize - 1n > to ? to : cursor + this.batchSize - 1n;
1032
- const logs = await this.provider.getLogs({
1033
- address: this.pointTokenAddress,
1034
- event: TRANSFER_EVENT,
1035
- args: { from: ZERO_ADDRESS },
1036
- fromBlock: cursor,
1037
- toBlock: chunkEnd
1038
- });
1039
- const events = this.decodeMintEvents(logs);
1043
+ const events = this.mintFeeWrapperAddress ? await this.fetchWrapperMintEvents(cursor, chunkEnd) : await this.fetchTransferMintEvents(cursor, chunkEnd);
1040
1044
  events.sort((a, b) => {
1041
1045
  if (a.blockNumber !== b.blockNumber) {
1042
1046
  return a.blockNumber < b.blockNumber ? -1 : 1;
@@ -1051,9 +1055,53 @@ var PointIndexer = class {
1051
1055
  }
1052
1056
  }
1053
1057
  // -------------------------------------------------------------------------
1054
- // Internals
1058
+ // Event fetching — two modes (wrapper vs direct)
1055
1059
  // -------------------------------------------------------------------------
1056
- decodeMintEvents(logs) {
1060
+ /**
1061
+ * Wrapper mode (v1.6): listen for `MintWithFee` on the wrapper,
1062
+ * filtered to events for THIS pointToken only. The event's `to` field
1063
+ * is the actual end user, and `grossAmount` matches the lock amount.
1064
+ */
1065
+ async fetchWrapperMintEvents(fromBlock, toBlock) {
1066
+ const logs = await this.provider.getLogs({
1067
+ address: this.mintFeeWrapperAddress,
1068
+ event: MINT_WITH_FEE_EVENT,
1069
+ args: { pointToken: this.pointTokenAddress },
1070
+ fromBlock,
1071
+ toBlock
1072
+ });
1073
+ const out = [];
1074
+ for (const log of logs) {
1075
+ const args = log.args;
1076
+ if (!args.pointToken || !args.to || args.grossAmount === void 0 || log.blockNumber === null || log.transactionHash === null) {
1077
+ continue;
1078
+ }
1079
+ if (getAddress3(args.pointToken) !== this.pointTokenAddress) continue;
1080
+ out.push({
1081
+ to: getAddress3(args.to),
1082
+ amount: args.grossAmount,
1083
+ blockNumber: log.blockNumber,
1084
+ txHash: log.transactionHash,
1085
+ logIndex: log.logIndex ?? 0
1086
+ });
1087
+ }
1088
+ return out;
1089
+ }
1090
+ /**
1091
+ * Direct mode (legacy / chains without wrapper): listen for
1092
+ * `Transfer(from=0x0 → to)` on the PointToken itself.
1093
+ */
1094
+ async fetchTransferMintEvents(fromBlock, toBlock) {
1095
+ const logs = await this.provider.getLogs({
1096
+ address: this.pointTokenAddress,
1097
+ event: TRANSFER_EVENT,
1098
+ args: { from: ZERO_ADDRESS },
1099
+ fromBlock,
1100
+ toBlock
1101
+ });
1102
+ return this.decodeTransferMintEvents(logs);
1103
+ }
1104
+ decodeTransferMintEvents(logs) {
1057
1105
  const out = [];
1058
1106
  for (const log of logs) {
1059
1107
  const args = log.args;
@@ -1070,6 +1118,9 @@ var PointIndexer = class {
1070
1118
  }
1071
1119
  return out;
1072
1120
  }
1121
+ // -------------------------------------------------------------------------
1122
+ // Finalization
1123
+ // -------------------------------------------------------------------------
1073
1124
  /**
1074
1125
  * Finalize a single mint event: match it to a PENDING lock in the
1075
1126
  * ledger, then call `deductBalance` (which also resolves the lock in
@@ -2380,6 +2431,11 @@ var PTClaimError = class extends PafiSdkError {
2380
2431
  this.details = details;
2381
2432
  }
2382
2433
  };
2434
+ function isNoWrapper2(address) {
2435
+ if (!address) return true;
2436
+ const lower = address.toLowerCase();
2437
+ return lower === "0x0000000000000000000000000000000000000000" || lower === "0x000000000000000000000000000000000000dead";
2438
+ }
2383
2439
  var DEFAULT_LOCK_MS = 15 * 60 * 1e3;
2384
2440
  var DEFAULT_SIG_DEADLINE_SEC2 = 15 * 60;
2385
2441
  var PTClaimHandler = class {
@@ -2416,9 +2472,11 @@ var PTClaimHandler = class {
2416
2472
  );
2417
2473
  }
2418
2474
  }
2419
- const { batchExecutor: batchExecutorAddress } = getContractAddresses3(
2420
- request.chainId
2421
- );
2475
+ const chainAddresses = getContractAddresses3(request.chainId);
2476
+ const { batchExecutor: batchExecutorAddress } = chainAddresses;
2477
+ const wrapperOverride = this.cfg.mintFeeWrapperAddress;
2478
+ const wrapperFromSdk = chainAddresses.mintFeeWrapper;
2479
+ const resolvedWrapper = wrapperOverride !== void 0 ? isNoWrapper2(wrapperOverride) ? void 0 : wrapperOverride : isNoWrapper2(wrapperFromSdk) ? void 0 : wrapperFromSdk;
2422
2480
  const lockId = await this.cfg.ledger.lockForMinting(
2423
2481
  request.userAddress,
2424
2482
  request.amount,
@@ -2447,7 +2505,7 @@ var PTClaimHandler = class {
2447
2505
  domain,
2448
2506
  mintRequestNonce: request.mintRequestNonce,
2449
2507
  deadline: signatureDeadline,
2450
- mintFeeWrapperAddress: this.cfg.mintFeeWrapperAddress
2508
+ mintFeeWrapperAddress: resolvedWrapper
2451
2509
  // No feeAmount/feeRecipient — RelayService auto-resolves.
2452
2510
  });
2453
2511
  } catch (err) {
@@ -2470,7 +2528,7 @@ var PTClaimHandler = class {
2470
2528
  mintRequestNonce: request.mintRequestNonce,
2471
2529
  deadline: signatureDeadline,
2472
2530
  feeAmount: 0n,
2473
- mintFeeWrapperAddress: this.cfg.mintFeeWrapperAddress
2531
+ mintFeeWrapperAddress: resolvedWrapper
2474
2532
  });
2475
2533
  } catch (err) {
2476
2534
  throw new PTClaimError(
@@ -4133,6 +4191,9 @@ function createIssuerService(config) {
4133
4191
  provider: config.provider
4134
4192
  });
4135
4193
  }
4194
+ const sdkWrapperAddress = getContractAddresses7(config.chainId).mintFeeWrapper;
4195
+ const wrapperOverride = config.indexer?.mintFeeWrapperAddress;
4196
+ const resolvedWrapperAddress = wrapperOverride !== void 0 ? wrapperOverride : sdkWrapperAddress;
4136
4197
  const indexers = /* @__PURE__ */ new Map();
4137
4198
  for (const tokenAddress of tokenAddresses) {
4138
4199
  const indexerConfig = {
@@ -4140,6 +4201,9 @@ function createIssuerService(config) {
4140
4201
  pointTokenAddress: tokenAddress,
4141
4202
  ledger
4142
4203
  };
4204
+ if (resolvedWrapperAddress !== void 0) {
4205
+ indexerConfig.mintFeeWrapperAddress = resolvedWrapperAddress;
4206
+ }
4143
4207
  if (config.indexer?.fromBlock !== void 0) {
4144
4208
  indexerConfig.fromBlock = config.indexer.fromBlock;
4145
4209
  }
@@ -4414,7 +4478,7 @@ var MemoryRedemptionHistoryStore = class {
4414
4478
  };
4415
4479
 
4416
4480
  // src/index.ts
4417
- var PAFI_ISSUER_SDK_VERSION = true ? "0.10.1" : "dev";
4481
+ var PAFI_ISSUER_SDK_VERSION = true ? "0.11.0" : "dev";
4418
4482
  export {
4419
4483
  AdapterMisconfiguredError,
4420
4484
  AuthError,