@pafi-dev/issuer 0.1.1 → 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.cjs +122 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -15
- package/dist/index.d.ts +92 -15
- package/dist/index.js +123 -59
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -57,19 +57,21 @@ var MemoryPointLedger = class {
|
|
|
57
57
|
// -------------------------------------------------------------------------
|
|
58
58
|
// Read
|
|
59
59
|
// -------------------------------------------------------------------------
|
|
60
|
-
async getBalance(userAddress) {
|
|
61
|
-
const
|
|
60
|
+
async getBalance(userAddress, tokenAddress) {
|
|
61
|
+
const user = (0, import_viem.getAddress)(userAddress);
|
|
62
|
+
const token = normalizeToken(tokenAddress);
|
|
62
63
|
this.purgeExpired();
|
|
63
|
-
const total = this.balances.get(
|
|
64
|
-
const locked = this.lockedTotalFor(
|
|
64
|
+
const total = this.balances.get(balanceKey(user, token)) ?? 0n;
|
|
65
|
+
const locked = this.lockedTotalFor(user, token);
|
|
65
66
|
return total - locked;
|
|
66
67
|
}
|
|
67
|
-
async getLockedRequests(userAddress) {
|
|
68
|
-
const
|
|
68
|
+
async getLockedRequests(userAddress, tokenAddress) {
|
|
69
|
+
const user = (0, import_viem.getAddress)(userAddress);
|
|
70
|
+
const token = normalizeToken(tokenAddress);
|
|
69
71
|
this.purgeExpired();
|
|
70
72
|
const out = [];
|
|
71
73
|
for (const lock of this.locks.values()) {
|
|
72
|
-
if (lock.userAddress ===
|
|
74
|
+
if (lock.userAddress === user && lock.status === "PENDING" && (lock.tokenAddress ?? DEFAULT_TOKEN_KEY) === token) {
|
|
73
75
|
out.push({ ...lock });
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -78,25 +80,28 @@ var MemoryPointLedger = class {
|
|
|
78
80
|
// -------------------------------------------------------------------------
|
|
79
81
|
// Write
|
|
80
82
|
// -------------------------------------------------------------------------
|
|
81
|
-
async creditBalance(userAddress, amount, _reason) {
|
|
83
|
+
async creditBalance(userAddress, amount, _reason, tokenAddress) {
|
|
82
84
|
if (amount <= 0n) {
|
|
83
85
|
throw new Error("MemoryPointLedger: credit amount must be positive");
|
|
84
86
|
}
|
|
85
|
-
const
|
|
87
|
+
const user = (0, import_viem.getAddress)(userAddress);
|
|
88
|
+
const token = normalizeToken(tokenAddress);
|
|
89
|
+
const key = balanceKey(user, token);
|
|
86
90
|
const current = this.balances.get(key) ?? 0n;
|
|
87
91
|
this.balances.set(key, current + amount);
|
|
88
92
|
}
|
|
89
|
-
async lockForMinting(userAddress, amount, lockDurationMs) {
|
|
93
|
+
async lockForMinting(userAddress, amount, lockDurationMs, tokenAddress) {
|
|
90
94
|
if (amount <= 0n) {
|
|
91
95
|
throw new Error("MemoryPointLedger: lock amount must be positive");
|
|
92
96
|
}
|
|
93
97
|
if (lockDurationMs <= 0) {
|
|
94
98
|
throw new Error("MemoryPointLedger: lockDurationMs must be positive");
|
|
95
99
|
}
|
|
96
|
-
const
|
|
100
|
+
const user = (0, import_viem.getAddress)(userAddress);
|
|
101
|
+
const token = normalizeToken(tokenAddress);
|
|
97
102
|
this.purgeExpired();
|
|
98
|
-
const total = this.balances.get(
|
|
99
|
-
const alreadyLocked = this.lockedTotalFor(
|
|
103
|
+
const total = this.balances.get(balanceKey(user, token)) ?? 0n;
|
|
104
|
+
const alreadyLocked = this.lockedTotalFor(user, token);
|
|
100
105
|
const available = total - alreadyLocked;
|
|
101
106
|
if (available < amount) {
|
|
102
107
|
throw new Error(
|
|
@@ -105,14 +110,18 @@ var MemoryPointLedger = class {
|
|
|
105
110
|
}
|
|
106
111
|
const lockId = `lock-${this.nextLockId++}`;
|
|
107
112
|
const now = this.now();
|
|
108
|
-
|
|
113
|
+
const lock = {
|
|
109
114
|
lockId,
|
|
110
|
-
userAddress:
|
|
115
|
+
userAddress: user,
|
|
111
116
|
amount,
|
|
112
117
|
status: "PENDING",
|
|
113
118
|
createdAt: now,
|
|
114
119
|
expiresAt: now + lockDurationMs
|
|
115
|
-
}
|
|
120
|
+
};
|
|
121
|
+
if (tokenAddress !== void 0) {
|
|
122
|
+
lock.tokenAddress = (0, import_viem.getAddress)(tokenAddress);
|
|
123
|
+
}
|
|
124
|
+
this.locks.set(lockId, lock);
|
|
116
125
|
return lockId;
|
|
117
126
|
}
|
|
118
127
|
async releaseLock(lockId) {
|
|
@@ -122,11 +131,13 @@ var MemoryPointLedger = class {
|
|
|
122
131
|
this.locks.delete(lockId);
|
|
123
132
|
}
|
|
124
133
|
}
|
|
125
|
-
async deductBalance(userAddress, amount, txHash) {
|
|
134
|
+
async deductBalance(userAddress, amount, txHash, tokenAddress) {
|
|
126
135
|
if (amount <= 0n) {
|
|
127
136
|
throw new Error("MemoryPointLedger: deduct amount must be positive");
|
|
128
137
|
}
|
|
129
|
-
const
|
|
138
|
+
const user = (0, import_viem.getAddress)(userAddress);
|
|
139
|
+
const token = normalizeToken(tokenAddress);
|
|
140
|
+
const key = balanceKey(user, token);
|
|
130
141
|
const current = this.balances.get(key) ?? 0n;
|
|
131
142
|
if (current < amount) {
|
|
132
143
|
throw new Error(
|
|
@@ -135,7 +146,7 @@ var MemoryPointLedger = class {
|
|
|
135
146
|
}
|
|
136
147
|
this.balances.set(key, current - amount);
|
|
137
148
|
for (const lock of this.locks.values()) {
|
|
138
|
-
if (lock.userAddress ===
|
|
149
|
+
if (lock.userAddress === user && lock.status === "PENDING" && lock.amount === amount && (lock.tokenAddress ?? DEFAULT_TOKEN_KEY) === token) {
|
|
139
150
|
lock.status = "MINTED";
|
|
140
151
|
lock.txHash = txHash;
|
|
141
152
|
return;
|
|
@@ -165,16 +176,23 @@ var MemoryPointLedger = class {
|
|
|
165
176
|
}
|
|
166
177
|
}
|
|
167
178
|
}
|
|
168
|
-
lockedTotalFor(userAddress) {
|
|
179
|
+
lockedTotalFor(userAddress, tokenKey) {
|
|
169
180
|
let total = 0n;
|
|
170
181
|
for (const lock of this.locks.values()) {
|
|
171
|
-
if (lock.userAddress === userAddress && lock.status === "PENDING") {
|
|
182
|
+
if (lock.userAddress === userAddress && lock.status === "PENDING" && (lock.tokenAddress ?? DEFAULT_TOKEN_KEY) === tokenKey) {
|
|
172
183
|
total += lock.amount;
|
|
173
184
|
}
|
|
174
185
|
}
|
|
175
186
|
return total;
|
|
176
187
|
}
|
|
177
188
|
};
|
|
189
|
+
var DEFAULT_TOKEN_KEY = "default";
|
|
190
|
+
function normalizeToken(tokenAddress) {
|
|
191
|
+
return tokenAddress === void 0 ? DEFAULT_TOKEN_KEY : (0, import_viem.getAddress)(tokenAddress);
|
|
192
|
+
}
|
|
193
|
+
function balanceKey(user, tokenKey) {
|
|
194
|
+
return `${user}|${tokenKey}`;
|
|
195
|
+
}
|
|
178
196
|
|
|
179
197
|
// src/policy/defaultPolicy.ts
|
|
180
198
|
var DefaultPolicyEngine = class {
|
|
@@ -196,7 +214,10 @@ var DefaultPolicyEngine = class {
|
|
|
196
214
|
if (request.amount <= 0n) {
|
|
197
215
|
return { approved: false, reason: "Amount must be positive" };
|
|
198
216
|
}
|
|
199
|
-
const available = await this.ledger.getBalance(
|
|
217
|
+
const available = await this.ledger.getBalance(
|
|
218
|
+
request.userAddress,
|
|
219
|
+
request.pointTokenAddress
|
|
220
|
+
);
|
|
200
221
|
if (available < request.amount) {
|
|
201
222
|
return {
|
|
202
223
|
approved: false,
|
|
@@ -874,7 +895,8 @@ var MintingGateway = class {
|
|
|
874
895
|
lockId = await this.ledger.lockForMinting(
|
|
875
896
|
request.userAddress,
|
|
876
897
|
receiverConsent.amount,
|
|
877
|
-
lockDurationMs
|
|
898
|
+
lockDurationMs,
|
|
899
|
+
request.pointTokenAddress
|
|
878
900
|
);
|
|
879
901
|
} catch (err) {
|
|
880
902
|
throw new MintingGatewayError(
|
|
@@ -1164,11 +1186,19 @@ var PointIndexer = class {
|
|
|
1164
1186
|
* issuer to mint without going through the gateway.
|
|
1165
1187
|
*/
|
|
1166
1188
|
async finalize(evt) {
|
|
1167
|
-
const locks = await this.ledger.getLockedRequests(
|
|
1189
|
+
const locks = await this.ledger.getLockedRequests(
|
|
1190
|
+
evt.to,
|
|
1191
|
+
this.pointTokenAddress
|
|
1192
|
+
);
|
|
1168
1193
|
const match = pickMatchingLock(locks, evt.amount);
|
|
1169
1194
|
if (!match) return;
|
|
1170
1195
|
try {
|
|
1171
|
-
await this.ledger.deductBalance(
|
|
1196
|
+
await this.ledger.deductBalance(
|
|
1197
|
+
evt.to,
|
|
1198
|
+
evt.amount,
|
|
1199
|
+
evt.txHash,
|
|
1200
|
+
this.pointTokenAddress
|
|
1201
|
+
);
|
|
1172
1202
|
} catch {
|
|
1173
1203
|
return;
|
|
1174
1204
|
}
|
|
@@ -1198,7 +1228,15 @@ var IssuerApiHandlers = class {
|
|
|
1198
1228
|
gateway;
|
|
1199
1229
|
ledger;
|
|
1200
1230
|
provider;
|
|
1201
|
-
|
|
1231
|
+
/**
|
|
1232
|
+
* Set of supported PointToken addresses (checksum-normalized). Handlers
|
|
1233
|
+
* validate the request's `pointTokenAddress` against this set.
|
|
1234
|
+
*/
|
|
1235
|
+
supportedTokens;
|
|
1236
|
+
/** First supported token — used as default when a handler doesn't
|
|
1237
|
+
* receive a `pointTokenAddress` in the request (shouldn't happen in
|
|
1238
|
+
* practice, but keeps type-narrowing happy). */
|
|
1239
|
+
defaultToken;
|
|
1202
1240
|
chainId;
|
|
1203
1241
|
contracts;
|
|
1204
1242
|
feeManager;
|
|
@@ -1208,7 +1246,15 @@ var IssuerApiHandlers = class {
|
|
|
1208
1246
|
this.gateway = config.gateway;
|
|
1209
1247
|
this.ledger = config.ledger;
|
|
1210
1248
|
this.provider = config.provider;
|
|
1211
|
-
|
|
1249
|
+
const raw = config.pointTokenAddresses && config.pointTokenAddresses.length > 0 ? config.pointTokenAddresses : config.pointTokenAddress ? [config.pointTokenAddress] : [];
|
|
1250
|
+
if (raw.length === 0) {
|
|
1251
|
+
throw new Error(
|
|
1252
|
+
"IssuerApiHandlers: pointTokenAddress or pointTokenAddresses required"
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
const normalized = raw.map((a) => (0, import_viem6.getAddress)(a));
|
|
1256
|
+
this.supportedTokens = new Set(normalized);
|
|
1257
|
+
this.defaultToken = normalized[0];
|
|
1212
1258
|
this.chainId = config.chainId;
|
|
1213
1259
|
this.contracts = config.contracts;
|
|
1214
1260
|
if (config.feeManager) this.feeManager = config.feeManager;
|
|
@@ -1305,21 +1351,26 @@ var IssuerApiHandlers = class {
|
|
|
1305
1351
|
);
|
|
1306
1352
|
}
|
|
1307
1353
|
const pointToken = (0, import_viem6.getAddress)(request.pointTokenAddress);
|
|
1308
|
-
if (
|
|
1354
|
+
if (!this.supportedTokens.has(pointToken)) {
|
|
1309
1355
|
throw new Error(
|
|
1310
1356
|
`handleUser: unsupported pointToken ${pointToken}`
|
|
1311
1357
|
);
|
|
1312
1358
|
}
|
|
1313
|
-
const [mintRequestNonce, receiverConsentNonce,
|
|
1359
|
+
const [mintRequestNonce, receiverConsentNonce, offChainBalance, onChainBalance, minter] = await Promise.all([
|
|
1314
1360
|
(0, import_core5.getMintRequestNonce)(this.provider, pointToken, normalizedAuthed),
|
|
1315
1361
|
(0, import_core5.getReceiverConsentNonce)(this.provider, pointToken, normalizedAuthed),
|
|
1316
|
-
this.ledger.getBalance(normalizedAuthed),
|
|
1362
|
+
this.ledger.getBalance(normalizedAuthed, pointToken),
|
|
1363
|
+
(0, import_core5.getPointTokenBalance)(this.provider, pointToken, normalizedAuthed),
|
|
1317
1364
|
(0, import_core5.isMinter)(this.provider, pointToken, normalizedAuthed)
|
|
1318
1365
|
]);
|
|
1319
1366
|
return {
|
|
1320
1367
|
mintRequestNonce,
|
|
1321
1368
|
receiverConsentNonce,
|
|
1322
|
-
|
|
1369
|
+
offChainBalance,
|
|
1370
|
+
onChainBalance,
|
|
1371
|
+
totalBalance: offChainBalance + onChainBalance,
|
|
1372
|
+
balance: offChainBalance,
|
|
1373
|
+
// deprecated alias
|
|
1323
1374
|
isMinter: minter
|
|
1324
1375
|
};
|
|
1325
1376
|
}
|
|
@@ -1343,7 +1394,7 @@ var IssuerApiHandlers = class {
|
|
|
1343
1394
|
);
|
|
1344
1395
|
}
|
|
1345
1396
|
const pointToken = (0, import_viem6.getAddress)(request.pointTokenAddress);
|
|
1346
|
-
if (
|
|
1397
|
+
if (!this.supportedTokens.has(pointToken)) {
|
|
1347
1398
|
throw new Error(
|
|
1348
1399
|
`handleBuildConsentTypedData: unsupported pointToken ${pointToken}`
|
|
1349
1400
|
);
|
|
@@ -1377,7 +1428,7 @@ var IssuerApiHandlers = class {
|
|
|
1377
1428
|
);
|
|
1378
1429
|
}
|
|
1379
1430
|
const pointToken = (0, import_viem6.getAddress)(request.pointTokenAddress);
|
|
1380
|
-
if (
|
|
1431
|
+
if (!this.supportedTokens.has(pointToken)) {
|
|
1381
1432
|
throw new Error(
|
|
1382
1433
|
`handleClaimAndSwap: unsupported pointToken ${pointToken}`
|
|
1383
1434
|
);
|
|
@@ -1615,6 +1666,7 @@ function toUsdtPerNative(priceFloat, usdtDecimals) {
|
|
|
1615
1666
|
}
|
|
1616
1667
|
|
|
1617
1668
|
// src/config.ts
|
|
1669
|
+
var import_viem7 = require("viem");
|
|
1618
1670
|
function createIssuerService(config) {
|
|
1619
1671
|
if (!config.provider) {
|
|
1620
1672
|
throw new Error("createIssuerService: provider is required");
|
|
@@ -1625,9 +1677,6 @@ function createIssuerService(config) {
|
|
|
1625
1677
|
if (!config.signer) {
|
|
1626
1678
|
throw new Error("createIssuerService: signer is required");
|
|
1627
1679
|
}
|
|
1628
|
-
if (!config.pointTokenAddress) {
|
|
1629
|
-
throw new Error("createIssuerService: pointTokenAddress is required");
|
|
1630
|
-
}
|
|
1631
1680
|
if (!config.relayAddress) {
|
|
1632
1681
|
throw new Error("createIssuerService: relayAddress is required");
|
|
1633
1682
|
}
|
|
@@ -1637,6 +1686,13 @@ function createIssuerService(config) {
|
|
|
1637
1686
|
if (!config.auth?.domain) {
|
|
1638
1687
|
throw new Error("createIssuerService: auth.domain is required");
|
|
1639
1688
|
}
|
|
1689
|
+
const rawAddresses = config.pointTokenAddresses && config.pointTokenAddresses.length > 0 ? config.pointTokenAddresses : config.pointTokenAddress ? [config.pointTokenAddress] : [];
|
|
1690
|
+
if (rawAddresses.length === 0) {
|
|
1691
|
+
throw new Error(
|
|
1692
|
+
"createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
const tokenAddresses = rawAddresses.map((a) => (0, import_viem7.getAddress)(a));
|
|
1640
1696
|
const ledger = config.ledger ?? new MemoryPointLedger();
|
|
1641
1697
|
const sessionStore = config.sessionStore ?? new MemorySessionStore();
|
|
1642
1698
|
const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
|
|
@@ -1680,33 +1736,37 @@ function createIssuerService(config) {
|
|
|
1680
1736
|
gatewayConfig.defaultLockBufferMs = config.gateway.defaultLockBufferMs;
|
|
1681
1737
|
}
|
|
1682
1738
|
const gateway = new MintingGateway(gatewayConfig);
|
|
1683
|
-
const
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1739
|
+
const indexers = /* @__PURE__ */ new Map();
|
|
1740
|
+
for (const tokenAddress of tokenAddresses) {
|
|
1741
|
+
const indexerConfig = {
|
|
1742
|
+
provider: config.provider,
|
|
1743
|
+
pointTokenAddress: tokenAddress,
|
|
1744
|
+
ledger
|
|
1745
|
+
};
|
|
1746
|
+
if (config.indexer?.fromBlock !== void 0) {
|
|
1747
|
+
indexerConfig.fromBlock = config.indexer.fromBlock;
|
|
1748
|
+
}
|
|
1749
|
+
if (config.indexer?.cursorStore) {
|
|
1750
|
+
indexerConfig.cursorStore = config.indexer.cursorStore;
|
|
1751
|
+
}
|
|
1752
|
+
if (config.indexer?.confirmations !== void 0) {
|
|
1753
|
+
indexerConfig.confirmations = config.indexer.confirmations;
|
|
1754
|
+
}
|
|
1755
|
+
if (config.indexer?.batchSize !== void 0) {
|
|
1756
|
+
indexerConfig.batchSize = config.indexer.batchSize;
|
|
1757
|
+
}
|
|
1758
|
+
if (config.indexer?.pollIntervalMs !== void 0) {
|
|
1759
|
+
indexerConfig.pollIntervalMs = config.indexer.pollIntervalMs;
|
|
1760
|
+
}
|
|
1761
|
+
indexers.set(tokenAddress, new PointIndexer(indexerConfig));
|
|
1702
1762
|
}
|
|
1703
|
-
const
|
|
1763
|
+
const firstIndexer = indexers.get(tokenAddresses[0]);
|
|
1704
1764
|
const handlersConfig = {
|
|
1705
1765
|
authService,
|
|
1706
1766
|
gateway,
|
|
1707
1767
|
ledger,
|
|
1708
1768
|
provider: config.provider,
|
|
1709
|
-
|
|
1769
|
+
pointTokenAddresses: tokenAddresses,
|
|
1710
1770
|
chainId: config.chainId,
|
|
1711
1771
|
contracts: config.contracts
|
|
1712
1772
|
};
|
|
@@ -1714,7 +1774,9 @@ function createIssuerService(config) {
|
|
|
1714
1774
|
if (config.poolsProvider) handlersConfig.poolsProvider = config.poolsProvider;
|
|
1715
1775
|
const handlers = new IssuerApiHandlers(handlersConfig);
|
|
1716
1776
|
if (config.indexer?.autoStart) {
|
|
1717
|
-
|
|
1777
|
+
for (const idx of indexers.values()) {
|
|
1778
|
+
idx.start();
|
|
1779
|
+
}
|
|
1718
1780
|
}
|
|
1719
1781
|
return {
|
|
1720
1782
|
authService,
|
|
@@ -1725,7 +1787,8 @@ function createIssuerService(config) {
|
|
|
1725
1787
|
relayService,
|
|
1726
1788
|
feeManager,
|
|
1727
1789
|
gateway,
|
|
1728
|
-
|
|
1790
|
+
indexers,
|
|
1791
|
+
indexer: firstIndexer,
|
|
1729
1792
|
handlers
|
|
1730
1793
|
};
|
|
1731
1794
|
}
|