@pafi-dev/issuer 0.1.2 → 0.2.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
@@ -18,6 +18,12 @@ interface LockedMintRequest {
18
18
  lockId: string;
19
19
  userAddress: Address;
20
20
  amount: bigint;
21
+ /**
22
+ * Which PointToken this lock belongs to. Added in 0.2.0 for multi-token
23
+ * issuer support. Optional for backward compat with 0.1.x ledgers —
24
+ * single-token consumers can ignore it.
25
+ */
26
+ tokenAddress?: Address;
21
27
  /** Lifecycle status */
22
28
  status: MintingStatus;
23
29
  /** When the lock was created (epoch ms) */
@@ -33,20 +39,28 @@ interface LockedMintRequest {
33
39
  *
34
40
  * Issuers replace the in-memory default with their own database-backed
35
41
  * implementation (Postgres, Redis, etc.).
42
+ *
43
+ * **Multi-token support (0.2.0):**
44
+ * Every mutating method accepts an optional `tokenAddress` parameter so
45
+ * balances can be scoped per `(user, token)`. Single-token issuers can
46
+ * ignore the parameter entirely — legacy 0.1.x implementations remain
47
+ * compatible. Multi-token issuers must persist + query balances keyed by
48
+ * `(userAddress, tokenAddress)`.
36
49
  */
37
50
  interface IPointLedger {
38
51
  /** Get a user's available off-chain point balance (excluding locked). */
39
- getBalance(userAddress: Address): Promise<bigint>;
52
+ getBalance(userAddress: Address, tokenAddress?: Address): Promise<bigint>;
40
53
  /**
41
54
  * Lock an amount for a pending mint request. Locked amounts are reserved
42
55
  * but not yet deducted; they protect against double-spend during the
43
56
  * EIP-712 validity window.
44
57
  *
45
58
  * @param lockDurationMs how long the lock should be held before auto-expiry
59
+ * @param tokenAddress which PointToken this lock is for (0.2.0+)
46
60
  * @returns lockId — opaque handle used by `releaseLock` / `updateMintStatus`
47
61
  * @throws if the user's available balance is below `amount`
48
62
  */
49
- lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number): Promise<string>;
63
+ lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number, tokenAddress?: Address): Promise<string>;
50
64
  /** Release a previously created lock (e.g. on tx failure / cancel). */
51
65
  releaseLock(lockId: string): Promise<void>;
52
66
  /**
@@ -54,11 +68,11 @@ interface IPointLedger {
54
68
  * mint has been observed by the indexer. Should also resolve any matching
55
69
  * lock so the funds aren't double-counted.
56
70
  */
57
- deductBalance(userAddress: Address, amount: bigint, txHash: Hex): Promise<void>;
71
+ deductBalance(userAddress: Address, amount: bigint, txHash: Hex, tokenAddress?: Address): Promise<void>;
58
72
  /** Credit points to a user's balance (e.g. from merchant activity). */
59
- creditBalance(userAddress: Address, amount: bigint, reason: string): Promise<void>;
73
+ creditBalance(userAddress: Address, amount: bigint, reason: string, tokenAddress?: Address): Promise<void>;
60
74
  /** List currently-pending locked mint requests for a user. */
61
- getLockedRequests(userAddress: Address): Promise<LockedMintRequest[]>;
75
+ getLockedRequests(userAddress: Address, tokenAddress?: Address): Promise<LockedMintRequest[]>;
62
76
  /**
63
77
  * Transition a lock to a new lifecycle status. The on-chain tx hash is
64
78
  * supplied when the status is `MINTED`.
@@ -75,6 +89,10 @@ interface IPointLedger {
75
89
  * Concurrency model: single-process, single-threaded (Node.js event loop).
76
90
  * The lock check + insert is atomic within a tick because no awaits sit
77
91
  * between balance read and lock write.
92
+ *
93
+ * **Multi-token (0.2.0):** Balances are keyed by `(user, token)`. If callers
94
+ * omit `tokenAddress`, the literal string "default" is used — that keeps
95
+ * single-token usage working exactly like 0.1.x.
78
96
  */
79
97
  declare class MemoryPointLedger implements IPointLedger {
80
98
  private balances;
@@ -84,12 +102,12 @@ declare class MemoryPointLedger implements IPointLedger {
84
102
  constructor(opts?: {
85
103
  now?: () => number;
86
104
  });
87
- getBalance(userAddress: Address): Promise<bigint>;
88
- getLockedRequests(userAddress: Address): Promise<LockedMintRequest[]>;
89
- creditBalance(userAddress: Address, amount: bigint, _reason: string): Promise<void>;
90
- lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number): Promise<string>;
105
+ getBalance(userAddress: Address, tokenAddress?: Address): Promise<bigint>;
106
+ getLockedRequests(userAddress: Address, tokenAddress?: Address): Promise<LockedMintRequest[]>;
107
+ creditBalance(userAddress: Address, amount: bigint, _reason: string, tokenAddress?: Address): Promise<void>;
108
+ lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number, tokenAddress?: Address): Promise<string>;
91
109
  releaseLock(lockId: string): Promise<void>;
92
- deductBalance(userAddress: Address, amount: bigint, txHash: Hex): Promise<void>;
110
+ deductBalance(userAddress: Address, amount: bigint, txHash: Hex, tokenAddress?: Address): Promise<void>;
93
111
  updateMintStatus(lockId: string, status: MintingStatus, txHash?: Hex): Promise<void>;
94
112
  /**
95
113
  * Auto-expire any PENDING lock past its expiry. Called lazily on every
@@ -1002,7 +1020,16 @@ interface IssuerApiHandlersConfig {
1002
1020
  ledger: IPointLedger;
1003
1021
  /** Used by `handleUser` to read on-chain nonces and minter status. */
1004
1022
  provider: PublicClient;
1005
- pointTokenAddress: Address;
1023
+ /**
1024
+ * Legacy single-token config. Prefer `pointTokenAddresses` for multi-token
1025
+ * issuers (0.2.0+).
1026
+ */
1027
+ pointTokenAddress?: Address;
1028
+ /**
1029
+ * All supported PointToken addresses. Handlers accept any address in this
1030
+ * list; others are rejected with "unsupported pointToken".
1031
+ */
1032
+ pointTokenAddresses?: Address[];
1006
1033
  chainId: number;
1007
1034
  contracts: ApiConfigResponse["contracts"];
1008
1035
  /** Required by `handleGasFee`; omit to disable the endpoint. */
@@ -1027,7 +1054,15 @@ declare class IssuerApiHandlers {
1027
1054
  private readonly gateway;
1028
1055
  private readonly ledger;
1029
1056
  private readonly provider;
1030
- private readonly pointTokenAddress;
1057
+ /**
1058
+ * Set of supported PointToken addresses (checksum-normalized). Handlers
1059
+ * validate the request's `pointTokenAddress` against this set.
1060
+ */
1061
+ private readonly supportedTokens;
1062
+ /** First supported token — used as default when a handler doesn't
1063
+ * receive a `pointTokenAddress` in the request (shouldn't happen in
1064
+ * practice, but keeps type-narrowing happy). */
1065
+ private readonly defaultToken;
1031
1066
  private readonly chainId;
1032
1067
  private readonly contracts;
1033
1068
  private readonly feeManager?;
@@ -1218,11 +1253,24 @@ declare function createSubgraphNativeUsdtQuoter(config: SubgraphNativeUsdtQuoter
1218
1253
  * falls back to the in-memory dev defaults — that makes the happy path
1219
1254
  * a single-call wire-up while still letting production issuers plug in
1220
1255
  * their own ledger, session store, policy engine, and KMS signer.
1256
+ *
1257
+ * **Multi-token (0.2.0):** Pass `pointTokenAddresses: Address[]` to
1258
+ * support multiple PointTokens under a single issuer backend. Legacy
1259
+ * `pointTokenAddress: Address` still works for single-token deployments.
1260
+ * When both are provided, `pointTokenAddresses` takes precedence.
1221
1261
  */
1222
1262
  interface IssuerServiceConfig {
1223
1263
  chainId: number;
1224
- /** Address of the deployed PointToken (one per issuer instance). */
1225
- pointTokenAddress: Address;
1264
+ /**
1265
+ * Address of the deployed PointToken. Legacy single-token shortcut;
1266
+ * prefer `pointTokenAddresses` for multi-token issuers.
1267
+ */
1268
+ pointTokenAddress?: Address;
1269
+ /**
1270
+ * All PointToken addresses this issuer supports. Takes precedence over
1271
+ * `pointTokenAddress`. Factory creates one `PointIndexer` per address.
1272
+ */
1273
+ pointTokenAddresses?: Address[];
1226
1274
  /** Address of the deployed Relay contract. */
1227
1275
  relayAddress: Address;
1228
1276
  /**
@@ -1295,6 +1343,16 @@ interface IssuerService {
1295
1343
  relayService: RelayService;
1296
1344
  feeManager: FeeManager | undefined;
1297
1345
  gateway: MintingGateway;
1346
+ /**
1347
+ * All indexers keyed by PointToken address. For multi-token issuers there
1348
+ * is one per configured token. Single-token issuers will find one entry.
1349
+ */
1350
+ indexers: Map<Address, PointIndexer>;
1351
+ /**
1352
+ * First indexer. Kept for backward compat with 0.1.x callers that
1353
+ * expected `service.indexer` to be a single instance.
1354
+ * @deprecated use `indexers.get(tokenAddress)` for multi-token.
1355
+ */
1298
1356
  indexer: PointIndexer;
1299
1357
  handlers: IssuerApiHandlers;
1300
1358
  }
@@ -1312,7 +1370,7 @@ interface IssuerService {
1312
1370
  * - `indexer.autoStart` → false
1313
1371
  *
1314
1372
  * Throws synchronously if any required field (`signer`, `provider`,
1315
- * `operatorWallet`, `auth.jwtSecret`, `pointTokenAddress`, ) is missing.
1373
+ * `operatorWallet`, `auth.jwtSecret`, at least one point token) is missing.
1316
1374
  */
1317
1375
  declare function createIssuerService(config: IssuerServiceConfig): IssuerService;
1318
1376
 
package/dist/index.d.ts CHANGED
@@ -18,6 +18,12 @@ interface LockedMintRequest {
18
18
  lockId: string;
19
19
  userAddress: Address;
20
20
  amount: bigint;
21
+ /**
22
+ * Which PointToken this lock belongs to. Added in 0.2.0 for multi-token
23
+ * issuer support. Optional for backward compat with 0.1.x ledgers —
24
+ * single-token consumers can ignore it.
25
+ */
26
+ tokenAddress?: Address;
21
27
  /** Lifecycle status */
22
28
  status: MintingStatus;
23
29
  /** When the lock was created (epoch ms) */
@@ -33,20 +39,28 @@ interface LockedMintRequest {
33
39
  *
34
40
  * Issuers replace the in-memory default with their own database-backed
35
41
  * implementation (Postgres, Redis, etc.).
42
+ *
43
+ * **Multi-token support (0.2.0):**
44
+ * Every mutating method accepts an optional `tokenAddress` parameter so
45
+ * balances can be scoped per `(user, token)`. Single-token issuers can
46
+ * ignore the parameter entirely — legacy 0.1.x implementations remain
47
+ * compatible. Multi-token issuers must persist + query balances keyed by
48
+ * `(userAddress, tokenAddress)`.
36
49
  */
37
50
  interface IPointLedger {
38
51
  /** Get a user's available off-chain point balance (excluding locked). */
39
- getBalance(userAddress: Address): Promise<bigint>;
52
+ getBalance(userAddress: Address, tokenAddress?: Address): Promise<bigint>;
40
53
  /**
41
54
  * Lock an amount for a pending mint request. Locked amounts are reserved
42
55
  * but not yet deducted; they protect against double-spend during the
43
56
  * EIP-712 validity window.
44
57
  *
45
58
  * @param lockDurationMs how long the lock should be held before auto-expiry
59
+ * @param tokenAddress which PointToken this lock is for (0.2.0+)
46
60
  * @returns lockId — opaque handle used by `releaseLock` / `updateMintStatus`
47
61
  * @throws if the user's available balance is below `amount`
48
62
  */
49
- lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number): Promise<string>;
63
+ lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number, tokenAddress?: Address): Promise<string>;
50
64
  /** Release a previously created lock (e.g. on tx failure / cancel). */
51
65
  releaseLock(lockId: string): Promise<void>;
52
66
  /**
@@ -54,11 +68,11 @@ interface IPointLedger {
54
68
  * mint has been observed by the indexer. Should also resolve any matching
55
69
  * lock so the funds aren't double-counted.
56
70
  */
57
- deductBalance(userAddress: Address, amount: bigint, txHash: Hex): Promise<void>;
71
+ deductBalance(userAddress: Address, amount: bigint, txHash: Hex, tokenAddress?: Address): Promise<void>;
58
72
  /** Credit points to a user's balance (e.g. from merchant activity). */
59
- creditBalance(userAddress: Address, amount: bigint, reason: string): Promise<void>;
73
+ creditBalance(userAddress: Address, amount: bigint, reason: string, tokenAddress?: Address): Promise<void>;
60
74
  /** List currently-pending locked mint requests for a user. */
61
- getLockedRequests(userAddress: Address): Promise<LockedMintRequest[]>;
75
+ getLockedRequests(userAddress: Address, tokenAddress?: Address): Promise<LockedMintRequest[]>;
62
76
  /**
63
77
  * Transition a lock to a new lifecycle status. The on-chain tx hash is
64
78
  * supplied when the status is `MINTED`.
@@ -75,6 +89,10 @@ interface IPointLedger {
75
89
  * Concurrency model: single-process, single-threaded (Node.js event loop).
76
90
  * The lock check + insert is atomic within a tick because no awaits sit
77
91
  * between balance read and lock write.
92
+ *
93
+ * **Multi-token (0.2.0):** Balances are keyed by `(user, token)`. If callers
94
+ * omit `tokenAddress`, the literal string "default" is used — that keeps
95
+ * single-token usage working exactly like 0.1.x.
78
96
  */
79
97
  declare class MemoryPointLedger implements IPointLedger {
80
98
  private balances;
@@ -84,12 +102,12 @@ declare class MemoryPointLedger implements IPointLedger {
84
102
  constructor(opts?: {
85
103
  now?: () => number;
86
104
  });
87
- getBalance(userAddress: Address): Promise<bigint>;
88
- getLockedRequests(userAddress: Address): Promise<LockedMintRequest[]>;
89
- creditBalance(userAddress: Address, amount: bigint, _reason: string): Promise<void>;
90
- lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number): Promise<string>;
105
+ getBalance(userAddress: Address, tokenAddress?: Address): Promise<bigint>;
106
+ getLockedRequests(userAddress: Address, tokenAddress?: Address): Promise<LockedMintRequest[]>;
107
+ creditBalance(userAddress: Address, amount: bigint, _reason: string, tokenAddress?: Address): Promise<void>;
108
+ lockForMinting(userAddress: Address, amount: bigint, lockDurationMs: number, tokenAddress?: Address): Promise<string>;
91
109
  releaseLock(lockId: string): Promise<void>;
92
- deductBalance(userAddress: Address, amount: bigint, txHash: Hex): Promise<void>;
110
+ deductBalance(userAddress: Address, amount: bigint, txHash: Hex, tokenAddress?: Address): Promise<void>;
93
111
  updateMintStatus(lockId: string, status: MintingStatus, txHash?: Hex): Promise<void>;
94
112
  /**
95
113
  * Auto-expire any PENDING lock past its expiry. Called lazily on every
@@ -1002,7 +1020,16 @@ interface IssuerApiHandlersConfig {
1002
1020
  ledger: IPointLedger;
1003
1021
  /** Used by `handleUser` to read on-chain nonces and minter status. */
1004
1022
  provider: PublicClient;
1005
- pointTokenAddress: Address;
1023
+ /**
1024
+ * Legacy single-token config. Prefer `pointTokenAddresses` for multi-token
1025
+ * issuers (0.2.0+).
1026
+ */
1027
+ pointTokenAddress?: Address;
1028
+ /**
1029
+ * All supported PointToken addresses. Handlers accept any address in this
1030
+ * list; others are rejected with "unsupported pointToken".
1031
+ */
1032
+ pointTokenAddresses?: Address[];
1006
1033
  chainId: number;
1007
1034
  contracts: ApiConfigResponse["contracts"];
1008
1035
  /** Required by `handleGasFee`; omit to disable the endpoint. */
@@ -1027,7 +1054,15 @@ declare class IssuerApiHandlers {
1027
1054
  private readonly gateway;
1028
1055
  private readonly ledger;
1029
1056
  private readonly provider;
1030
- private readonly pointTokenAddress;
1057
+ /**
1058
+ * Set of supported PointToken addresses (checksum-normalized). Handlers
1059
+ * validate the request's `pointTokenAddress` against this set.
1060
+ */
1061
+ private readonly supportedTokens;
1062
+ /** First supported token — used as default when a handler doesn't
1063
+ * receive a `pointTokenAddress` in the request (shouldn't happen in
1064
+ * practice, but keeps type-narrowing happy). */
1065
+ private readonly defaultToken;
1031
1066
  private readonly chainId;
1032
1067
  private readonly contracts;
1033
1068
  private readonly feeManager?;
@@ -1218,11 +1253,24 @@ declare function createSubgraphNativeUsdtQuoter(config: SubgraphNativeUsdtQuoter
1218
1253
  * falls back to the in-memory dev defaults — that makes the happy path
1219
1254
  * a single-call wire-up while still letting production issuers plug in
1220
1255
  * their own ledger, session store, policy engine, and KMS signer.
1256
+ *
1257
+ * **Multi-token (0.2.0):** Pass `pointTokenAddresses: Address[]` to
1258
+ * support multiple PointTokens under a single issuer backend. Legacy
1259
+ * `pointTokenAddress: Address` still works for single-token deployments.
1260
+ * When both are provided, `pointTokenAddresses` takes precedence.
1221
1261
  */
1222
1262
  interface IssuerServiceConfig {
1223
1263
  chainId: number;
1224
- /** Address of the deployed PointToken (one per issuer instance). */
1225
- pointTokenAddress: Address;
1264
+ /**
1265
+ * Address of the deployed PointToken. Legacy single-token shortcut;
1266
+ * prefer `pointTokenAddresses` for multi-token issuers.
1267
+ */
1268
+ pointTokenAddress?: Address;
1269
+ /**
1270
+ * All PointToken addresses this issuer supports. Takes precedence over
1271
+ * `pointTokenAddress`. Factory creates one `PointIndexer` per address.
1272
+ */
1273
+ pointTokenAddresses?: Address[];
1226
1274
  /** Address of the deployed Relay contract. */
1227
1275
  relayAddress: Address;
1228
1276
  /**
@@ -1295,6 +1343,16 @@ interface IssuerService {
1295
1343
  relayService: RelayService;
1296
1344
  feeManager: FeeManager | undefined;
1297
1345
  gateway: MintingGateway;
1346
+ /**
1347
+ * All indexers keyed by PointToken address. For multi-token issuers there
1348
+ * is one per configured token. Single-token issuers will find one entry.
1349
+ */
1350
+ indexers: Map<Address, PointIndexer>;
1351
+ /**
1352
+ * First indexer. Kept for backward compat with 0.1.x callers that
1353
+ * expected `service.indexer` to be a single instance.
1354
+ * @deprecated use `indexers.get(tokenAddress)` for multi-token.
1355
+ */
1298
1356
  indexer: PointIndexer;
1299
1357
  handlers: IssuerApiHandlers;
1300
1358
  }
@@ -1312,7 +1370,7 @@ interface IssuerService {
1312
1370
  * - `indexer.autoStart` → false
1313
1371
  *
1314
1372
  * Throws synchronously if any required field (`signer`, `provider`,
1315
- * `operatorWallet`, `auth.jwtSecret`, `pointTokenAddress`, ) is missing.
1373
+ * `operatorWallet`, `auth.jwtSecret`, at least one point token) is missing.
1316
1374
  */
1317
1375
  declare function createIssuerService(config: IssuerServiceConfig): IssuerService;
1318
1376