@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.js
CHANGED
|
@@ -11,19 +11,21 @@ var MemoryPointLedger = class {
|
|
|
11
11
|
// -------------------------------------------------------------------------
|
|
12
12
|
// Read
|
|
13
13
|
// -------------------------------------------------------------------------
|
|
14
|
-
async getBalance(userAddress) {
|
|
15
|
-
const
|
|
14
|
+
async getBalance(userAddress, tokenAddress) {
|
|
15
|
+
const user = getAddress(userAddress);
|
|
16
|
+
const token = normalizeToken(tokenAddress);
|
|
16
17
|
this.purgeExpired();
|
|
17
|
-
const total = this.balances.get(
|
|
18
|
-
const locked = this.lockedTotalFor(
|
|
18
|
+
const total = this.balances.get(balanceKey(user, token)) ?? 0n;
|
|
19
|
+
const locked = this.lockedTotalFor(user, token);
|
|
19
20
|
return total - locked;
|
|
20
21
|
}
|
|
21
|
-
async getLockedRequests(userAddress) {
|
|
22
|
-
const
|
|
22
|
+
async getLockedRequests(userAddress, tokenAddress) {
|
|
23
|
+
const user = getAddress(userAddress);
|
|
24
|
+
const token = normalizeToken(tokenAddress);
|
|
23
25
|
this.purgeExpired();
|
|
24
26
|
const out = [];
|
|
25
27
|
for (const lock of this.locks.values()) {
|
|
26
|
-
if (lock.userAddress ===
|
|
28
|
+
if (lock.userAddress === user && lock.status === "PENDING" && (lock.tokenAddress ?? DEFAULT_TOKEN_KEY) === token) {
|
|
27
29
|
out.push({ ...lock });
|
|
28
30
|
}
|
|
29
31
|
}
|
|
@@ -32,25 +34,28 @@ var MemoryPointLedger = class {
|
|
|
32
34
|
// -------------------------------------------------------------------------
|
|
33
35
|
// Write
|
|
34
36
|
// -------------------------------------------------------------------------
|
|
35
|
-
async creditBalance(userAddress, amount, _reason) {
|
|
37
|
+
async creditBalance(userAddress, amount, _reason, tokenAddress) {
|
|
36
38
|
if (amount <= 0n) {
|
|
37
39
|
throw new Error("MemoryPointLedger: credit amount must be positive");
|
|
38
40
|
}
|
|
39
|
-
const
|
|
41
|
+
const user = getAddress(userAddress);
|
|
42
|
+
const token = normalizeToken(tokenAddress);
|
|
43
|
+
const key = balanceKey(user, token);
|
|
40
44
|
const current = this.balances.get(key) ?? 0n;
|
|
41
45
|
this.balances.set(key, current + amount);
|
|
42
46
|
}
|
|
43
|
-
async lockForMinting(userAddress, amount, lockDurationMs) {
|
|
47
|
+
async lockForMinting(userAddress, amount, lockDurationMs, tokenAddress) {
|
|
44
48
|
if (amount <= 0n) {
|
|
45
49
|
throw new Error("MemoryPointLedger: lock amount must be positive");
|
|
46
50
|
}
|
|
47
51
|
if (lockDurationMs <= 0) {
|
|
48
52
|
throw new Error("MemoryPointLedger: lockDurationMs must be positive");
|
|
49
53
|
}
|
|
50
|
-
const
|
|
54
|
+
const user = getAddress(userAddress);
|
|
55
|
+
const token = normalizeToken(tokenAddress);
|
|
51
56
|
this.purgeExpired();
|
|
52
|
-
const total = this.balances.get(
|
|
53
|
-
const alreadyLocked = this.lockedTotalFor(
|
|
57
|
+
const total = this.balances.get(balanceKey(user, token)) ?? 0n;
|
|
58
|
+
const alreadyLocked = this.lockedTotalFor(user, token);
|
|
54
59
|
const available = total - alreadyLocked;
|
|
55
60
|
if (available < amount) {
|
|
56
61
|
throw new Error(
|
|
@@ -59,14 +64,18 @@ var MemoryPointLedger = class {
|
|
|
59
64
|
}
|
|
60
65
|
const lockId = `lock-${this.nextLockId++}`;
|
|
61
66
|
const now = this.now();
|
|
62
|
-
|
|
67
|
+
const lock = {
|
|
63
68
|
lockId,
|
|
64
|
-
userAddress:
|
|
69
|
+
userAddress: user,
|
|
65
70
|
amount,
|
|
66
71
|
status: "PENDING",
|
|
67
72
|
createdAt: now,
|
|
68
73
|
expiresAt: now + lockDurationMs
|
|
69
|
-
}
|
|
74
|
+
};
|
|
75
|
+
if (tokenAddress !== void 0) {
|
|
76
|
+
lock.tokenAddress = getAddress(tokenAddress);
|
|
77
|
+
}
|
|
78
|
+
this.locks.set(lockId, lock);
|
|
70
79
|
return lockId;
|
|
71
80
|
}
|
|
72
81
|
async releaseLock(lockId) {
|
|
@@ -76,11 +85,13 @@ var MemoryPointLedger = class {
|
|
|
76
85
|
this.locks.delete(lockId);
|
|
77
86
|
}
|
|
78
87
|
}
|
|
79
|
-
async deductBalance(userAddress, amount, txHash) {
|
|
88
|
+
async deductBalance(userAddress, amount, txHash, tokenAddress) {
|
|
80
89
|
if (amount <= 0n) {
|
|
81
90
|
throw new Error("MemoryPointLedger: deduct amount must be positive");
|
|
82
91
|
}
|
|
83
|
-
const
|
|
92
|
+
const user = getAddress(userAddress);
|
|
93
|
+
const token = normalizeToken(tokenAddress);
|
|
94
|
+
const key = balanceKey(user, token);
|
|
84
95
|
const current = this.balances.get(key) ?? 0n;
|
|
85
96
|
if (current < amount) {
|
|
86
97
|
throw new Error(
|
|
@@ -89,7 +100,7 @@ var MemoryPointLedger = class {
|
|
|
89
100
|
}
|
|
90
101
|
this.balances.set(key, current - amount);
|
|
91
102
|
for (const lock of this.locks.values()) {
|
|
92
|
-
if (lock.userAddress ===
|
|
103
|
+
if (lock.userAddress === user && lock.status === "PENDING" && lock.amount === amount && (lock.tokenAddress ?? DEFAULT_TOKEN_KEY) === token) {
|
|
93
104
|
lock.status = "MINTED";
|
|
94
105
|
lock.txHash = txHash;
|
|
95
106
|
return;
|
|
@@ -119,16 +130,23 @@ var MemoryPointLedger = class {
|
|
|
119
130
|
}
|
|
120
131
|
}
|
|
121
132
|
}
|
|
122
|
-
lockedTotalFor(userAddress) {
|
|
133
|
+
lockedTotalFor(userAddress, tokenKey) {
|
|
123
134
|
let total = 0n;
|
|
124
135
|
for (const lock of this.locks.values()) {
|
|
125
|
-
if (lock.userAddress === userAddress && lock.status === "PENDING") {
|
|
136
|
+
if (lock.userAddress === userAddress && lock.status === "PENDING" && (lock.tokenAddress ?? DEFAULT_TOKEN_KEY) === tokenKey) {
|
|
126
137
|
total += lock.amount;
|
|
127
138
|
}
|
|
128
139
|
}
|
|
129
140
|
return total;
|
|
130
141
|
}
|
|
131
142
|
};
|
|
143
|
+
var DEFAULT_TOKEN_KEY = "default";
|
|
144
|
+
function normalizeToken(tokenAddress) {
|
|
145
|
+
return tokenAddress === void 0 ? DEFAULT_TOKEN_KEY : getAddress(tokenAddress);
|
|
146
|
+
}
|
|
147
|
+
function balanceKey(user, tokenKey) {
|
|
148
|
+
return `${user}|${tokenKey}`;
|
|
149
|
+
}
|
|
132
150
|
|
|
133
151
|
// src/policy/defaultPolicy.ts
|
|
134
152
|
var DefaultPolicyEngine = class {
|
|
@@ -150,7 +168,10 @@ var DefaultPolicyEngine = class {
|
|
|
150
168
|
if (request.amount <= 0n) {
|
|
151
169
|
return { approved: false, reason: "Amount must be positive" };
|
|
152
170
|
}
|
|
153
|
-
const available = await this.ledger.getBalance(
|
|
171
|
+
const available = await this.ledger.getBalance(
|
|
172
|
+
request.userAddress,
|
|
173
|
+
request.pointTokenAddress
|
|
174
|
+
);
|
|
154
175
|
if (available < request.amount) {
|
|
155
176
|
return {
|
|
156
177
|
approved: false,
|
|
@@ -838,7 +859,8 @@ var MintingGateway = class {
|
|
|
838
859
|
lockId = await this.ledger.lockForMinting(
|
|
839
860
|
request.userAddress,
|
|
840
861
|
receiverConsent.amount,
|
|
841
|
-
lockDurationMs
|
|
862
|
+
lockDurationMs,
|
|
863
|
+
request.pointTokenAddress
|
|
842
864
|
);
|
|
843
865
|
} catch (err) {
|
|
844
866
|
throw new MintingGatewayError(
|
|
@@ -1128,11 +1150,19 @@ var PointIndexer = class {
|
|
|
1128
1150
|
* issuer to mint without going through the gateway.
|
|
1129
1151
|
*/
|
|
1130
1152
|
async finalize(evt) {
|
|
1131
|
-
const locks = await this.ledger.getLockedRequests(
|
|
1153
|
+
const locks = await this.ledger.getLockedRequests(
|
|
1154
|
+
evt.to,
|
|
1155
|
+
this.pointTokenAddress
|
|
1156
|
+
);
|
|
1132
1157
|
const match = pickMatchingLock(locks, evt.amount);
|
|
1133
1158
|
if (!match) return;
|
|
1134
1159
|
try {
|
|
1135
|
-
await this.ledger.deductBalance(
|
|
1160
|
+
await this.ledger.deductBalance(
|
|
1161
|
+
evt.to,
|
|
1162
|
+
evt.amount,
|
|
1163
|
+
evt.txHash,
|
|
1164
|
+
this.pointTokenAddress
|
|
1165
|
+
);
|
|
1136
1166
|
} catch {
|
|
1137
1167
|
return;
|
|
1138
1168
|
}
|
|
@@ -1158,6 +1188,7 @@ function pickMatchingLock(locks, amount) {
|
|
|
1158
1188
|
import { getAddress as getAddress5 } from "viem";
|
|
1159
1189
|
import {
|
|
1160
1190
|
getMintRequestNonce,
|
|
1191
|
+
getPointTokenBalance,
|
|
1161
1192
|
getReceiverConsentNonce,
|
|
1162
1193
|
getTokenName,
|
|
1163
1194
|
isMinter,
|
|
@@ -1168,7 +1199,15 @@ var IssuerApiHandlers = class {
|
|
|
1168
1199
|
gateway;
|
|
1169
1200
|
ledger;
|
|
1170
1201
|
provider;
|
|
1171
|
-
|
|
1202
|
+
/**
|
|
1203
|
+
* Set of supported PointToken addresses (checksum-normalized). Handlers
|
|
1204
|
+
* validate the request's `pointTokenAddress` against this set.
|
|
1205
|
+
*/
|
|
1206
|
+
supportedTokens;
|
|
1207
|
+
/** First supported token — used as default when a handler doesn't
|
|
1208
|
+
* receive a `pointTokenAddress` in the request (shouldn't happen in
|
|
1209
|
+
* practice, but keeps type-narrowing happy). */
|
|
1210
|
+
defaultToken;
|
|
1172
1211
|
chainId;
|
|
1173
1212
|
contracts;
|
|
1174
1213
|
feeManager;
|
|
@@ -1178,7 +1217,15 @@ var IssuerApiHandlers = class {
|
|
|
1178
1217
|
this.gateway = config.gateway;
|
|
1179
1218
|
this.ledger = config.ledger;
|
|
1180
1219
|
this.provider = config.provider;
|
|
1181
|
-
|
|
1220
|
+
const raw = config.pointTokenAddresses && config.pointTokenAddresses.length > 0 ? config.pointTokenAddresses : config.pointTokenAddress ? [config.pointTokenAddress] : [];
|
|
1221
|
+
if (raw.length === 0) {
|
|
1222
|
+
throw new Error(
|
|
1223
|
+
"IssuerApiHandlers: pointTokenAddress or pointTokenAddresses required"
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
const normalized = raw.map((a) => getAddress5(a));
|
|
1227
|
+
this.supportedTokens = new Set(normalized);
|
|
1228
|
+
this.defaultToken = normalized[0];
|
|
1182
1229
|
this.chainId = config.chainId;
|
|
1183
1230
|
this.contracts = config.contracts;
|
|
1184
1231
|
if (config.feeManager) this.feeManager = config.feeManager;
|
|
@@ -1275,21 +1322,26 @@ var IssuerApiHandlers = class {
|
|
|
1275
1322
|
);
|
|
1276
1323
|
}
|
|
1277
1324
|
const pointToken = getAddress5(request.pointTokenAddress);
|
|
1278
|
-
if (
|
|
1325
|
+
if (!this.supportedTokens.has(pointToken)) {
|
|
1279
1326
|
throw new Error(
|
|
1280
1327
|
`handleUser: unsupported pointToken ${pointToken}`
|
|
1281
1328
|
);
|
|
1282
1329
|
}
|
|
1283
|
-
const [mintRequestNonce, receiverConsentNonce,
|
|
1330
|
+
const [mintRequestNonce, receiverConsentNonce, offChainBalance, onChainBalance, minter] = await Promise.all([
|
|
1284
1331
|
getMintRequestNonce(this.provider, pointToken, normalizedAuthed),
|
|
1285
1332
|
getReceiverConsentNonce(this.provider, pointToken, normalizedAuthed),
|
|
1286
|
-
this.ledger.getBalance(normalizedAuthed),
|
|
1333
|
+
this.ledger.getBalance(normalizedAuthed, pointToken),
|
|
1334
|
+
getPointTokenBalance(this.provider, pointToken, normalizedAuthed),
|
|
1287
1335
|
isMinter(this.provider, pointToken, normalizedAuthed)
|
|
1288
1336
|
]);
|
|
1289
1337
|
return {
|
|
1290
1338
|
mintRequestNonce,
|
|
1291
1339
|
receiverConsentNonce,
|
|
1292
|
-
|
|
1340
|
+
offChainBalance,
|
|
1341
|
+
onChainBalance,
|
|
1342
|
+
totalBalance: offChainBalance + onChainBalance,
|
|
1343
|
+
balance: offChainBalance,
|
|
1344
|
+
// deprecated alias
|
|
1293
1345
|
isMinter: minter
|
|
1294
1346
|
};
|
|
1295
1347
|
}
|
|
@@ -1313,7 +1365,7 @@ var IssuerApiHandlers = class {
|
|
|
1313
1365
|
);
|
|
1314
1366
|
}
|
|
1315
1367
|
const pointToken = getAddress5(request.pointTokenAddress);
|
|
1316
|
-
if (
|
|
1368
|
+
if (!this.supportedTokens.has(pointToken)) {
|
|
1317
1369
|
throw new Error(
|
|
1318
1370
|
`handleBuildConsentTypedData: unsupported pointToken ${pointToken}`
|
|
1319
1371
|
);
|
|
@@ -1347,7 +1399,7 @@ var IssuerApiHandlers = class {
|
|
|
1347
1399
|
);
|
|
1348
1400
|
}
|
|
1349
1401
|
const pointToken = getAddress5(request.pointTokenAddress);
|
|
1350
|
-
if (
|
|
1402
|
+
if (!this.supportedTokens.has(pointToken)) {
|
|
1351
1403
|
throw new Error(
|
|
1352
1404
|
`handleClaimAndSwap: unsupported pointToken ${pointToken}`
|
|
1353
1405
|
);
|
|
@@ -1585,6 +1637,7 @@ function toUsdtPerNative(priceFloat, usdtDecimals) {
|
|
|
1585
1637
|
}
|
|
1586
1638
|
|
|
1587
1639
|
// src/config.ts
|
|
1640
|
+
import { getAddress as getAddress6 } from "viem";
|
|
1588
1641
|
function createIssuerService(config) {
|
|
1589
1642
|
if (!config.provider) {
|
|
1590
1643
|
throw new Error("createIssuerService: provider is required");
|
|
@@ -1595,9 +1648,6 @@ function createIssuerService(config) {
|
|
|
1595
1648
|
if (!config.signer) {
|
|
1596
1649
|
throw new Error("createIssuerService: signer is required");
|
|
1597
1650
|
}
|
|
1598
|
-
if (!config.pointTokenAddress) {
|
|
1599
|
-
throw new Error("createIssuerService: pointTokenAddress is required");
|
|
1600
|
-
}
|
|
1601
1651
|
if (!config.relayAddress) {
|
|
1602
1652
|
throw new Error("createIssuerService: relayAddress is required");
|
|
1603
1653
|
}
|
|
@@ -1607,6 +1657,13 @@ function createIssuerService(config) {
|
|
|
1607
1657
|
if (!config.auth?.domain) {
|
|
1608
1658
|
throw new Error("createIssuerService: auth.domain is required");
|
|
1609
1659
|
}
|
|
1660
|
+
const rawAddresses = config.pointTokenAddresses && config.pointTokenAddresses.length > 0 ? config.pointTokenAddresses : config.pointTokenAddress ? [config.pointTokenAddress] : [];
|
|
1661
|
+
if (rawAddresses.length === 0) {
|
|
1662
|
+
throw new Error(
|
|
1663
|
+
"createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
const tokenAddresses = rawAddresses.map((a) => getAddress6(a));
|
|
1610
1667
|
const ledger = config.ledger ?? new MemoryPointLedger();
|
|
1611
1668
|
const sessionStore = config.sessionStore ?? new MemorySessionStore();
|
|
1612
1669
|
const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
|
|
@@ -1650,33 +1707,37 @@ function createIssuerService(config) {
|
|
|
1650
1707
|
gatewayConfig.defaultLockBufferMs = config.gateway.defaultLockBufferMs;
|
|
1651
1708
|
}
|
|
1652
1709
|
const gateway = new MintingGateway(gatewayConfig);
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1710
|
+
const indexers = /* @__PURE__ */ new Map();
|
|
1711
|
+
for (const tokenAddress of tokenAddresses) {
|
|
1712
|
+
const indexerConfig = {
|
|
1713
|
+
provider: config.provider,
|
|
1714
|
+
pointTokenAddress: tokenAddress,
|
|
1715
|
+
ledger
|
|
1716
|
+
};
|
|
1717
|
+
if (config.indexer?.fromBlock !== void 0) {
|
|
1718
|
+
indexerConfig.fromBlock = config.indexer.fromBlock;
|
|
1719
|
+
}
|
|
1720
|
+
if (config.indexer?.cursorStore) {
|
|
1721
|
+
indexerConfig.cursorStore = config.indexer.cursorStore;
|
|
1722
|
+
}
|
|
1723
|
+
if (config.indexer?.confirmations !== void 0) {
|
|
1724
|
+
indexerConfig.confirmations = config.indexer.confirmations;
|
|
1725
|
+
}
|
|
1726
|
+
if (config.indexer?.batchSize !== void 0) {
|
|
1727
|
+
indexerConfig.batchSize = config.indexer.batchSize;
|
|
1728
|
+
}
|
|
1729
|
+
if (config.indexer?.pollIntervalMs !== void 0) {
|
|
1730
|
+
indexerConfig.pollIntervalMs = config.indexer.pollIntervalMs;
|
|
1731
|
+
}
|
|
1732
|
+
indexers.set(tokenAddress, new PointIndexer(indexerConfig));
|
|
1672
1733
|
}
|
|
1673
|
-
const
|
|
1734
|
+
const firstIndexer = indexers.get(tokenAddresses[0]);
|
|
1674
1735
|
const handlersConfig = {
|
|
1675
1736
|
authService,
|
|
1676
1737
|
gateway,
|
|
1677
1738
|
ledger,
|
|
1678
1739
|
provider: config.provider,
|
|
1679
|
-
|
|
1740
|
+
pointTokenAddresses: tokenAddresses,
|
|
1680
1741
|
chainId: config.chainId,
|
|
1681
1742
|
contracts: config.contracts
|
|
1682
1743
|
};
|
|
@@ -1684,7 +1745,9 @@ function createIssuerService(config) {
|
|
|
1684
1745
|
if (config.poolsProvider) handlersConfig.poolsProvider = config.poolsProvider;
|
|
1685
1746
|
const handlers = new IssuerApiHandlers(handlersConfig);
|
|
1686
1747
|
if (config.indexer?.autoStart) {
|
|
1687
|
-
|
|
1748
|
+
for (const idx of indexers.values()) {
|
|
1749
|
+
idx.start();
|
|
1750
|
+
}
|
|
1688
1751
|
}
|
|
1689
1752
|
return {
|
|
1690
1753
|
authService,
|
|
@@ -1695,7 +1758,8 @@ function createIssuerService(config) {
|
|
|
1695
1758
|
relayService,
|
|
1696
1759
|
feeManager,
|
|
1697
1760
|
gateway,
|
|
1698
|
-
|
|
1761
|
+
indexers,
|
|
1762
|
+
indexer: firstIndexer,
|
|
1699
1763
|
handlers
|
|
1700
1764
|
};
|
|
1701
1765
|
}
|