@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.cjs +115 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -15
- package/dist/index.d.ts +73 -15
- package/dist/index.js +115 -57
- 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
|
}
|
|
@@ -1169,7 +1199,15 @@ var IssuerApiHandlers = class {
|
|
|
1169
1199
|
gateway;
|
|
1170
1200
|
ledger;
|
|
1171
1201
|
provider;
|
|
1172
|
-
|
|
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;
|
|
1173
1211
|
chainId;
|
|
1174
1212
|
contracts;
|
|
1175
1213
|
feeManager;
|
|
@@ -1179,7 +1217,15 @@ var IssuerApiHandlers = class {
|
|
|
1179
1217
|
this.gateway = config.gateway;
|
|
1180
1218
|
this.ledger = config.ledger;
|
|
1181
1219
|
this.provider = config.provider;
|
|
1182
|
-
|
|
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];
|
|
1183
1229
|
this.chainId = config.chainId;
|
|
1184
1230
|
this.contracts = config.contracts;
|
|
1185
1231
|
if (config.feeManager) this.feeManager = config.feeManager;
|
|
@@ -1276,7 +1322,7 @@ var IssuerApiHandlers = class {
|
|
|
1276
1322
|
);
|
|
1277
1323
|
}
|
|
1278
1324
|
const pointToken = getAddress5(request.pointTokenAddress);
|
|
1279
|
-
if (
|
|
1325
|
+
if (!this.supportedTokens.has(pointToken)) {
|
|
1280
1326
|
throw new Error(
|
|
1281
1327
|
`handleUser: unsupported pointToken ${pointToken}`
|
|
1282
1328
|
);
|
|
@@ -1284,7 +1330,7 @@ var IssuerApiHandlers = class {
|
|
|
1284
1330
|
const [mintRequestNonce, receiverConsentNonce, offChainBalance, onChainBalance, minter] = await Promise.all([
|
|
1285
1331
|
getMintRequestNonce(this.provider, pointToken, normalizedAuthed),
|
|
1286
1332
|
getReceiverConsentNonce(this.provider, pointToken, normalizedAuthed),
|
|
1287
|
-
this.ledger.getBalance(normalizedAuthed),
|
|
1333
|
+
this.ledger.getBalance(normalizedAuthed, pointToken),
|
|
1288
1334
|
getPointTokenBalance(this.provider, pointToken, normalizedAuthed),
|
|
1289
1335
|
isMinter(this.provider, pointToken, normalizedAuthed)
|
|
1290
1336
|
]);
|
|
@@ -1319,7 +1365,7 @@ var IssuerApiHandlers = class {
|
|
|
1319
1365
|
);
|
|
1320
1366
|
}
|
|
1321
1367
|
const pointToken = getAddress5(request.pointTokenAddress);
|
|
1322
|
-
if (
|
|
1368
|
+
if (!this.supportedTokens.has(pointToken)) {
|
|
1323
1369
|
throw new Error(
|
|
1324
1370
|
`handleBuildConsentTypedData: unsupported pointToken ${pointToken}`
|
|
1325
1371
|
);
|
|
@@ -1353,7 +1399,7 @@ var IssuerApiHandlers = class {
|
|
|
1353
1399
|
);
|
|
1354
1400
|
}
|
|
1355
1401
|
const pointToken = getAddress5(request.pointTokenAddress);
|
|
1356
|
-
if (
|
|
1402
|
+
if (!this.supportedTokens.has(pointToken)) {
|
|
1357
1403
|
throw new Error(
|
|
1358
1404
|
`handleClaimAndSwap: unsupported pointToken ${pointToken}`
|
|
1359
1405
|
);
|
|
@@ -1591,6 +1637,7 @@ function toUsdtPerNative(priceFloat, usdtDecimals) {
|
|
|
1591
1637
|
}
|
|
1592
1638
|
|
|
1593
1639
|
// src/config.ts
|
|
1640
|
+
import { getAddress as getAddress6 } from "viem";
|
|
1594
1641
|
function createIssuerService(config) {
|
|
1595
1642
|
if (!config.provider) {
|
|
1596
1643
|
throw new Error("createIssuerService: provider is required");
|
|
@@ -1601,9 +1648,6 @@ function createIssuerService(config) {
|
|
|
1601
1648
|
if (!config.signer) {
|
|
1602
1649
|
throw new Error("createIssuerService: signer is required");
|
|
1603
1650
|
}
|
|
1604
|
-
if (!config.pointTokenAddress) {
|
|
1605
|
-
throw new Error("createIssuerService: pointTokenAddress is required");
|
|
1606
|
-
}
|
|
1607
1651
|
if (!config.relayAddress) {
|
|
1608
1652
|
throw new Error("createIssuerService: relayAddress is required");
|
|
1609
1653
|
}
|
|
@@ -1613,6 +1657,13 @@ function createIssuerService(config) {
|
|
|
1613
1657
|
if (!config.auth?.domain) {
|
|
1614
1658
|
throw new Error("createIssuerService: auth.domain is required");
|
|
1615
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));
|
|
1616
1667
|
const ledger = config.ledger ?? new MemoryPointLedger();
|
|
1617
1668
|
const sessionStore = config.sessionStore ?? new MemorySessionStore();
|
|
1618
1669
|
const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
|
|
@@ -1656,33 +1707,37 @@ function createIssuerService(config) {
|
|
|
1656
1707
|
gatewayConfig.defaultLockBufferMs = config.gateway.defaultLockBufferMs;
|
|
1657
1708
|
}
|
|
1658
1709
|
const gateway = new MintingGateway(gatewayConfig);
|
|
1659
|
-
const
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
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));
|
|
1678
1733
|
}
|
|
1679
|
-
const
|
|
1734
|
+
const firstIndexer = indexers.get(tokenAddresses[0]);
|
|
1680
1735
|
const handlersConfig = {
|
|
1681
1736
|
authService,
|
|
1682
1737
|
gateway,
|
|
1683
1738
|
ledger,
|
|
1684
1739
|
provider: config.provider,
|
|
1685
|
-
|
|
1740
|
+
pointTokenAddresses: tokenAddresses,
|
|
1686
1741
|
chainId: config.chainId,
|
|
1687
1742
|
contracts: config.contracts
|
|
1688
1743
|
};
|
|
@@ -1690,7 +1745,9 @@ function createIssuerService(config) {
|
|
|
1690
1745
|
if (config.poolsProvider) handlersConfig.poolsProvider = config.poolsProvider;
|
|
1691
1746
|
const handlers = new IssuerApiHandlers(handlersConfig);
|
|
1692
1747
|
if (config.indexer?.autoStart) {
|
|
1693
|
-
|
|
1748
|
+
for (const idx of indexers.values()) {
|
|
1749
|
+
idx.start();
|
|
1750
|
+
}
|
|
1694
1751
|
}
|
|
1695
1752
|
return {
|
|
1696
1753
|
authService,
|
|
@@ -1701,7 +1758,8 @@ function createIssuerService(config) {
|
|
|
1701
1758
|
relayService,
|
|
1702
1759
|
feeManager,
|
|
1703
1760
|
gateway,
|
|
1704
|
-
|
|
1761
|
+
indexers,
|
|
1762
|
+
indexer: firstIndexer,
|
|
1705
1763
|
handlers
|
|
1706
1764
|
};
|
|
1707
1765
|
}
|