@pafi-dev/issuer 0.20.0 → 0.22.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/README.md +48 -7
- package/dist/index.cjs +188 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +195 -19
- package/dist/index.d.ts +195 -19
- package/dist/index.js +182 -54
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -1391,6 +1391,10 @@ var DEFAULT_BATCH_SIZE2 = 2000n;
|
|
|
1391
1391
|
var DEFAULT_POLL_INTERVAL_MS2 = 5e3;
|
|
1392
1392
|
var BurnIndexer = class {
|
|
1393
1393
|
provider;
|
|
1394
|
+
/**
|
|
1395
|
+
* The PointToken this indexer watches. Exposed so callers can key
|
|
1396
|
+
* leader-election locks / cursor stores by token (audit H-04 fix).
|
|
1397
|
+
*/
|
|
1394
1398
|
pointTokenAddress;
|
|
1395
1399
|
ledger;
|
|
1396
1400
|
cursorStore;
|
|
@@ -1550,6 +1554,45 @@ var BurnIndexer = class {
|
|
|
1550
1554
|
}
|
|
1551
1555
|
};
|
|
1552
1556
|
|
|
1557
|
+
// src/indexer/postgresSingletonLock.ts
|
|
1558
|
+
function makePostgresSingletonLock(runner) {
|
|
1559
|
+
return {
|
|
1560
|
+
async acquire(key) {
|
|
1561
|
+
const lockId = hashKeyToInt64(key);
|
|
1562
|
+
const rows = await runner.query(
|
|
1563
|
+
"SELECT pg_try_advisory_lock($1::bigint) AS got",
|
|
1564
|
+
[lockId]
|
|
1565
|
+
);
|
|
1566
|
+
const got = rows[0]?.got === true;
|
|
1567
|
+
if (!got) return null;
|
|
1568
|
+
return {
|
|
1569
|
+
async release() {
|
|
1570
|
+
try {
|
|
1571
|
+
await runner.query("SELECT pg_advisory_unlock($1::bigint)", [
|
|
1572
|
+
lockId
|
|
1573
|
+
]);
|
|
1574
|
+
} catch {
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
function hashKeyToInt64(key) {
|
|
1582
|
+
const FNV_OFFSET = 0xcbf29ce484222325n;
|
|
1583
|
+
const FNV_PRIME = 0x100000001b3n;
|
|
1584
|
+
const MASK_64 = (1n << 64n) - 1n;
|
|
1585
|
+
let hash = FNV_OFFSET;
|
|
1586
|
+
for (let i = 0; i < key.length; i++) {
|
|
1587
|
+
hash ^= BigInt(key.charCodeAt(i));
|
|
1588
|
+
hash = hash * FNV_PRIME & MASK_64;
|
|
1589
|
+
}
|
|
1590
|
+
const SIGNED_MAX = (1n << 63n) - 1n;
|
|
1591
|
+
const TWO64 = 1n << 64n;
|
|
1592
|
+
const signed = hash > SIGNED_MAX ? hash - TWO64 : hash;
|
|
1593
|
+
return signed.toString();
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1553
1596
|
// src/api/handlers.ts
|
|
1554
1597
|
import { getAddress as getAddress5 } from "viem";
|
|
1555
1598
|
import {
|
|
@@ -1915,8 +1958,59 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
1915
1958
|
}
|
|
1916
1959
|
};
|
|
1917
1960
|
|
|
1918
|
-
// src/api/
|
|
1961
|
+
// src/api/pointTokenDomainResolver.ts
|
|
1919
1962
|
import { getAddress as getAddress6 } from "viem";
|
|
1963
|
+
var NAME_ABI = [
|
|
1964
|
+
{
|
|
1965
|
+
type: "function",
|
|
1966
|
+
name: "name",
|
|
1967
|
+
stateMutability: "view",
|
|
1968
|
+
inputs: [],
|
|
1969
|
+
outputs: [{ type: "string" }]
|
|
1970
|
+
}
|
|
1971
|
+
];
|
|
1972
|
+
var PointTokenDomainResolver = class {
|
|
1973
|
+
provider;
|
|
1974
|
+
overrides;
|
|
1975
|
+
cache = /* @__PURE__ */ new Map();
|
|
1976
|
+
constructor(config) {
|
|
1977
|
+
this.provider = config.provider;
|
|
1978
|
+
this.overrides = /* @__PURE__ */ new Map();
|
|
1979
|
+
if (config.overrides) {
|
|
1980
|
+
for (const [addr, name] of Object.entries(config.overrides)) {
|
|
1981
|
+
this.overrides.set(getAddress6(addr).toLowerCase(), name);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
async resolve(pointTokenAddress) {
|
|
1986
|
+
const key = getAddress6(pointTokenAddress).toLowerCase();
|
|
1987
|
+
const cached = this.cache.get(key);
|
|
1988
|
+
if (cached !== void 0) return cached;
|
|
1989
|
+
const override = this.overrides.get(key);
|
|
1990
|
+
if (override !== void 0) {
|
|
1991
|
+
this.cache.set(key, override);
|
|
1992
|
+
return override;
|
|
1993
|
+
}
|
|
1994
|
+
const name = await this.provider.readContract({
|
|
1995
|
+
address: pointTokenAddress,
|
|
1996
|
+
abi: NAME_ABI,
|
|
1997
|
+
functionName: "name"
|
|
1998
|
+
});
|
|
1999
|
+
this.cache.set(key, name);
|
|
2000
|
+
return name;
|
|
2001
|
+
}
|
|
2002
|
+
/** Invalidate one address (after deploy / proxy upgrade) or all. */
|
|
2003
|
+
invalidate(pointTokenAddress) {
|
|
2004
|
+
if (pointTokenAddress) {
|
|
2005
|
+
this.cache.delete(getAddress6(pointTokenAddress).toLowerCase());
|
|
2006
|
+
} else {
|
|
2007
|
+
this.cache.clear();
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
2011
|
+
|
|
2012
|
+
// src/api/handlers/ptRedeemHandler.ts
|
|
2013
|
+
import { getAddress as getAddress7 } from "viem";
|
|
1920
2014
|
import {
|
|
1921
2015
|
signBurnRequest,
|
|
1922
2016
|
POINT_TOKEN_ABI as POINT_TOKEN_ABI2,
|
|
@@ -1942,10 +2036,9 @@ var PTRedeemHandler = class {
|
|
|
1942
2036
|
relayService;
|
|
1943
2037
|
provider;
|
|
1944
2038
|
feeService;
|
|
1945
|
-
pointTokenAddress;
|
|
1946
2039
|
batchExecutorAddress;
|
|
1947
2040
|
chainId;
|
|
1948
|
-
|
|
2041
|
+
domainResolver;
|
|
1949
2042
|
burnerSignerWallet;
|
|
1950
2043
|
redeemLockDurationMs;
|
|
1951
2044
|
signatureDeadlineSeconds;
|
|
@@ -1982,10 +2075,9 @@ var PTRedeemHandler = class {
|
|
|
1982
2075
|
this.relayService = config.relayService;
|
|
1983
2076
|
this.provider = config.provider;
|
|
1984
2077
|
this.feeService = config.feeService;
|
|
1985
|
-
this.
|
|
1986
|
-
this.batchExecutorAddress = getAddress6(config.batchExecutorAddress);
|
|
2078
|
+
this.batchExecutorAddress = getAddress7(config.batchExecutorAddress);
|
|
1987
2079
|
this.chainId = config.chainId;
|
|
1988
|
-
this.
|
|
2080
|
+
this.domainResolver = config.domainResolver;
|
|
1989
2081
|
this.burnerSignerWallet = config.burnerSignerWallet;
|
|
1990
2082
|
if (this.burnerSignerWallet?.account?.type === "local") {
|
|
1991
2083
|
console.warn("[PAFI] PTRedeemHandler: burnerSignerWallet uses a local (private key) account. Use a KMS-backed signer in production.");
|
|
@@ -1998,7 +2090,7 @@ var PTRedeemHandler = class {
|
|
|
1998
2090
|
}
|
|
1999
2091
|
}
|
|
2000
2092
|
async handle(request) {
|
|
2001
|
-
if (
|
|
2093
|
+
if (getAddress7(request.authenticatedAddress) !== getAddress7(request.userAddress)) {
|
|
2002
2094
|
throw new PTRedeemError(
|
|
2003
2095
|
"UNAUTHORIZED",
|
|
2004
2096
|
`userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
|
|
@@ -2007,11 +2099,12 @@ var PTRedeemHandler = class {
|
|
|
2007
2099
|
if (request.amount <= 0n) {
|
|
2008
2100
|
throw new PTRedeemError("INVALID_AMOUNT", "redeem amount must be positive");
|
|
2009
2101
|
}
|
|
2102
|
+
const pointTokenAddress = getAddress7(request.pointTokenAddress);
|
|
2010
2103
|
if (this.redemptionService) {
|
|
2011
2104
|
const decision = await this.redemptionService.evaluate(
|
|
2012
2105
|
request.userAddress,
|
|
2013
2106
|
request.amount,
|
|
2014
|
-
|
|
2107
|
+
pointTokenAddress
|
|
2015
2108
|
);
|
|
2016
2109
|
if (!decision.allowed) {
|
|
2017
2110
|
const denial = decision.denial;
|
|
@@ -2025,7 +2118,7 @@ var PTRedeemHandler = class {
|
|
|
2025
2118
|
let burnNonce;
|
|
2026
2119
|
try {
|
|
2027
2120
|
burnNonce = await this.provider.readContract({
|
|
2028
|
-
address:
|
|
2121
|
+
address: pointTokenAddress,
|
|
2029
2122
|
abi: POINT_TOKEN_ABI2,
|
|
2030
2123
|
functionName: "burnRequestNonces",
|
|
2031
2124
|
args: [request.userAddress]
|
|
@@ -2036,27 +2129,27 @@ var PTRedeemHandler = class {
|
|
|
2036
2129
|
`failed to read burnRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
|
|
2037
2130
|
);
|
|
2038
2131
|
}
|
|
2039
|
-
const
|
|
2040
|
-
let userNonces = this.inFlightNonces.get(
|
|
2132
|
+
const nonceKey = `${getAddress7(request.userAddress).toLowerCase()}:${pointTokenAddress.toLowerCase()}`;
|
|
2133
|
+
let userNonces = this.inFlightNonces.get(nonceKey);
|
|
2041
2134
|
if (!userNonces) {
|
|
2042
2135
|
userNonces = /* @__PURE__ */ new Set();
|
|
2043
|
-
this.inFlightNonces.set(
|
|
2136
|
+
this.inFlightNonces.set(nonceKey, userNonces);
|
|
2044
2137
|
}
|
|
2045
2138
|
if (userNonces.has(burnNonce)) {
|
|
2046
2139
|
throw new PTRedeemError(
|
|
2047
2140
|
"NONCE_IN_FLIGHT",
|
|
2048
|
-
`A burn request for nonce ${burnNonce} is already in progress for ${request.userAddress}. Retry after the current request completes.`
|
|
2141
|
+
`A burn request for nonce ${burnNonce} is already in progress for ${request.userAddress} on ${pointTokenAddress}. Retry after the current request completes.`
|
|
2049
2142
|
);
|
|
2050
2143
|
}
|
|
2051
2144
|
userNonces.add(burnNonce);
|
|
2052
2145
|
try {
|
|
2053
|
-
return await this._handleAfterNonceLock(request, burnNonce);
|
|
2146
|
+
return await this._handleAfterNonceLock(request, burnNonce, pointTokenAddress);
|
|
2054
2147
|
} finally {
|
|
2055
2148
|
userNonces.delete(burnNonce);
|
|
2056
|
-
if (userNonces.size === 0) this.inFlightNonces.delete(
|
|
2149
|
+
if (userNonces.size === 0) this.inFlightNonces.delete(nonceKey);
|
|
2057
2150
|
}
|
|
2058
2151
|
}
|
|
2059
|
-
async _handleAfterNonceLock(request, burnNonce) {
|
|
2152
|
+
async _handleAfterNonceLock(request, burnNonce, pointTokenAddress) {
|
|
2060
2153
|
const previewDeadline = BigInt(
|
|
2061
2154
|
Math.floor(this.now() / 1e3) + this.signatureDeadlineSeconds
|
|
2062
2155
|
);
|
|
@@ -2067,13 +2160,13 @@ var PTRedeemHandler = class {
|
|
|
2067
2160
|
const previewUserOp = this.relayService.previewBurnUserOp({
|
|
2068
2161
|
userAddress: request.userAddress,
|
|
2069
2162
|
aaNonce: burnNonce,
|
|
2070
|
-
pointTokenAddress
|
|
2163
|
+
pointTokenAddress,
|
|
2071
2164
|
amount: request.amount,
|
|
2072
2165
|
deadline: previewDeadline
|
|
2073
2166
|
});
|
|
2074
2167
|
fee = await this.feeService.estimateGasFee({
|
|
2075
2168
|
scenario: "burn",
|
|
2076
|
-
contractAddress:
|
|
2169
|
+
contractAddress: pointTokenAddress,
|
|
2077
2170
|
partialUserOp: {
|
|
2078
2171
|
sender: previewUserOp.sender,
|
|
2079
2172
|
nonce: previewUserOp.nonce,
|
|
@@ -2092,7 +2185,7 @@ var PTRedeemHandler = class {
|
|
|
2092
2185
|
}
|
|
2093
2186
|
const onChainBalance = await getPointTokenBalance2(
|
|
2094
2187
|
this.provider,
|
|
2095
|
-
|
|
2188
|
+
pointTokenAddress,
|
|
2096
2189
|
request.userAddress
|
|
2097
2190
|
);
|
|
2098
2191
|
if (onChainBalance < request.amount) {
|
|
@@ -2102,10 +2195,11 @@ var PTRedeemHandler = class {
|
|
|
2102
2195
|
);
|
|
2103
2196
|
}
|
|
2104
2197
|
const deadline = previewDeadline;
|
|
2198
|
+
const domainName = await this.domainResolver.resolve(pointTokenAddress);
|
|
2105
2199
|
const domain = {
|
|
2106
|
-
name:
|
|
2200
|
+
name: domainName,
|
|
2107
2201
|
chainId: this.chainId,
|
|
2108
|
-
verifyingContract:
|
|
2202
|
+
verifyingContract: pointTokenAddress
|
|
2109
2203
|
};
|
|
2110
2204
|
const sponsoredBurnAmount = request.amount - fee;
|
|
2111
2205
|
const sponsoredBurnRequest = {
|
|
@@ -2127,14 +2221,14 @@ var PTRedeemHandler = class {
|
|
|
2127
2221
|
request.userAddress,
|
|
2128
2222
|
sponsoredBurnAmount,
|
|
2129
2223
|
this.redeemLockDurationMs,
|
|
2130
|
-
|
|
2224
|
+
pointTokenAddress
|
|
2131
2225
|
);
|
|
2132
2226
|
try {
|
|
2133
2227
|
const sponsoredUserOp = await this.relayService.prepareBurn({
|
|
2134
2228
|
mode: "burnWithSig",
|
|
2135
2229
|
userAddress: request.userAddress,
|
|
2136
2230
|
aaNonce: request.aaNonce,
|
|
2137
|
-
pointTokenAddress
|
|
2231
|
+
pointTokenAddress,
|
|
2138
2232
|
batchExecutorAddress: this.batchExecutorAddress,
|
|
2139
2233
|
burnRequest: sponsoredBurnRequest,
|
|
2140
2234
|
burnerSignature: sponsoredSig,
|
|
@@ -2162,7 +2256,7 @@ var PTRedeemHandler = class {
|
|
|
2162
2256
|
request.userAddress,
|
|
2163
2257
|
request.amount,
|
|
2164
2258
|
this.redeemLockDurationMs,
|
|
2165
|
-
|
|
2259
|
+
pointTokenAddress
|
|
2166
2260
|
);
|
|
2167
2261
|
let fallbackUserOp;
|
|
2168
2262
|
try {
|
|
@@ -2170,7 +2264,7 @@ var PTRedeemHandler = class {
|
|
|
2170
2264
|
mode: "burnWithSig",
|
|
2171
2265
|
userAddress: request.userAddress,
|
|
2172
2266
|
aaNonce: request.aaNonce,
|
|
2173
|
-
pointTokenAddress
|
|
2267
|
+
pointTokenAddress,
|
|
2174
2268
|
batchExecutorAddress: this.batchExecutorAddress,
|
|
2175
2269
|
burnRequest: fallbackBurnRequest,
|
|
2176
2270
|
burnerSignature: fallbackSig,
|
|
@@ -2195,7 +2289,7 @@ var PTRedeemHandler = class {
|
|
|
2195
2289
|
await this.redemptionService.recordSuccessfulInitiate({
|
|
2196
2290
|
user: request.userAddress,
|
|
2197
2291
|
amountPt: request.amount,
|
|
2198
|
-
pointTokenAddress
|
|
2292
|
+
pointTokenAddress,
|
|
2199
2293
|
reservationId: sponsoredLockId
|
|
2200
2294
|
}).catch(() => {
|
|
2201
2295
|
});
|
|
@@ -2318,7 +2412,7 @@ async function handleRedeemStatus(params) {
|
|
|
2318
2412
|
}
|
|
2319
2413
|
|
|
2320
2414
|
// src/api/mobileHandlers.ts
|
|
2321
|
-
import { getAddress as
|
|
2415
|
+
import { getAddress as getAddress8 } from "viem";
|
|
2322
2416
|
import {
|
|
2323
2417
|
ENTRY_POINT_V08,
|
|
2324
2418
|
parseEip7702DelegatedAddress
|
|
@@ -2670,7 +2764,7 @@ async function handleMobileSubmit(params) {
|
|
|
2670
2764
|
if (!entry) {
|
|
2671
2765
|
throw new PendingUserOpNotFoundError(params.lockId);
|
|
2672
2766
|
}
|
|
2673
|
-
if (
|
|
2767
|
+
if (getAddress8(entry.sender) !== getAddress8(params.authenticatedAddress)) {
|
|
2674
2768
|
throw new PendingUserOpForbiddenError(params.lockId);
|
|
2675
2769
|
}
|
|
2676
2770
|
const variant = params.variant ?? "sponsored";
|
|
@@ -2686,7 +2780,7 @@ async function handleMobileSubmit(params) {
|
|
|
2686
2780
|
}
|
|
2687
2781
|
|
|
2688
2782
|
// src/api/handlers/ptClaimHandler.ts
|
|
2689
|
-
import { getAddress as
|
|
2783
|
+
import { getAddress as getAddress9 } from "viem";
|
|
2690
2784
|
import {
|
|
2691
2785
|
decodeBatchExecuteCalls,
|
|
2692
2786
|
getContractAddresses as getContractAddresses3
|
|
@@ -2735,7 +2829,7 @@ var PTClaimHandler = class {
|
|
|
2735
2829
|
};
|
|
2736
2830
|
}
|
|
2737
2831
|
async handle(request) {
|
|
2738
|
-
if (
|
|
2832
|
+
if (getAddress9(request.authenticatedAddress) !== getAddress9(request.userAddress)) {
|
|
2739
2833
|
throw new PTClaimError(
|
|
2740
2834
|
"VALIDATION_FAILED",
|
|
2741
2835
|
`userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
|
|
@@ -2790,8 +2884,11 @@ var PTClaimHandler = class {
|
|
|
2790
2884
|
callData: previewUserOp.callData
|
|
2791
2885
|
}
|
|
2792
2886
|
}) : 0n;
|
|
2887
|
+
const domainName = await this.cfg.domainResolver.resolve(
|
|
2888
|
+
request.pointTokenAddress
|
|
2889
|
+
);
|
|
2793
2890
|
const domain = {
|
|
2794
|
-
name:
|
|
2891
|
+
name: domainName,
|
|
2795
2892
|
chainId: request.chainId,
|
|
2796
2893
|
verifyingContract: request.pointTokenAddress
|
|
2797
2894
|
};
|
|
@@ -2807,8 +2904,16 @@ var PTClaimHandler = class {
|
|
|
2807
2904
|
domain,
|
|
2808
2905
|
mintRequestNonce: request.mintRequestNonce,
|
|
2809
2906
|
deadline: signatureDeadline,
|
|
2810
|
-
mintFeeWrapperAddress: resolvedWrapper
|
|
2811
|
-
//
|
|
2907
|
+
mintFeeWrapperAddress: resolvedWrapper,
|
|
2908
|
+
// Pass the bundler-estimated `feeAmount` explicitly so the
|
|
2909
|
+
// RelayService skips its legacy `quoteOperatorFeePt` path
|
|
2910
|
+
// (which uses the SDK's old 12_000 bps premium default).
|
|
2911
|
+
// Without this, the response's `feeAmount` (from FeeManager,
|
|
2912
|
+
// 100% premium on top of PAFI's 110% server-side estimate)
|
|
2913
|
+
// would diverge from the actual PT.transfer amount in the
|
|
2914
|
+
// UserOp batch (`quoteOperatorFeePt`'s 120%), and the user
|
|
2915
|
+
// would see one value while the wallet transferred another.
|
|
2916
|
+
feeAmount
|
|
2812
2917
|
});
|
|
2813
2918
|
} catch (err) {
|
|
2814
2919
|
throw new PTClaimError(
|
|
@@ -3006,7 +3111,7 @@ import {
|
|
|
3006
3111
|
getContractAddresses as getContractAddresses5,
|
|
3007
3112
|
serializeUserOpToJsonRpc as serializeUserOpToJsonRpc2
|
|
3008
3113
|
} from "@pafi-dev/core";
|
|
3009
|
-
import { getAddress as
|
|
3114
|
+
import { getAddress as getAddress10 } from "viem";
|
|
3010
3115
|
var DEFAULT_DELEGATE_GAS = {
|
|
3011
3116
|
callGasLimit: 100000n,
|
|
3012
3117
|
verificationGasLimit: 150000n,
|
|
@@ -3093,7 +3198,7 @@ async function handleDelegateSubmit(params) {
|
|
|
3093
3198
|
if (!entry) {
|
|
3094
3199
|
throw new PendingUserOpNotFoundError(params.lockId);
|
|
3095
3200
|
}
|
|
3096
|
-
if (
|
|
3201
|
+
if (getAddress10(entry.sender) !== getAddress10(params.authenticatedAddress)) {
|
|
3097
3202
|
throw new PendingUserOpForbiddenError(params.lockId);
|
|
3098
3203
|
}
|
|
3099
3204
|
if (!entry.eip7702Auth) {
|
|
@@ -3114,7 +3219,7 @@ async function handleDelegateSubmit(params) {
|
|
|
3114
3219
|
|
|
3115
3220
|
// src/api/issuerApiAdapter.ts
|
|
3116
3221
|
import { randomUUID } from "crypto";
|
|
3117
|
-
import { getAddress as
|
|
3222
|
+
import { getAddress as getAddress11 } from "viem";
|
|
3118
3223
|
import {
|
|
3119
3224
|
buildAndSignSponsorAuth,
|
|
3120
3225
|
decodeBatchExecuteCalls as decodeBatchExecuteCalls3,
|
|
@@ -3179,7 +3284,7 @@ var IssuerApiAdapter = class {
|
|
|
3179
3284
|
async pools(authenticatedAddress, chainId, pointTokenAddress) {
|
|
3180
3285
|
const result = await this.cfg.issuerService.api.handlePools(
|
|
3181
3286
|
authenticatedAddress,
|
|
3182
|
-
{ chainId, pointTokenAddress:
|
|
3287
|
+
{ chainId, pointTokenAddress: getAddress11(pointTokenAddress) }
|
|
3183
3288
|
);
|
|
3184
3289
|
return { pools: result.pools };
|
|
3185
3290
|
}
|
|
@@ -3188,8 +3293,8 @@ var IssuerApiAdapter = class {
|
|
|
3188
3293
|
authenticatedAddress,
|
|
3189
3294
|
{
|
|
3190
3295
|
chainId,
|
|
3191
|
-
userAddress:
|
|
3192
|
-
pointTokenAddress:
|
|
3296
|
+
userAddress: getAddress11(userAddress),
|
|
3297
|
+
pointTokenAddress: getAddress11(pointTokenAddress)
|
|
3193
3298
|
}
|
|
3194
3299
|
);
|
|
3195
3300
|
return {
|
|
@@ -3210,7 +3315,7 @@ var IssuerApiAdapter = class {
|
|
|
3210
3315
|
"ptClaimHandler",
|
|
3211
3316
|
"claim"
|
|
3212
3317
|
);
|
|
3213
|
-
const pointTokenAddress =
|
|
3318
|
+
const pointTokenAddress = getAddress11(input.pointTokenAddress);
|
|
3214
3319
|
const result = await ptClaimHandler.handle({
|
|
3215
3320
|
authenticatedAddress: input.authenticatedAddress,
|
|
3216
3321
|
userAddress: input.authenticatedAddress,
|
|
@@ -3237,9 +3342,11 @@ var IssuerApiAdapter = class {
|
|
|
3237
3342
|
}
|
|
3238
3343
|
async redeem(input) {
|
|
3239
3344
|
this.assertRedeemHandler();
|
|
3345
|
+
const pointTokenAddress = getAddress11(input.pointTokenAddress);
|
|
3240
3346
|
const response = await this.cfg.ptRedeemHandler.handle({
|
|
3241
3347
|
userAddress: input.authenticatedAddress,
|
|
3242
3348
|
authenticatedAddress: input.authenticatedAddress,
|
|
3349
|
+
pointTokenAddress,
|
|
3243
3350
|
amount: input.amount,
|
|
3244
3351
|
aaNonce: input.aaNonce,
|
|
3245
3352
|
chainId: input.chainId
|
|
@@ -3305,7 +3412,7 @@ var IssuerApiAdapter = class {
|
|
|
3305
3412
|
"ptClaimHandler",
|
|
3306
3413
|
"claimPrepare"
|
|
3307
3414
|
);
|
|
3308
|
-
const pointTokenAddress =
|
|
3415
|
+
const pointTokenAddress = getAddress11(input.pointTokenAddress);
|
|
3309
3416
|
const claimResult = await ptClaimHandler.handle({
|
|
3310
3417
|
authenticatedAddress: input.authenticatedAddress,
|
|
3311
3418
|
userAddress: input.authenticatedAddress,
|
|
@@ -3351,10 +3458,11 @@ var IssuerApiAdapter = class {
|
|
|
3351
3458
|
}
|
|
3352
3459
|
async redeemPrepare(input) {
|
|
3353
3460
|
this.assertRedeemHandler();
|
|
3354
|
-
const pointTokenAddress =
|
|
3461
|
+
const pointTokenAddress = getAddress11(input.pointTokenAddress);
|
|
3355
3462
|
const redeemResponse = await this.cfg.ptRedeemHandler.handle({
|
|
3356
3463
|
userAddress: input.authenticatedAddress,
|
|
3357
3464
|
authenticatedAddress: input.authenticatedAddress,
|
|
3465
|
+
pointTokenAddress,
|
|
3358
3466
|
amount: input.amount,
|
|
3359
3467
|
aaNonce: input.aaNonce,
|
|
3360
3468
|
chainId: input.chainId
|
|
@@ -4102,7 +4210,7 @@ var PafiBackendClient = class {
|
|
|
4102
4210
|
};
|
|
4103
4211
|
|
|
4104
4212
|
// src/config.ts
|
|
4105
|
-
import { getAddress as
|
|
4213
|
+
import { getAddress as getAddress12 } from "viem";
|
|
4106
4214
|
import { getContractAddresses as getContractAddresses7 } from "@pafi-dev/core";
|
|
4107
4215
|
|
|
4108
4216
|
// src/redemption/evaluator.ts
|
|
@@ -4412,7 +4520,7 @@ var RedemptionService = class {
|
|
|
4412
4520
|
};
|
|
4413
4521
|
|
|
4414
4522
|
// src/config.ts
|
|
4415
|
-
function createIssuerService(config) {
|
|
4523
|
+
async function createIssuerService(config) {
|
|
4416
4524
|
if (!config.provider) {
|
|
4417
4525
|
throw new Error("createIssuerService: provider is required");
|
|
4418
4526
|
}
|
|
@@ -4431,7 +4539,7 @@ function createIssuerService(config) {
|
|
|
4431
4539
|
"createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
|
|
4432
4540
|
);
|
|
4433
4541
|
}
|
|
4434
|
-
const tokenAddresses = rawAddresses.map((a) =>
|
|
4542
|
+
const tokenAddresses = rawAddresses.map((a) => getAddress12(a));
|
|
4435
4543
|
const ledger = config.ledger;
|
|
4436
4544
|
const sessionStore = config.sessionStore ?? new MemorySessionStore();
|
|
4437
4545
|
const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
|
|
@@ -4531,9 +4639,26 @@ function createIssuerService(config) {
|
|
|
4531
4639
|
handlersConfig.mintFeeWrapperAddress = resolvedWrapperAddress;
|
|
4532
4640
|
}
|
|
4533
4641
|
const handlers = new IssuerApiHandlers(handlersConfig);
|
|
4642
|
+
const indexerLeaderLocks = [];
|
|
4534
4643
|
if (config.indexer?.autoStart) {
|
|
4535
|
-
|
|
4536
|
-
|
|
4644
|
+
const lock = config.indexer.singletonLock;
|
|
4645
|
+
if (!lock) {
|
|
4646
|
+
console.warn(
|
|
4647
|
+
"[@pafi-dev/issuer] indexer.autoStart=true without singletonLock \u2014 this is UNSAFE in multi-replica deployments (audit finding H-04). Either set replicas=1 + INDEXER_AUTOSTART=false on non-leader pods, or pass `singletonLock: makePostgresSingletonLock(dataSource)`. This permissive path will be removed in a future major release."
|
|
4648
|
+
);
|
|
4649
|
+
for (const idx of indexers.values()) {
|
|
4650
|
+
idx.start();
|
|
4651
|
+
}
|
|
4652
|
+
} else {
|
|
4653
|
+
for (const [tokenAddr, idx] of indexers.entries()) {
|
|
4654
|
+
const key = `pafi-issuer:point-indexer:${tokenAddr.toLowerCase()}`;
|
|
4655
|
+
const handle = await lock.acquire(key);
|
|
4656
|
+
if (!handle) {
|
|
4657
|
+
continue;
|
|
4658
|
+
}
|
|
4659
|
+
idx.start();
|
|
4660
|
+
indexerLeaderLocks.push(handle);
|
|
4661
|
+
}
|
|
4537
4662
|
}
|
|
4538
4663
|
}
|
|
4539
4664
|
return {
|
|
@@ -4544,13 +4669,14 @@ function createIssuerService(config) {
|
|
|
4544
4669
|
relay: relayService,
|
|
4545
4670
|
fee: feeManager,
|
|
4546
4671
|
indexers,
|
|
4672
|
+
indexerLeaderLocks,
|
|
4547
4673
|
api: handlers,
|
|
4548
4674
|
redemption
|
|
4549
4675
|
};
|
|
4550
4676
|
}
|
|
4551
4677
|
|
|
4552
4678
|
// src/issuer-state/validator.ts
|
|
4553
|
-
import { getAddress as
|
|
4679
|
+
import { getAddress as getAddress13 } from "viem";
|
|
4554
4680
|
import {
|
|
4555
4681
|
POINT_TOKEN_ABI as POINT_TOKEN_ABI3,
|
|
4556
4682
|
issuerRegistryAbi,
|
|
@@ -4582,7 +4708,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
4582
4708
|
*/
|
|
4583
4709
|
invalidate(pointToken) {
|
|
4584
4710
|
if (pointToken) {
|
|
4585
|
-
const key =
|
|
4711
|
+
const key = getAddress13(pointToken);
|
|
4586
4712
|
this.pointTokenIssuerCache.delete(key);
|
|
4587
4713
|
this.stateCache.delete(key);
|
|
4588
4714
|
this.inflight.delete(key);
|
|
@@ -4597,7 +4723,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
4597
4723
|
* The issuer field is set at `initialize()` and never changes.
|
|
4598
4724
|
*/
|
|
4599
4725
|
async getIssuerAddressForPointToken(pointToken) {
|
|
4600
|
-
const key =
|
|
4726
|
+
const key = getAddress13(pointToken);
|
|
4601
4727
|
const cached = this.pointTokenIssuerCache.get(key);
|
|
4602
4728
|
if (cached) return cached;
|
|
4603
4729
|
const issuer = await this.provider.readContract({
|
|
@@ -4605,15 +4731,15 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
4605
4731
|
abi: POINT_TOKEN_ABI3,
|
|
4606
4732
|
functionName: "issuer"
|
|
4607
4733
|
});
|
|
4608
|
-
this.pointTokenIssuerCache.set(key,
|
|
4609
|
-
return
|
|
4734
|
+
this.pointTokenIssuerCache.set(key, getAddress13(issuer));
|
|
4735
|
+
return getAddress13(issuer);
|
|
4610
4736
|
}
|
|
4611
4737
|
/**
|
|
4612
4738
|
* Read registry record + totalSupply, with 30s cache and in-flight
|
|
4613
4739
|
* deduplication. Does NOT throw on inactive/missing — returns raw state.
|
|
4614
4740
|
*/
|
|
4615
4741
|
async getIssuerState(pointToken) {
|
|
4616
|
-
const tokenAddr =
|
|
4742
|
+
const tokenAddr = getAddress13(pointToken);
|
|
4617
4743
|
const now = Date.now();
|
|
4618
4744
|
const cached = this.stateCache.get(tokenAddr);
|
|
4619
4745
|
if (cached && cached.expiresAt > now) return cached.value;
|
|
@@ -4756,7 +4882,7 @@ var MemoryRedemptionHistoryStore = class {
|
|
|
4756
4882
|
};
|
|
4757
4883
|
|
|
4758
4884
|
// src/index.ts
|
|
4759
|
-
var PAFI_ISSUER_SDK_VERSION = true ? "0.
|
|
4885
|
+
var PAFI_ISSUER_SDK_VERSION = true ? "0.22.0" : "dev";
|
|
4760
4886
|
export {
|
|
4761
4887
|
AdapterMisconfiguredError,
|
|
4762
4888
|
AuthError,
|
|
@@ -4795,6 +4921,7 @@ export {
|
|
|
4795
4921
|
PerpDepositError,
|
|
4796
4922
|
PerpDepositHandler,
|
|
4797
4923
|
PointIndexer,
|
|
4924
|
+
PointTokenDomainResolver,
|
|
4798
4925
|
PolicyProvider,
|
|
4799
4926
|
REDEMPTION_HISTORY_WINDOW_SEC,
|
|
4800
4927
|
RedemptionService,
|
|
@@ -4821,6 +4948,7 @@ export {
|
|
|
4821
4948
|
handleMobilePrepare,
|
|
4822
4949
|
handleMobileSubmit,
|
|
4823
4950
|
handleRedeemStatus,
|
|
4951
|
+
makePostgresSingletonLock,
|
|
4824
4952
|
mergePaymasterFields,
|
|
4825
4953
|
payloadFromGenericError,
|
|
4826
4954
|
payloadFromHttpException,
|