@pafi-dev/issuer 0.39.2 → 0.40.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/auth-client/index.cjs +65 -79
- package/dist/auth-client/index.cjs.map +1 -1
- package/dist/auth-client/index.js +2 -2
- package/dist/{chunk-7VEYSL2C.js → chunk-2Z3M2KQG.js} +69 -80
- package/dist/{chunk-7VEYSL2C.js.map → chunk-2Z3M2KQG.js.map} +1 -1
- package/dist/chunk-7QVYU63E.js +7 -0
- package/dist/{chunk-QLNGNH4A.js → chunk-RNQQYJIB.js} +23 -7
- package/dist/{chunk-QLNGNH4A.js.map → chunk-RNQQYJIB.js.map} +1 -1
- package/dist/direct-auth/index.cjs +363 -195
- package/dist/direct-auth/index.cjs.map +1 -1
- package/dist/direct-auth/index.js +304 -132
- package/dist/direct-auth/index.js.map +1 -1
- package/dist/http/index.cjs +14 -1
- package/dist/http/index.cjs.map +1 -1
- package/dist/http/index.js +2 -2
- package/dist/index.cjs +1093 -1482
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -156
- package/dist/index.d.ts +33 -156
- package/dist/index.js +1069 -1533
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.cjs +114 -50
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.js +106 -61
- package/dist/nestjs/index.js.map +1 -1
- package/dist/wallet-auth/index.cjs +11 -5
- package/dist/wallet-auth/index.cjs.map +1 -1
- package/dist/wallet-auth/index.js +13 -6
- package/dist/wallet-auth/index.js.map +1 -1
- package/package.json +5 -3
- package/dist/chunk-BRKEJJFQ.js +0 -17
- /package/dist/{chunk-BRKEJJFQ.js.map → chunk-7QVYU63E.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,7 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
7
|
var __export = (target, all) => {
|
|
7
8
|
for (var name in all)
|
|
8
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -44,7 +45,7 @@ __export(index_exports, {
|
|
|
44
45
|
NonceManager: () => NonceManager,
|
|
45
46
|
NoopRateLimiter: () => NoopRateLimiter,
|
|
46
47
|
PAFI_ISSUER_SDK_VERSION: () => PAFI_ISSUER_SDK_VERSION,
|
|
47
|
-
PAFI_SUBGRAPH_URL: () =>
|
|
48
|
+
PAFI_SUBGRAPH_URL: () => import_core14.PAFI_SUBGRAPH_URL,
|
|
48
49
|
PTClaimError: () => PTClaimError,
|
|
49
50
|
PTClaimHandler: () => PTClaimHandler,
|
|
50
51
|
PTRedeemError: () => PTRedeemError,
|
|
@@ -82,7 +83,6 @@ __export(index_exports, {
|
|
|
82
83
|
defaultPolicyFor: () => defaultPolicyFor,
|
|
83
84
|
evaluateRedemption: () => evaluateRedemption,
|
|
84
85
|
handleClaimStatus: () => handleClaimStatus,
|
|
85
|
-
handleDelegateSubmit: () => handleDelegateSubmit,
|
|
86
86
|
handleMobilePrepare: () => handleMobilePrepare,
|
|
87
87
|
handleMobileSubmit: () => handleMobileSubmit,
|
|
88
88
|
handleRedeemStatus: () => handleRedeemStatus,
|
|
@@ -94,8 +94,7 @@ __export(index_exports, {
|
|
|
94
94
|
prepareMobileUserOp: () => prepareMobileUserOp,
|
|
95
95
|
relayUserOp: () => relayUserOp,
|
|
96
96
|
requestPaymaster: () => requestPaymaster,
|
|
97
|
-
serializeEntryToJsonRpc: () => serializeEntryToJsonRpc
|
|
98
|
-
serializeUserOpTypedData: () => serializeUserOpTypedData
|
|
97
|
+
serializeEntryToJsonRpc: () => serializeEntryToJsonRpc
|
|
99
98
|
});
|
|
100
99
|
module.exports = __toCommonJS(index_exports);
|
|
101
100
|
|
|
@@ -104,6 +103,9 @@ var import_core = require("@pafi-dev/core");
|
|
|
104
103
|
var import_core2 = require("@pafi-dev/core");
|
|
105
104
|
var import_core3 = require("@pafi-dev/core");
|
|
106
105
|
var ConfigurationError = class extends import_core2.PafiSdkError {
|
|
106
|
+
static {
|
|
107
|
+
__name(this, "ConfigurationError");
|
|
108
|
+
}
|
|
107
109
|
httpStatus = "service_unavailable";
|
|
108
110
|
code;
|
|
109
111
|
details;
|
|
@@ -128,6 +130,7 @@ function buildSdkErrorBody(err) {
|
|
|
128
130
|
if (err.details !== void 0) body.details = err.details;
|
|
129
131
|
return body;
|
|
130
132
|
}
|
|
133
|
+
__name(buildSdkErrorBody, "buildSdkErrorBody");
|
|
131
134
|
function createSdkErrorMapper(factories) {
|
|
132
135
|
return (err) => {
|
|
133
136
|
if (!(err instanceof import_core.PafiSdkError)) {
|
|
@@ -146,17 +149,20 @@ function createSdkErrorMapper(factories) {
|
|
|
146
149
|
}
|
|
147
150
|
};
|
|
148
151
|
}
|
|
152
|
+
__name(createSdkErrorMapper, "createSdkErrorMapper");
|
|
149
153
|
|
|
150
154
|
// src/http/errorEnvelope.ts
|
|
151
155
|
function isValidationPipeBody(body) {
|
|
152
156
|
return Array.isArray(body["message"]) && typeof body["error"] === "string" && body["error"] === "Bad Request";
|
|
153
157
|
}
|
|
158
|
+
__name(isValidationPipeBody, "isValidationPipeBody");
|
|
154
159
|
function extractParamFromValidatorMessage(message) {
|
|
155
160
|
const idx = message.indexOf(" ");
|
|
156
161
|
if (idx <= 0) return void 0;
|
|
157
162
|
const candidate = message.slice(0, idx);
|
|
158
163
|
return /^[A-Za-z_][\w.]*$/.test(candidate) ? candidate : void 0;
|
|
159
164
|
}
|
|
165
|
+
__name(extractParamFromValidatorMessage, "extractParamFromValidatorMessage");
|
|
160
166
|
function normalizeValidationPipeBody(body) {
|
|
161
167
|
const fields = {};
|
|
162
168
|
for (const msg of body.message) {
|
|
@@ -171,11 +177,14 @@ function normalizeValidationPipeBody(body) {
|
|
|
171
177
|
code: "VALIDATION_FAILED",
|
|
172
178
|
message: body.message.join("; "),
|
|
173
179
|
safeToRetry: false,
|
|
174
|
-
metadata: {
|
|
180
|
+
metadata: {
|
|
181
|
+
fieldErrors: fields
|
|
182
|
+
}
|
|
175
183
|
};
|
|
176
184
|
if (param) payload.param = param;
|
|
177
185
|
return payload;
|
|
178
186
|
}
|
|
187
|
+
__name(normalizeValidationPipeBody, "normalizeValidationPipeBody");
|
|
179
188
|
function normalizeHttpExceptionBody(desc) {
|
|
180
189
|
const { statusCode, responseBody, exceptionName, fallbackMessage } = desc;
|
|
181
190
|
const defaultType = (0, import_core.defaultErrorTypeForStatus)(statusCode);
|
|
@@ -216,6 +225,7 @@ function normalizeHttpExceptionBody(desc) {
|
|
|
216
225
|
safeToRetry: false
|
|
217
226
|
};
|
|
218
227
|
}
|
|
228
|
+
__name(normalizeHttpExceptionBody, "normalizeHttpExceptionBody");
|
|
219
229
|
function payloadFromPafiSdkError(err) {
|
|
220
230
|
const body = buildSdkErrorBody(err);
|
|
221
231
|
const payload = {
|
|
@@ -229,10 +239,12 @@ function payloadFromPafiSdkError(err) {
|
|
|
229
239
|
if (body.details !== void 0) payload.details = body.details;
|
|
230
240
|
return payload;
|
|
231
241
|
}
|
|
242
|
+
__name(payloadFromPafiSdkError, "payloadFromPafiSdkError");
|
|
232
243
|
function sanitizeDbErrorMessage(message) {
|
|
233
244
|
if (/^[A-Z_]+: /.test(message) && message.length < 256) return message;
|
|
234
245
|
return "Internal database error";
|
|
235
246
|
}
|
|
247
|
+
__name(sanitizeDbErrorMessage, "sanitizeDbErrorMessage");
|
|
236
248
|
function buildErrorEnvelope(input) {
|
|
237
249
|
const now = (input.ctx.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
238
250
|
return {
|
|
@@ -246,9 +258,11 @@ function buildErrorEnvelope(input) {
|
|
|
246
258
|
}
|
|
247
259
|
};
|
|
248
260
|
}
|
|
261
|
+
__name(buildErrorEnvelope, "buildErrorEnvelope");
|
|
249
262
|
function payloadFromHttpException(desc) {
|
|
250
263
|
return normalizeHttpExceptionBody(desc);
|
|
251
264
|
}
|
|
265
|
+
__name(payloadFromHttpException, "payloadFromHttpException");
|
|
252
266
|
function payloadFromGenericError(err) {
|
|
253
267
|
const name = err.name || "INTERNAL_SERVER_ERROR";
|
|
254
268
|
const isDbError = name === "QueryFailedError" || name === "EntityNotFoundError";
|
|
@@ -259,9 +273,13 @@ function payloadFromGenericError(err) {
|
|
|
259
273
|
safeToRetry: false
|
|
260
274
|
};
|
|
261
275
|
}
|
|
276
|
+
__name(payloadFromGenericError, "payloadFromGenericError");
|
|
262
277
|
|
|
263
278
|
// src/policy/defaultPolicy.ts
|
|
264
279
|
var DefaultPolicyEngine = class {
|
|
280
|
+
static {
|
|
281
|
+
__name(this, "DefaultPolicyEngine");
|
|
282
|
+
}
|
|
265
283
|
ledger;
|
|
266
284
|
provider;
|
|
267
285
|
mintingOracleAddress;
|
|
@@ -277,20 +295,18 @@ var DefaultPolicyEngine = class {
|
|
|
277
295
|
if (opts.resolveIssuer) this.resolveIssuer = opts.resolveIssuer;
|
|
278
296
|
if (!opts.mintingOracleAddress || !opts.provider || !opts.verifyMintCap || !opts.resolveIssuer) {
|
|
279
297
|
if (process.env.NODE_ENV === "production") {
|
|
280
|
-
throw new Error(
|
|
281
|
-
"[PAFI] DefaultPolicyEngine: on-chain MintingOracle cap check is required in production. Configure mintingOracleAddress, provider, verifyMintCap, and resolveIssuer."
|
|
282
|
-
);
|
|
298
|
+
throw new Error("[PAFI] DefaultPolicyEngine: on-chain MintingOracle cap check is required in production. Configure mintingOracleAddress, provider, verifyMintCap, and resolveIssuer.");
|
|
283
299
|
}
|
|
284
300
|
}
|
|
285
301
|
}
|
|
286
302
|
async evaluate(request) {
|
|
287
303
|
if (request.amount <= 0n) {
|
|
288
|
-
return {
|
|
304
|
+
return {
|
|
305
|
+
approved: false,
|
|
306
|
+
reason: "Amount must be positive"
|
|
307
|
+
};
|
|
289
308
|
}
|
|
290
|
-
const available = await this.ledger.getBalance(
|
|
291
|
-
request.userAddress,
|
|
292
|
-
request.pointTokenAddress
|
|
293
|
-
);
|
|
309
|
+
const available = await this.ledger.getBalance(request.userAddress, request.pointTokenAddress);
|
|
294
310
|
if (available < request.amount) {
|
|
295
311
|
return {
|
|
296
312
|
approved: false,
|
|
@@ -300,12 +316,7 @@ var DefaultPolicyEngine = class {
|
|
|
300
316
|
if (this.mintingOracleAddress && this.provider && this.verifyMintCap && this.resolveIssuer) {
|
|
301
317
|
try {
|
|
302
318
|
const issuer = await this.resolveIssuer(request.pointTokenAddress);
|
|
303
|
-
await this.verifyMintCap(
|
|
304
|
-
this.provider,
|
|
305
|
-
this.mintingOracleAddress,
|
|
306
|
-
issuer,
|
|
307
|
-
request.amount
|
|
308
|
-
);
|
|
319
|
+
await this.verifyMintCap(this.provider, this.mintingOracleAddress, issuer, request.amount);
|
|
309
320
|
} catch (err) {
|
|
310
321
|
const msg = err instanceof Error ? err.message : String(err);
|
|
311
322
|
return {
|
|
@@ -314,7 +325,9 @@ var DefaultPolicyEngine = class {
|
|
|
314
325
|
};
|
|
315
326
|
}
|
|
316
327
|
}
|
|
317
|
-
return {
|
|
328
|
+
return {
|
|
329
|
+
approved: true
|
|
330
|
+
};
|
|
318
331
|
}
|
|
319
332
|
};
|
|
320
333
|
|
|
@@ -323,17 +336,16 @@ var import_node_crypto = require("crypto");
|
|
|
323
336
|
var import_viem = require("viem");
|
|
324
337
|
var DEFAULT_NONCE_TTL_MS = 5 * 60 * 1e3;
|
|
325
338
|
var MemorySessionStore = class {
|
|
339
|
+
static {
|
|
340
|
+
__name(this, "MemorySessionStore");
|
|
341
|
+
}
|
|
326
342
|
nonces = /* @__PURE__ */ new Map();
|
|
327
|
-
// nonce → expiresAt
|
|
328
343
|
sessions = /* @__PURE__ */ new Map();
|
|
329
|
-
// tokenId → session
|
|
330
344
|
nonceTtlMs;
|
|
331
345
|
now;
|
|
332
346
|
constructor(opts = {}) {
|
|
333
347
|
if (process.env.NODE_ENV === "production" && !opts.dangerouslyAllowMemoryStoreInProduction) {
|
|
334
|
-
throw new Error(
|
|
335
|
-
"[PAFI] MemorySessionStore refuses to start in production (NODE_ENV=production). Multi-pod K8s deploys do not share Map state, so sessions are not revocable across replicas \u2014 `logout` on pod A leaves the token valid on pod B until expiry. Use a Redis-backed session store (see RedisSessionStoreService in gg56-backend or implement your own ISessionStore). To bypass for a single-pod deploy, pass `dangerouslyAllowMemoryStoreInProduction: true`."
|
|
336
|
-
);
|
|
348
|
+
throw new Error("[PAFI] MemorySessionStore refuses to start in production (NODE_ENV=production). Multi-pod K8s deploys do not share Map state, so sessions are not revocable across replicas \u2014 `logout` on pod A leaves the token valid on pod B until expiry. Use a Redis-backed session store (see RedisSessionStoreService in gg56-backend or implement your own ISessionStore). To bypass for a single-pod deploy, pass `dangerouslyAllowMemoryStoreInProduction: true`.");
|
|
337
349
|
}
|
|
338
350
|
this.nonceTtlMs = opts.nonceTtlMs ?? DEFAULT_NONCE_TTL_MS;
|
|
339
351
|
this.now = opts.now ?? (() => Date.now());
|
|
@@ -373,7 +385,9 @@ var MemorySessionStore = class {
|
|
|
373
385
|
this.sessions.delete(tokenId);
|
|
374
386
|
return null;
|
|
375
387
|
}
|
|
376
|
-
return {
|
|
388
|
+
return {
|
|
389
|
+
...session
|
|
390
|
+
};
|
|
377
391
|
}
|
|
378
392
|
async revokeSession(tokenId) {
|
|
379
393
|
this.sessions.delete(tokenId);
|
|
@@ -405,18 +419,21 @@ var MemorySessionStore = class {
|
|
|
405
419
|
|
|
406
420
|
// src/auth/nonceManager.ts
|
|
407
421
|
var NonceManager = class {
|
|
422
|
+
static {
|
|
423
|
+
__name(this, "NonceManager");
|
|
424
|
+
}
|
|
425
|
+
store;
|
|
408
426
|
constructor(store) {
|
|
409
427
|
this.store = store;
|
|
410
428
|
}
|
|
411
|
-
store;
|
|
412
429
|
/** Generate a fresh login nonce. The store is responsible for TTL. */
|
|
413
430
|
async generate() {
|
|
414
431
|
return this.store.createNonce();
|
|
415
432
|
}
|
|
416
433
|
/**
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
434
|
+
* Atomically validate + consume a nonce. Returns `true` iff the nonce
|
|
435
|
+
* was known and unexpired (and is now removed from the store).
|
|
436
|
+
*/
|
|
420
437
|
async consume(nonce) {
|
|
421
438
|
return this.store.consumeNonce(nonce);
|
|
422
439
|
}
|
|
@@ -448,7 +465,11 @@ function statusForCode(code) {
|
|
|
448
465
|
return "not_found";
|
|
449
466
|
}
|
|
450
467
|
}
|
|
468
|
+
__name(statusForCode, "statusForCode");
|
|
451
469
|
var AuthError = class extends import_core.PafiSdkError {
|
|
470
|
+
static {
|
|
471
|
+
__name(this, "AuthError");
|
|
472
|
+
}
|
|
452
473
|
code;
|
|
453
474
|
httpStatus;
|
|
454
475
|
constructor(code, message) {
|
|
@@ -461,37 +482,28 @@ var AuthError = class extends import_core.PafiSdkError {
|
|
|
461
482
|
// src/auth/loginVerifier.ts
|
|
462
483
|
function assertJwtSecretStrength(secret) {
|
|
463
484
|
if (!secret) {
|
|
464
|
-
throw new Error(
|
|
465
|
-
"AuthService: jwtSecret is required. Generate via `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`"
|
|
466
|
-
);
|
|
485
|
+
throw new Error("AuthService: jwtSecret is required. Generate via `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"`");
|
|
467
486
|
}
|
|
468
487
|
if (secret.length < 32) {
|
|
469
|
-
throw new Error(
|
|
470
|
-
`AuthService: jwtSecret too short (${secret.length} chars; need \u2265 32). HS256 brute-force becomes feasible below this threshold.`
|
|
471
|
-
);
|
|
488
|
+
throw new Error(`AuthService: jwtSecret too short (${secret.length} chars; need \u2265 32). HS256 brute-force becomes feasible below this threshold.`);
|
|
472
489
|
}
|
|
473
490
|
const uniqueChars = new Set(secret).size;
|
|
474
491
|
if (uniqueChars < 16) {
|
|
475
|
-
throw new Error(
|
|
476
|
-
`AuthService: jwtSecret has only ${uniqueChars} unique characters; need \u2265 16. A trivially weak secret (e.g. "aaaaa...") will pass length but offer almost no entropy. Use \`crypto.randomBytes(32).toString('hex')\`.`
|
|
477
|
-
);
|
|
492
|
+
throw new Error(`AuthService: jwtSecret has only ${uniqueChars} unique characters; need \u2265 16. A trivially weak secret (e.g. "aaaaa...") will pass length but offer almost no entropy. Use \`crypto.randomBytes(32).toString('hex')\`.`);
|
|
478
493
|
}
|
|
479
494
|
const entropy = shannonEntropyBitsPerChar(secret);
|
|
480
495
|
if (entropy < 3.5) {
|
|
481
|
-
throw new Error(
|
|
482
|
-
`AuthService: jwtSecret entropy too low (${entropy.toFixed(2)} bits/char; need \u2265 3.5). Use \`crypto.randomBytes(32).toString('hex')\` (\u2248 4.0 bits/char).`
|
|
483
|
-
);
|
|
496
|
+
throw new Error(`AuthService: jwtSecret entropy too low (${entropy.toFixed(2)} bits/char; need \u2265 3.5). Use \`crypto.randomBytes(32).toString('hex')\` (\u2248 4.0 bits/char).`);
|
|
484
497
|
}
|
|
485
498
|
for (let period = 1; period <= secret.length / 4; period++) {
|
|
486
499
|
if (secret.length % period !== 0) continue;
|
|
487
500
|
const head = secret.slice(0, period);
|
|
488
501
|
if (secret === head.repeat(secret.length / period)) {
|
|
489
|
-
throw new Error(
|
|
490
|
-
`AuthService: jwtSecret is a repeating pattern of period ${period}. Use \`crypto.randomBytes(32).toString('hex')\`.`
|
|
491
|
-
);
|
|
502
|
+
throw new Error(`AuthService: jwtSecret is a repeating pattern of period ${period}. Use \`crypto.randomBytes(32).toString('hex')\`.`);
|
|
492
503
|
}
|
|
493
504
|
}
|
|
494
505
|
}
|
|
506
|
+
__name(assertJwtSecretStrength, "assertJwtSecretStrength");
|
|
495
507
|
function shannonEntropyBitsPerChar(s) {
|
|
496
508
|
const counts = /* @__PURE__ */ new Map();
|
|
497
509
|
for (const c of s) counts.set(c, (counts.get(c) ?? 0) + 1);
|
|
@@ -503,6 +515,7 @@ function shannonEntropyBitsPerChar(s) {
|
|
|
503
515
|
}
|
|
504
516
|
return h;
|
|
505
517
|
}
|
|
518
|
+
__name(shannonEntropyBitsPerChar, "shannonEntropyBitsPerChar");
|
|
506
519
|
function decodeExpiredJwtJti(token) {
|
|
507
520
|
try {
|
|
508
521
|
const parts = token.split(".");
|
|
@@ -512,13 +525,19 @@ function decodeExpiredJwtJti(token) {
|
|
|
512
525
|
const padded = payloadB64.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((payloadB64.length + 3) % 4);
|
|
513
526
|
const json = Buffer.from(padded, "base64").toString("utf-8");
|
|
514
527
|
const claims = JSON.parse(json);
|
|
515
|
-
return typeof claims.jti === "string" ? {
|
|
528
|
+
return typeof claims.jti === "string" ? {
|
|
529
|
+
jti: claims.jti
|
|
530
|
+
} : {};
|
|
516
531
|
} catch {
|
|
517
532
|
return {};
|
|
518
533
|
}
|
|
519
534
|
}
|
|
535
|
+
__name(decodeExpiredJwtJti, "decodeExpiredJwtJti");
|
|
520
536
|
var DEFAULT_EXPIRES_IN = "24h";
|
|
521
537
|
var AuthService = class {
|
|
538
|
+
static {
|
|
539
|
+
__name(this, "AuthService");
|
|
540
|
+
}
|
|
522
541
|
sessionStore;
|
|
523
542
|
jwtSecret;
|
|
524
543
|
jwtExpiresIn;
|
|
@@ -548,9 +567,9 @@ var AuthService = class {
|
|
|
548
567
|
return this.nonceManager.generate();
|
|
549
568
|
}
|
|
550
569
|
/**
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
570
|
+
* Verify a signed login message and issue a JWT on success.
|
|
571
|
+
* Throws an `AuthError` on any validation failure.
|
|
572
|
+
*/
|
|
554
573
|
async login(message, signature) {
|
|
555
574
|
let parsed;
|
|
556
575
|
try {
|
|
@@ -560,46 +579,28 @@ var AuthService = class {
|
|
|
560
579
|
throw new AuthError("INVALID_MESSAGE", `Could not parse login message: ${msg}`);
|
|
561
580
|
}
|
|
562
581
|
if (parsed.expirationTime == null) {
|
|
563
|
-
throw new AuthError(
|
|
564
|
-
"INVALID_MESSAGE",
|
|
565
|
-
"login message must include expirationTime"
|
|
566
|
-
);
|
|
582
|
+
throw new AuthError("INVALID_MESSAGE", "login message must include expirationTime");
|
|
567
583
|
}
|
|
568
584
|
if (parsed.domain !== this.domain) {
|
|
569
|
-
throw new AuthError(
|
|
570
|
-
"DOMAIN_MISMATCH",
|
|
571
|
-
`Expected domain "${this.domain}", got "${parsed.domain}"`
|
|
572
|
-
);
|
|
585
|
+
throw new AuthError("DOMAIN_MISMATCH", `Expected domain "${this.domain}", got "${parsed.domain}"`);
|
|
573
586
|
}
|
|
574
587
|
if (parsed.chainId !== this.chainId) {
|
|
575
|
-
throw new AuthError(
|
|
576
|
-
"CHAIN_MISMATCH",
|
|
577
|
-
`Expected chainId ${this.chainId}, got ${parsed.chainId}`
|
|
578
|
-
);
|
|
588
|
+
throw new AuthError("CHAIN_MISMATCH", `Expected chainId ${this.chainId}, got ${parsed.chainId}`);
|
|
579
589
|
}
|
|
580
590
|
const now = this.now();
|
|
581
591
|
if (parsed.notBefore && parsed.notBefore.getTime() > now.getTime()) {
|
|
582
|
-
throw new AuthError(
|
|
583
|
-
"MESSAGE_NOT_YET_VALID",
|
|
584
|
-
"Login message is not yet valid"
|
|
585
|
-
);
|
|
592
|
+
throw new AuthError("MESSAGE_NOT_YET_VALID", "Login message is not yet valid");
|
|
586
593
|
}
|
|
587
594
|
if (parsed.expirationTime && parsed.expirationTime.getTime() <= now.getTime()) {
|
|
588
595
|
throw new AuthError("MESSAGE_EXPIRED", "Login message has expired");
|
|
589
596
|
}
|
|
590
597
|
const verifyResult = await (0, import_core4.verifyLoginMessage)(message, signature);
|
|
591
598
|
if (!verifyResult.valid) {
|
|
592
|
-
throw new AuthError(
|
|
593
|
-
"SIGNATURE_INVALID",
|
|
594
|
-
"Signature does not match the address in the login message"
|
|
595
|
-
);
|
|
599
|
+
throw new AuthError("SIGNATURE_INVALID", "Signature does not match the address in the login message");
|
|
596
600
|
}
|
|
597
601
|
const nonceOk = await this.nonceManager.consume(parsed.nonce);
|
|
598
602
|
if (!nonceOk) {
|
|
599
|
-
throw new AuthError(
|
|
600
|
-
"NONCE_INVALID",
|
|
601
|
-
"Nonce is unknown, expired, or already used"
|
|
602
|
-
);
|
|
603
|
+
throw new AuthError("NONCE_INVALID", "Nonce is unknown, expired, or already used");
|
|
603
604
|
}
|
|
604
605
|
const userAddress = (0, import_viem2.getAddress)(verifyResult.address);
|
|
605
606
|
const tokenId = (0, import_node_crypto2.randomBytes)(16).toString("hex");
|
|
@@ -616,11 +617,18 @@ var AuthService = class {
|
|
|
616
617
|
let signer = new import_jose.SignJWT({
|
|
617
618
|
userAddress,
|
|
618
619
|
chainId: this.chainId
|
|
619
|
-
}).setProtectedHeader({
|
|
620
|
+
}).setProtectedHeader({
|
|
621
|
+
alg: "HS256"
|
|
622
|
+
}).setJti(tokenId).setIssuedAt(Math.floor(issuedAt.getTime() / 1e3)).setExpirationTime(Math.floor(expiresAt.getTime() / 1e3));
|
|
620
623
|
if (this.issuer) signer = signer.setIssuer(this.issuer);
|
|
621
624
|
if (this.audience) signer = signer.setAudience(this.audience);
|
|
622
625
|
const token = await signer.sign(this.jwtSecret);
|
|
623
|
-
return {
|
|
626
|
+
return {
|
|
627
|
+
token,
|
|
628
|
+
userAddress,
|
|
629
|
+
tokenId,
|
|
630
|
+
expiresAt
|
|
631
|
+
};
|
|
624
632
|
}
|
|
625
633
|
/** Revoke the session backing the given JWT (logout). */
|
|
626
634
|
async logout(token) {
|
|
@@ -628,9 +636,12 @@ var AuthService = class {
|
|
|
628
636
|
try {
|
|
629
637
|
const result = await (0, import_jose.jwtVerify)(token, this.jwtSecret, {
|
|
630
638
|
clockTolerance: 60,
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
639
|
+
...this.issuer ? {
|
|
640
|
+
issuer: this.issuer
|
|
641
|
+
} : {},
|
|
642
|
+
...this.audience ? {
|
|
643
|
+
audience: this.audience
|
|
644
|
+
} : {}
|
|
634
645
|
});
|
|
635
646
|
payload = result.payload;
|
|
636
647
|
} catch (err) {
|
|
@@ -648,10 +659,7 @@ var AuthService = class {
|
|
|
648
659
|
if (err instanceof import_jose.errors.JWSSignatureVerificationFailed || err instanceof import_jose.errors.JWSInvalid || err instanceof import_jose.errors.JWTInvalid) {
|
|
649
660
|
throw new AuthError("TOKEN_INVALID", "JWT verification failed");
|
|
650
661
|
}
|
|
651
|
-
throw new AuthError(
|
|
652
|
-
"TOKEN_INVALID",
|
|
653
|
-
`JWT verification failed: ${err instanceof Error ? err.message : String(err)}`
|
|
654
|
-
);
|
|
662
|
+
throw new AuthError("TOKEN_INVALID", `JWT verification failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
655
663
|
}
|
|
656
664
|
if (payload.jti) {
|
|
657
665
|
try {
|
|
@@ -667,16 +675,20 @@ var AuthService = class {
|
|
|
667
675
|
console.error("[PAFI] AuthService logout: session store error", err);
|
|
668
676
|
}
|
|
669
677
|
/**
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
678
|
+
* Verify a JWT and return the authenticated user context. Throws an
|
|
679
|
+
* `AuthError` if the token is missing, malformed, expired, revoked, or
|
|
680
|
+
* signed by a different key.
|
|
681
|
+
*/
|
|
674
682
|
async verifyToken(token) {
|
|
675
683
|
let payload;
|
|
676
684
|
try {
|
|
677
685
|
const result = await (0, import_jose.jwtVerify)(token, this.jwtSecret, {
|
|
678
|
-
...this.issuer ? {
|
|
679
|
-
|
|
686
|
+
...this.issuer ? {
|
|
687
|
+
issuer: this.issuer
|
|
688
|
+
} : {},
|
|
689
|
+
...this.audience ? {
|
|
690
|
+
audience: this.audience
|
|
691
|
+
} : {}
|
|
680
692
|
});
|
|
681
693
|
payload = result.payload;
|
|
682
694
|
} catch (err) {
|
|
@@ -694,10 +706,7 @@ var AuthService = class {
|
|
|
694
706
|
}
|
|
695
707
|
const session = await this.sessionStore.getSession(tokenId);
|
|
696
708
|
if (!session) {
|
|
697
|
-
throw new AuthError(
|
|
698
|
-
"SESSION_REVOKED",
|
|
699
|
-
"Session is no longer active (revoked or expired)"
|
|
700
|
-
);
|
|
709
|
+
throw new AuthError("SESSION_REVOKED", "Session is no longer active (revoked or expired)");
|
|
701
710
|
}
|
|
702
711
|
const userAddress = payload.userAddress;
|
|
703
712
|
const chainId = payload.chainId;
|
|
@@ -714,51 +723,50 @@ var AuthService = class {
|
|
|
714
723
|
function parseExpiry(from, expiresIn) {
|
|
715
724
|
const match = expiresIn.match(/^(\d+)\s*(s|m|h|d)$/i);
|
|
716
725
|
if (!match) {
|
|
717
|
-
throw new Error(
|
|
718
|
-
`AuthService: unsupported jwtExpiresIn "${expiresIn}" \u2014 use e.g. "24h"`
|
|
719
|
-
);
|
|
726
|
+
throw new Error(`AuthService: unsupported jwtExpiresIn "${expiresIn}" \u2014 use e.g. "24h"`);
|
|
720
727
|
}
|
|
721
728
|
const n = Number(match[1]);
|
|
722
729
|
const unit = match[2].toLowerCase();
|
|
723
730
|
const multiplier = unit === "s" ? 1e3 : unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5;
|
|
724
731
|
return new Date(from.getTime() + n * multiplier);
|
|
725
732
|
}
|
|
733
|
+
__name(parseExpiry, "parseExpiry");
|
|
726
734
|
|
|
727
735
|
// src/auth/jwtMiddleware.ts
|
|
728
736
|
async function authenticateRequest(authHeader, authService) {
|
|
729
737
|
if (!authHeader) {
|
|
730
|
-
throw new AuthError(
|
|
731
|
-
"MISSING_TOKEN",
|
|
732
|
-
"Authorization header is required"
|
|
733
|
-
);
|
|
738
|
+
throw new AuthError("MISSING_TOKEN", "Authorization header is required");
|
|
734
739
|
}
|
|
735
740
|
const match = authHeader.match(/^Bearer\s+(\S+)\s*$/i);
|
|
736
741
|
if (!match) {
|
|
737
|
-
throw new AuthError(
|
|
738
|
-
"MALFORMED_TOKEN",
|
|
739
|
-
"Authorization header must be in the form 'Bearer <token>'"
|
|
740
|
-
);
|
|
742
|
+
throw new AuthError("MALFORMED_TOKEN", "Authorization header must be in the form 'Bearer <token>'");
|
|
741
743
|
}
|
|
742
744
|
const token = match[1];
|
|
743
745
|
return authService.verifyToken(token);
|
|
744
746
|
}
|
|
747
|
+
__name(authenticateRequest, "authenticateRequest");
|
|
745
748
|
|
|
746
749
|
// src/auth/rateLimiter.ts
|
|
747
750
|
var DEFAULT_LIMITS = {
|
|
748
|
-
auth_nonce: {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
751
|
+
auth_nonce: {
|
|
752
|
+
max: 30,
|
|
753
|
+
windowMs: 6e4
|
|
754
|
+
},
|
|
755
|
+
auth_login: {
|
|
756
|
+
max: 5,
|
|
757
|
+
windowMs: 6e4
|
|
758
|
+
}
|
|
752
759
|
};
|
|
753
760
|
var MemoryRateLimiter = class {
|
|
761
|
+
static {
|
|
762
|
+
__name(this, "MemoryRateLimiter");
|
|
763
|
+
}
|
|
754
764
|
buckets = /* @__PURE__ */ new Map();
|
|
755
765
|
limits;
|
|
756
766
|
now;
|
|
757
767
|
constructor(config = {}) {
|
|
758
768
|
if (process.env.NODE_ENV === "production" && !process.env.PAFI_ALLOW_MEMORY_RATE_LIMITER_IN_PROD) {
|
|
759
|
-
console.warn(
|
|
760
|
-
"[PAFI] MemoryRateLimiter not safe for multi-pod K8s deploys \u2014 rate counters are NOT shared across replicas, allowing round-robin bypass. Use a Redis-backed IRateLimiter in production."
|
|
761
|
-
);
|
|
769
|
+
console.warn("[PAFI] MemoryRateLimiter not safe for multi-pod K8s deploys \u2014 rate counters are NOT shared across replicas, allowing round-robin bypass. Use a Redis-backed IRateLimiter in production.");
|
|
762
770
|
}
|
|
763
771
|
this.limits = {
|
|
764
772
|
...DEFAULT_LIMITS,
|
|
@@ -768,47 +776,62 @@ var MemoryRateLimiter = class {
|
|
|
768
776
|
}
|
|
769
777
|
async consume(key, action) {
|
|
770
778
|
const limit = this.limits[action];
|
|
771
|
-
if (!limit) return {
|
|
779
|
+
if (!limit) return {
|
|
780
|
+
allowed: true
|
|
781
|
+
};
|
|
772
782
|
const bucketKey = `${action}:${key}`;
|
|
773
783
|
const now = this.now();
|
|
774
784
|
const bucket = this.buckets.get(bucketKey);
|
|
775
785
|
if (!bucket || now - bucket.windowStartedAt >= limit.windowMs) {
|
|
776
|
-
this.buckets.set(bucketKey, {
|
|
777
|
-
|
|
786
|
+
this.buckets.set(bucketKey, {
|
|
787
|
+
count: 1,
|
|
788
|
+
windowStartedAt: now
|
|
789
|
+
});
|
|
790
|
+
return {
|
|
791
|
+
allowed: true
|
|
792
|
+
};
|
|
778
793
|
}
|
|
779
794
|
if (bucket.count < limit.max) {
|
|
780
795
|
bucket.count += 1;
|
|
781
|
-
return {
|
|
796
|
+
return {
|
|
797
|
+
allowed: true
|
|
798
|
+
};
|
|
782
799
|
}
|
|
783
|
-
const retryAfterMs = Math.max(
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
800
|
+
const retryAfterMs = Math.max(0, bucket.windowStartedAt + limit.windowMs - now);
|
|
801
|
+
return {
|
|
802
|
+
allowed: false,
|
|
803
|
+
retryAfterMs
|
|
804
|
+
};
|
|
788
805
|
}
|
|
789
806
|
/**
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
807
|
+
* Test helper — clear all buckets. Not part of `IRateLimiter`; only
|
|
808
|
+
* exposed on the in-memory impl for unit tests.
|
|
809
|
+
*/
|
|
793
810
|
reset() {
|
|
794
811
|
this.buckets.clear();
|
|
795
812
|
}
|
|
796
813
|
};
|
|
797
814
|
var NoopRateLimiter = class {
|
|
815
|
+
static {
|
|
816
|
+
__name(this, "NoopRateLimiter");
|
|
817
|
+
}
|
|
798
818
|
warned = false;
|
|
799
819
|
async consume() {
|
|
800
820
|
if (!this.warned && process.env.NODE_ENV === "production") {
|
|
801
|
-
console.warn(
|
|
802
|
-
"[PAFI] NoopRateLimiter active \u2014 `/auth/nonce` and `/auth/login` are NOT throttled. Wire a `MemoryRateLimiter` (dev) or Redis-backed impl (prod) via `IssuerApiHandlersConfig.rateLimiter`."
|
|
803
|
-
);
|
|
821
|
+
console.warn("[PAFI] NoopRateLimiter active \u2014 `/auth/nonce` and `/auth/login` are NOT throttled. Wire a `MemoryRateLimiter` (dev) or Redis-backed impl (prod) via `IssuerApiHandlersConfig.rateLimiter`.");
|
|
804
822
|
this.warned = true;
|
|
805
823
|
}
|
|
806
|
-
return {
|
|
824
|
+
return {
|
|
825
|
+
allowed: true
|
|
826
|
+
};
|
|
807
827
|
}
|
|
808
828
|
};
|
|
809
829
|
|
|
810
830
|
// src/relay/types.ts
|
|
811
831
|
var RelayError = class extends import_core.PafiSdkError {
|
|
832
|
+
static {
|
|
833
|
+
__name(this, "RelayError");
|
|
834
|
+
}
|
|
812
835
|
httpStatus = "unprocessable";
|
|
813
836
|
code;
|
|
814
837
|
constructor(code, message, cause) {
|
|
@@ -824,6 +847,9 @@ var RelayError = class extends import_core.PafiSdkError {
|
|
|
824
847
|
var import_viem3 = require("viem");
|
|
825
848
|
var import_core5 = require("@pafi-dev/core");
|
|
826
849
|
var RelayService = class {
|
|
850
|
+
static {
|
|
851
|
+
__name(this, "RelayService");
|
|
852
|
+
}
|
|
827
853
|
provider;
|
|
828
854
|
chainId;
|
|
829
855
|
constructor(config = {}) {
|
|
@@ -831,25 +857,28 @@ var RelayService = class {
|
|
|
831
857
|
this.chainId = config.chainId;
|
|
832
858
|
}
|
|
833
859
|
/**
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
860
|
+
* Resolve the fee recipient + amount applied to the next UserOp:
|
|
861
|
+
*
|
|
862
|
+
* - If caller passed an explicit `feeRecipient`, use it (testing
|
|
863
|
+
* only — sponsor-relayer's L1 will reject any non-canonical
|
|
864
|
+
* recipient with `INSUFFICIENT_FEE`). Otherwise, default to
|
|
865
|
+
* `getContractAddresses(chainId).pafiFeeRecipient` when the
|
|
866
|
+
* service has a `chainId` configured.
|
|
867
|
+
* - If caller passed `feeAmount`, use it. Otherwise, when the
|
|
868
|
+
* service has both `provider` + `chainId`, auto-quote via
|
|
869
|
+
* `quoteOperatorFeePt`.
|
|
870
|
+
* - When the service is unconfigured AND caller passed nothing,
|
|
871
|
+
* return `{ feeAmount: 0n, feeRecipient: undefined }` — legacy
|
|
872
|
+
* "no fee" behavior, caller must opt in for the gas-reimbursement
|
|
873
|
+
* transfer to be added to the batch.
|
|
874
|
+
*/
|
|
849
875
|
async resolveFee(params) {
|
|
850
876
|
const feeRecipient = params.feeRecipient ?? (this.chainId !== void 0 ? (0, import_core5.getContractAddresses)(this.chainId).pafiFeeRecipient : void 0);
|
|
851
877
|
if (params.feeAmount !== void 0) {
|
|
852
|
-
return {
|
|
878
|
+
return {
|
|
879
|
+
feeAmount: params.feeAmount,
|
|
880
|
+
feeRecipient
|
|
881
|
+
};
|
|
853
882
|
}
|
|
854
883
|
if (this.provider && this.chainId !== void 0) {
|
|
855
884
|
const feeAmount = await (0, import_core5.quoteOperatorFeePt)({
|
|
@@ -857,44 +886,39 @@ var RelayService = class {
|
|
|
857
886
|
chainId: this.chainId,
|
|
858
887
|
pointTokenAddress: params.pointTokenAddress,
|
|
859
888
|
allowStaleFallback: true,
|
|
860
|
-
onFallback: (info) => {
|
|
861
|
-
console.warn(
|
|
862
|
-
|
|
863
|
-
);
|
|
864
|
-
}
|
|
889
|
+
onFallback: /* @__PURE__ */ __name((info) => {
|
|
890
|
+
console.warn(`[RelayService] operatorFeeQuoter fallback (${info.source}): ${info.reason} \u2192 using ${info.fallbackValue}`);
|
|
891
|
+
}, "onFallback")
|
|
865
892
|
});
|
|
866
|
-
return {
|
|
893
|
+
return {
|
|
894
|
+
feeAmount,
|
|
895
|
+
feeRecipient
|
|
896
|
+
};
|
|
867
897
|
}
|
|
868
|
-
return {
|
|
898
|
+
return {
|
|
899
|
+
feeAmount: 0n,
|
|
900
|
+
feeRecipient
|
|
901
|
+
};
|
|
869
902
|
}
|
|
870
903
|
/**
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
904
|
+
* Build an unsigned UserOp for Scenario 1 (Mint) — sig-gated
|
|
905
|
+
* `PointToken.mint(to, amount, deadline, minterSig)`.
|
|
906
|
+
*/
|
|
874
907
|
async prepareMint(params) {
|
|
875
908
|
if (!params.batchExecutorAddress) {
|
|
876
|
-
throw new RelayError(
|
|
877
|
-
"ENCODE_FAILED",
|
|
878
|
-
"prepareMint: batchExecutorAddress required"
|
|
879
|
-
);
|
|
909
|
+
throw new RelayError("ENCODE_FAILED", "prepareMint: batchExecutorAddress required");
|
|
880
910
|
}
|
|
881
911
|
if (!params.userAddress) {
|
|
882
912
|
throw new RelayError("ENCODE_FAILED", "prepareMint: userAddress required");
|
|
883
913
|
}
|
|
884
914
|
if (!params.pointTokenAddress) {
|
|
885
|
-
throw new RelayError(
|
|
886
|
-
"ENCODE_FAILED",
|
|
887
|
-
"prepareMint: pointTokenAddress required"
|
|
888
|
-
);
|
|
915
|
+
throw new RelayError("ENCODE_FAILED", "prepareMint: pointTokenAddress required");
|
|
889
916
|
}
|
|
890
917
|
if (params.amount <= 0n) {
|
|
891
918
|
throw new RelayError("ENCODE_FAILED", "prepareMint: amount must be positive");
|
|
892
919
|
}
|
|
893
920
|
if (!params.issuerSignerWallet) {
|
|
894
|
-
throw new RelayError(
|
|
895
|
-
"ENCODE_FAILED",
|
|
896
|
-
"prepareMint: issuerSignerWallet required (for MintRequest EIP-712 signature)"
|
|
897
|
-
);
|
|
921
|
+
throw new RelayError("ENCODE_FAILED", "prepareMint: issuerSignerWallet required (for MintRequest EIP-712 signature)");
|
|
898
922
|
}
|
|
899
923
|
if (params.deadline <= 0n) {
|
|
900
924
|
throw new RelayError("ENCODE_FAILED", "prepareMint: deadline must be positive");
|
|
@@ -905,35 +929,24 @@ var RelayService = class {
|
|
|
905
929
|
}
|
|
906
930
|
const MAX_DEADLINE_WINDOW = 3600n;
|
|
907
931
|
if (params.deadline > nowSecs + MAX_DEADLINE_WINDOW) {
|
|
908
|
-
throw new RelayError(
|
|
909
|
-
"ENCODE_FAILED",
|
|
910
|
-
"prepareMint: deadline exceeds maximum allowed window (1 hour)"
|
|
911
|
-
);
|
|
932
|
+
throw new RelayError("ENCODE_FAILED", "prepareMint: deadline exceeds maximum allowed window (1 hour)");
|
|
912
933
|
}
|
|
913
934
|
const MINT_SOURCE = import_core5.Source.EQUITY;
|
|
914
935
|
const useWrapper = params.mintFeeWrapperAddress !== void 0;
|
|
915
936
|
const receiverForSig = useWrapper ? params.mintFeeWrapperAddress : params.userAddress;
|
|
916
937
|
let minterSig;
|
|
917
938
|
try {
|
|
918
|
-
const sig = await (0, import_core5.signMintRequest)(
|
|
919
|
-
params.
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
nonce: params.mintRequestNonce,
|
|
927
|
-
deadline: params.deadline
|
|
928
|
-
}
|
|
929
|
-
);
|
|
939
|
+
const sig = await (0, import_core5.signMintRequest)(params.issuerSignerWallet, params.domain, {
|
|
940
|
+
user: params.userAddress,
|
|
941
|
+
receiver: receiverForSig,
|
|
942
|
+
amount: params.amount,
|
|
943
|
+
source: MINT_SOURCE,
|
|
944
|
+
nonce: params.mintRequestNonce,
|
|
945
|
+
deadline: params.deadline
|
|
946
|
+
});
|
|
930
947
|
minterSig = sig.serialized;
|
|
931
948
|
} catch (err) {
|
|
932
|
-
throw new RelayError(
|
|
933
|
-
"ENCODE_FAILED",
|
|
934
|
-
`prepareMint: failed to sign MintForRequest: ${errorMessage(err)}`,
|
|
935
|
-
err
|
|
936
|
-
);
|
|
949
|
+
throw new RelayError("ENCODE_FAILED", `prepareMint: failed to sign MintForRequest: ${errorMessage(err)}`, err);
|
|
937
950
|
}
|
|
938
951
|
let mintCallData;
|
|
939
952
|
let mintTarget;
|
|
@@ -966,11 +979,7 @@ var RelayService = class {
|
|
|
966
979
|
mintTarget = params.pointTokenAddress;
|
|
967
980
|
}
|
|
968
981
|
} catch (err) {
|
|
969
|
-
throw new RelayError(
|
|
970
|
-
"ENCODE_FAILED",
|
|
971
|
-
`prepareMint: failed to encode mint call: ${errorMessage(err)}`,
|
|
972
|
-
err
|
|
973
|
-
);
|
|
982
|
+
throw new RelayError("ENCODE_FAILED", `prepareMint: failed to encode mint call: ${errorMessage(err)}`, err);
|
|
974
983
|
}
|
|
975
984
|
const operations = [
|
|
976
985
|
{
|
|
@@ -986,16 +995,10 @@ var RelayService = class {
|
|
|
986
995
|
});
|
|
987
996
|
if (feeAmount > 0n) {
|
|
988
997
|
if (!feeRecipient) {
|
|
989
|
-
throw new RelayError(
|
|
990
|
-
"ENCODE_FAILED",
|
|
991
|
-
"prepareMint: feeRecipient could not be resolved \u2014 pass `feeRecipient` explicitly or construct RelayService with a `chainId`."
|
|
992
|
-
);
|
|
998
|
+
throw new RelayError("ENCODE_FAILED", "prepareMint: feeRecipient could not be resolved \u2014 pass `feeRecipient` explicitly or construct RelayService with a `chainId`.");
|
|
993
999
|
}
|
|
994
1000
|
if (feeRecipient === "0x0000000000000000000000000000000000000000") {
|
|
995
|
-
throw new RelayError(
|
|
996
|
-
"ENCODE_FAILED",
|
|
997
|
-
"prepareMint: feeRecipient must not be zero address"
|
|
998
|
-
);
|
|
1001
|
+
throw new RelayError("ENCODE_FAILED", "prepareMint: feeRecipient must not be zero address");
|
|
999
1002
|
}
|
|
1000
1003
|
operations.push({
|
|
1001
1004
|
target: params.pointTokenAddress,
|
|
@@ -1003,7 +1006,10 @@ var RelayService = class {
|
|
|
1003
1006
|
data: (0, import_viem3.encodeFunctionData)({
|
|
1004
1007
|
abi: import_viem3.erc20Abi,
|
|
1005
1008
|
functionName: "transfer",
|
|
1006
|
-
args: [
|
|
1009
|
+
args: [
|
|
1010
|
+
feeRecipient,
|
|
1011
|
+
feeAmount
|
|
1012
|
+
]
|
|
1007
1013
|
})
|
|
1008
1014
|
});
|
|
1009
1015
|
}
|
|
@@ -1019,29 +1025,23 @@ var RelayService = class {
|
|
|
1019
1025
|
});
|
|
1020
1026
|
}
|
|
1021
1027
|
/**
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1028
|
+
* Build an unsigned UserOp for Scenario 2 (Burn/Redeem) — sig-gated
|
|
1029
|
+
* `PointToken.burn(from, amount, deadline, burnerSig)`. Caller
|
|
1030
|
+
* provides a pre-signed `BurnRequest` + sig bytes (typically from
|
|
1031
|
+
* `PTRedeemHandler`).
|
|
1032
|
+
*
|
|
1033
|
+
* Direct burn (no sig) is not used — every burn goes through the
|
|
1034
|
+
* issuer-signed `BurnRequest` path.
|
|
1035
|
+
*/
|
|
1030
1036
|
async prepareBurn(params) {
|
|
1031
1037
|
if (!params.pointTokenAddress) {
|
|
1032
1038
|
throw new RelayError("ENCODE_FAILED", "prepareBurn: pointTokenAddress required");
|
|
1033
1039
|
}
|
|
1034
1040
|
if (!params.batchExecutorAddress) {
|
|
1035
|
-
throw new RelayError(
|
|
1036
|
-
"ENCODE_FAILED",
|
|
1037
|
-
"prepareBurn: batchExecutorAddress required"
|
|
1038
|
-
);
|
|
1041
|
+
throw new RelayError("ENCODE_FAILED", "prepareBurn: batchExecutorAddress required");
|
|
1039
1042
|
}
|
|
1040
1043
|
if (!params.burnRequest || !params.burnerSignature) {
|
|
1041
|
-
throw new RelayError(
|
|
1042
|
-
"ENCODE_FAILED",
|
|
1043
|
-
"prepareBurn: burnRequest + burnerSignature required"
|
|
1044
|
-
);
|
|
1044
|
+
throw new RelayError("ENCODE_FAILED", "prepareBurn: burnRequest + burnerSignature required");
|
|
1045
1045
|
}
|
|
1046
1046
|
let burnCallData;
|
|
1047
1047
|
try {
|
|
@@ -1057,11 +1057,7 @@ var RelayService = class {
|
|
|
1057
1057
|
]
|
|
1058
1058
|
});
|
|
1059
1059
|
} catch (err) {
|
|
1060
|
-
throw new RelayError(
|
|
1061
|
-
"ENCODE_FAILED",
|
|
1062
|
-
`prepareBurn: failed to encode burn call: ${errorMessage(err)}`,
|
|
1063
|
-
err
|
|
1064
|
-
);
|
|
1060
|
+
throw new RelayError("ENCODE_FAILED", `prepareBurn: failed to encode burn call: ${errorMessage(err)}`, err);
|
|
1065
1061
|
}
|
|
1066
1062
|
const operations = [];
|
|
1067
1063
|
const { feeAmount, feeRecipient } = await this.resolveFee({
|
|
@@ -1071,16 +1067,10 @@ var RelayService = class {
|
|
|
1071
1067
|
});
|
|
1072
1068
|
if (feeAmount > 0n) {
|
|
1073
1069
|
if (!feeRecipient) {
|
|
1074
|
-
throw new RelayError(
|
|
1075
|
-
"ENCODE_FAILED",
|
|
1076
|
-
"prepareBurn: feeRecipient could not be resolved \u2014 pass `feeRecipient` explicitly or construct RelayService with a `chainId`."
|
|
1077
|
-
);
|
|
1070
|
+
throw new RelayError("ENCODE_FAILED", "prepareBurn: feeRecipient could not be resolved \u2014 pass `feeRecipient` explicitly or construct RelayService with a `chainId`.");
|
|
1078
1071
|
}
|
|
1079
1072
|
if (feeRecipient === "0x0000000000000000000000000000000000000000") {
|
|
1080
|
-
throw new RelayError(
|
|
1081
|
-
"ENCODE_FAILED",
|
|
1082
|
-
"prepareBurn: feeRecipient must not be zero address"
|
|
1083
|
-
);
|
|
1073
|
+
throw new RelayError("ENCODE_FAILED", "prepareBurn: feeRecipient must not be zero address");
|
|
1084
1074
|
}
|
|
1085
1075
|
operations.push({
|
|
1086
1076
|
target: params.pointTokenAddress,
|
|
@@ -1088,7 +1078,10 @@ var RelayService = class {
|
|
|
1088
1078
|
data: (0, import_viem3.encodeFunctionData)({
|
|
1089
1079
|
abi: import_viem3.erc20Abi,
|
|
1090
1080
|
functionName: "transfer",
|
|
1091
|
-
args: [
|
|
1081
|
+
args: [
|
|
1082
|
+
feeRecipient,
|
|
1083
|
+
feeAmount
|
|
1084
|
+
]
|
|
1092
1085
|
})
|
|
1093
1086
|
});
|
|
1094
1087
|
}
|
|
@@ -1123,10 +1116,10 @@ var RelayService = class {
|
|
|
1123
1116
|
// call seeds the cache; subsequent ones hit it.
|
|
1124
1117
|
// =========================================================================
|
|
1125
1118
|
/**
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1119
|
+
* Build a dummy `PartialUserOperation` for the mint scenario, suitable
|
|
1120
|
+
* for `feeManager.estimateGasFee({ partialUserOp, ... })`. NO signing —
|
|
1121
|
+
* uses a 65-byte zero signature placeholder.
|
|
1122
|
+
*/
|
|
1130
1123
|
previewMintUserOp(params) {
|
|
1131
1124
|
const useWrapper = params.mintFeeWrapperAddress !== void 0;
|
|
1132
1125
|
let mintCallData;
|
|
@@ -1161,7 +1154,13 @@ var RelayService = class {
|
|
|
1161
1154
|
return (0, import_core5.buildPartialUserOperation)({
|
|
1162
1155
|
sender: params.userAddress,
|
|
1163
1156
|
nonce: params.aaNonce,
|
|
1164
|
-
operations: [
|
|
1157
|
+
operations: [
|
|
1158
|
+
{
|
|
1159
|
+
target: mintTarget,
|
|
1160
|
+
value: 0n,
|
|
1161
|
+
data: mintCallData
|
|
1162
|
+
}
|
|
1163
|
+
],
|
|
1165
1164
|
// Gas limits ignored by bundler estimate — it computes them.
|
|
1166
1165
|
gasLimits: {
|
|
1167
1166
|
callGasLimit: 1n,
|
|
@@ -1187,7 +1186,11 @@ var RelayService = class {
|
|
|
1187
1186
|
sender: params.userAddress,
|
|
1188
1187
|
nonce: params.aaNonce,
|
|
1189
1188
|
operations: [
|
|
1190
|
-
{
|
|
1189
|
+
{
|
|
1190
|
+
target: params.pointTokenAddress,
|
|
1191
|
+
value: 0n,
|
|
1192
|
+
data: burnCallData
|
|
1193
|
+
}
|
|
1191
1194
|
],
|
|
1192
1195
|
gasLimits: {
|
|
1193
1196
|
callGasLimit: 1n,
|
|
@@ -1201,11 +1204,15 @@ var PLACEHOLDER_SIG_65 = `0x${"00".repeat(65)}`;
|
|
|
1201
1204
|
function errorMessage(err) {
|
|
1202
1205
|
return err instanceof Error ? err.message : String(err);
|
|
1203
1206
|
}
|
|
1207
|
+
__name(errorMessage, "errorMessage");
|
|
1204
1208
|
|
|
1205
1209
|
// src/relay/feeManager.ts
|
|
1206
1210
|
var DEFAULT_GAS_UNITS = 500000n;
|
|
1207
1211
|
var DEFAULT_PREMIUM_BPS = 1e4;
|
|
1208
1212
|
var FeeManager = class _FeeManager {
|
|
1213
|
+
static {
|
|
1214
|
+
__name(this, "FeeManager");
|
|
1215
|
+
}
|
|
1209
1216
|
provider;
|
|
1210
1217
|
fallbackGasUnits;
|
|
1211
1218
|
gasPremiumBps;
|
|
@@ -1222,8 +1229,7 @@ var FeeManager = class _FeeManager {
|
|
|
1222
1229
|
static FEE_CACHE_TTL_MS = 1e4;
|
|
1223
1230
|
constructor(config) {
|
|
1224
1231
|
if (!config.provider) throw new Error("FeeManager: provider required");
|
|
1225
|
-
if (!config.quoteNativeToFee)
|
|
1226
|
-
throw new Error("FeeManager: quoteNativeToFee required");
|
|
1232
|
+
if (!config.quoteNativeToFee) throw new Error("FeeManager: quoteNativeToFee required");
|
|
1227
1233
|
this.provider = config.provider;
|
|
1228
1234
|
this.fallbackGasUnits = config.gasUnits ?? DEFAULT_GAS_UNITS;
|
|
1229
1235
|
this.gasPremiumBps = config.gasPremiumBps ?? DEFAULT_PREMIUM_BPS;
|
|
@@ -1232,16 +1238,16 @@ var FeeManager = class _FeeManager {
|
|
|
1232
1238
|
this.metrics = config.metrics;
|
|
1233
1239
|
}
|
|
1234
1240
|
/**
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1241
|
+
* Estimate the operator fee for the next sponsored UserOp.
|
|
1242
|
+
*
|
|
1243
|
+
* Without `opts` → legacy path: `gasUnits × gasPrice × premium →
|
|
1244
|
+
* quoteNativeToFee`. Cached for 10 s to absorb bursts.
|
|
1245
|
+
*
|
|
1246
|
+
* With `opts` AND `bundlerClient` → estimator path. Each call may
|
|
1247
|
+
* hit a different bundler-cached result; the SDK does NOT add its
|
|
1248
|
+
* own value cache because the estimator's cache TTL is the source
|
|
1249
|
+
* of truth for "how long is this estimate good for".
|
|
1250
|
+
*/
|
|
1245
1251
|
async estimateGasFee(opts = {}) {
|
|
1246
1252
|
const isLegacyCall = !opts.partialUserOp && !opts.scenario && !opts.contractAddress;
|
|
1247
1253
|
const now = Date.now();
|
|
@@ -1251,14 +1257,12 @@ var FeeManager = class _FeeManager {
|
|
|
1251
1257
|
const t0 = Date.now();
|
|
1252
1258
|
const { gasUnits, source } = await this.resolveGasUnits(opts);
|
|
1253
1259
|
const latencyMs = Date.now() - t0;
|
|
1254
|
-
this.safeEmit(
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
})
|
|
1261
|
-
);
|
|
1260
|
+
this.safeEmit(() => this.metrics?.onEstimate?.({
|
|
1261
|
+
source,
|
|
1262
|
+
scenario: opts.scenario,
|
|
1263
|
+
gasUnits,
|
|
1264
|
+
latencyMs
|
|
1265
|
+
}));
|
|
1262
1266
|
const gasPrice = await this.provider.getGasPrice();
|
|
1263
1267
|
const nativeCost = gasPrice * gasUnits;
|
|
1264
1268
|
const withPremium = nativeCost * BigInt(this.gasPremiumBps) / 10000n;
|
|
@@ -1276,7 +1280,10 @@ var FeeManager = class _FeeManager {
|
|
|
1276
1280
|
}
|
|
1277
1281
|
async resolveGasUnits(opts) {
|
|
1278
1282
|
if (!this.bundlerClient || !opts.partialUserOp || !opts.scenario || !opts.contractAddress) {
|
|
1279
|
-
return {
|
|
1283
|
+
return {
|
|
1284
|
+
gasUnits: this.fallbackGasUnits,
|
|
1285
|
+
source: "fallback"
|
|
1286
|
+
};
|
|
1280
1287
|
}
|
|
1281
1288
|
try {
|
|
1282
1289
|
const result = await this.bundlerClient.getGasUnits({
|
|
@@ -1285,16 +1292,20 @@ var FeeManager = class _FeeManager {
|
|
|
1285
1292
|
paymasterAddress: opts.paymasterAddress,
|
|
1286
1293
|
partialUserOp: opts.partialUserOp
|
|
1287
1294
|
});
|
|
1288
|
-
return {
|
|
1295
|
+
return {
|
|
1296
|
+
gasUnits: result.gasUnits,
|
|
1297
|
+
source: "estimator"
|
|
1298
|
+
};
|
|
1289
1299
|
} catch (err) {
|
|
1290
1300
|
const reason = err instanceof Error ? err.message : String(err);
|
|
1291
|
-
this.safeEmit(
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1301
|
+
this.safeEmit(() => this.metrics?.onEstimatorError?.({
|
|
1302
|
+
scenario: opts.scenario,
|
|
1303
|
+
reason
|
|
1304
|
+
}));
|
|
1305
|
+
return {
|
|
1306
|
+
gasUnits: this.fallbackGasUnits,
|
|
1307
|
+
source: "fallback"
|
|
1308
|
+
};
|
|
1298
1309
|
}
|
|
1299
1310
|
}
|
|
1300
1311
|
safeEmit(fn) {
|
|
@@ -1307,6 +1318,9 @@ var FeeManager = class _FeeManager {
|
|
|
1307
1318
|
|
|
1308
1319
|
// src/relay/bundlerEstimator.ts
|
|
1309
1320
|
var PafiEstimatorHttpError = class extends Error {
|
|
1321
|
+
static {
|
|
1322
|
+
__name(this, "PafiEstimatorHttpError");
|
|
1323
|
+
}
|
|
1310
1324
|
status;
|
|
1311
1325
|
body;
|
|
1312
1326
|
constructor(status, body, message) {
|
|
@@ -1322,9 +1336,7 @@ function createPafiEstimatorClient(config) {
|
|
|
1322
1336
|
if (!issuerId) throw new Error("createPafiEstimatorClient: issuerId required");
|
|
1323
1337
|
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
1324
1338
|
if (!fetchImpl) {
|
|
1325
|
-
throw new Error(
|
|
1326
|
-
"createPafiEstimatorClient: no fetch implementation available \u2014 pass `fetchImpl`"
|
|
1327
|
-
);
|
|
1339
|
+
throw new Error("createPafiEstimatorClient: no fetch implementation available \u2014 pass `fetchImpl`");
|
|
1328
1340
|
}
|
|
1329
1341
|
const url = `${baseUrl.replace(/\/$/, "")}/v1/estimate-gas-fee`;
|
|
1330
1342
|
return {
|
|
@@ -1368,15 +1380,19 @@ function createPafiEstimatorClient(config) {
|
|
|
1368
1380
|
}
|
|
1369
1381
|
};
|
|
1370
1382
|
}
|
|
1383
|
+
__name(createPafiEstimatorClient, "createPafiEstimatorClient");
|
|
1371
1384
|
|
|
1372
1385
|
// src/indexer/types.ts
|
|
1373
1386
|
var InMemoryCursorStore = class _InMemoryCursorStore {
|
|
1387
|
+
static {
|
|
1388
|
+
__name(this, "InMemoryCursorStore");
|
|
1389
|
+
}
|
|
1374
1390
|
cursor;
|
|
1375
1391
|
/**
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1392
|
+
* Child stores keyed by `forKey()`. Each child has its own cursor,
|
|
1393
|
+
* so a single InMemoryCursorStore can back N PointIndexers in tests
|
|
1394
|
+
* / single-process callers.
|
|
1395
|
+
*/
|
|
1380
1396
|
children = /* @__PURE__ */ new Map();
|
|
1381
1397
|
async load() {
|
|
1382
1398
|
return this.cursor;
|
|
@@ -1397,21 +1413,18 @@ var InMemoryCursorStore = class _InMemoryCursorStore {
|
|
|
1397
1413
|
// src/indexer/pointIndexer.ts
|
|
1398
1414
|
var import_viem4 = require("viem");
|
|
1399
1415
|
var PointIndexerFinalizeError = class extends Error {
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
this.context = context;
|
|
1403
|
-
this.cause = cause;
|
|
1404
|
-
this.name = "PointIndexerFinalizeError";
|
|
1416
|
+
static {
|
|
1417
|
+
__name(this, "PointIndexerFinalizeError");
|
|
1405
1418
|
}
|
|
1406
1419
|
context;
|
|
1407
1420
|
cause;
|
|
1421
|
+
constructor(message, context, cause) {
|
|
1422
|
+
super(message), this.context = context, this.cause = cause;
|
|
1423
|
+
this.name = "PointIndexerFinalizeError";
|
|
1424
|
+
}
|
|
1408
1425
|
};
|
|
1409
|
-
var TRANSFER_EVENT = (0, import_viem4.parseAbiItem)(
|
|
1410
|
-
|
|
1411
|
-
);
|
|
1412
|
-
var MINT_WITH_FEE_EVENT = (0, import_viem4.parseAbiItem)(
|
|
1413
|
-
"event MintWithFee(address indexed pointToken, address indexed to, uint256 grossAmount, uint256 netAmount, uint256 feeAmount)"
|
|
1414
|
-
);
|
|
1426
|
+
var TRANSFER_EVENT = (0, import_viem4.parseAbiItem)("event Transfer(address indexed from, address indexed to, uint256 value)");
|
|
1427
|
+
var MINT_WITH_FEE_EVENT = (0, import_viem4.parseAbiItem)("event MintWithFee(address indexed pointToken, address indexed to, uint256 grossAmount, uint256 netAmount, uint256 feeAmount)");
|
|
1415
1428
|
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
1416
1429
|
var DEAD_ADDRESS = "0x000000000000000000000000000000000000dEaD";
|
|
1417
1430
|
var DEFAULT_CONFIRMATIONS = 3;
|
|
@@ -1422,7 +1435,11 @@ function isNoWrapper(addr) {
|
|
|
1422
1435
|
const checksummed = (0, import_viem4.getAddress)(addr);
|
|
1423
1436
|
return checksummed === ZERO_ADDRESS || checksummed === DEAD_ADDRESS;
|
|
1424
1437
|
}
|
|
1438
|
+
__name(isNoWrapper, "isNoWrapper");
|
|
1425
1439
|
var PointIndexer = class {
|
|
1440
|
+
static {
|
|
1441
|
+
__name(this, "PointIndexer");
|
|
1442
|
+
}
|
|
1426
1443
|
provider;
|
|
1427
1444
|
pointTokenAddress;
|
|
1428
1445
|
mintFeeWrapperAddress;
|
|
@@ -1437,8 +1454,7 @@ var PointIndexer = class {
|
|
|
1437
1454
|
timer;
|
|
1438
1455
|
constructor(config) {
|
|
1439
1456
|
if (!config.provider) throw new Error("PointIndexer: provider required");
|
|
1440
|
-
if (!config.pointTokenAddress)
|
|
1441
|
-
throw new Error("PointIndexer: pointTokenAddress required");
|
|
1457
|
+
if (!config.pointTokenAddress) throw new Error("PointIndexer: pointTokenAddress required");
|
|
1442
1458
|
if (!config.ledger) throw new Error("PointIndexer: ledger required");
|
|
1443
1459
|
this.provider = config.provider;
|
|
1444
1460
|
this.pointTokenAddress = (0, import_viem4.getAddress)(config.pointTokenAddress);
|
|
@@ -1469,10 +1485,10 @@ var PointIndexer = class {
|
|
|
1469
1485
|
}
|
|
1470
1486
|
}
|
|
1471
1487
|
/**
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1488
|
+
* Run one poll cycle: load cursor → scan [cursor, safeHead] in
|
|
1489
|
+
* `batchSize` chunks → persist new cursor. Swallows any error and
|
|
1490
|
+
* schedules the next tick. Visible for test harnesses via a public name.
|
|
1491
|
+
*/
|
|
1476
1492
|
async tick() {
|
|
1477
1493
|
if (!this.running) return;
|
|
1478
1494
|
try {
|
|
@@ -1507,19 +1523,16 @@ var PointIndexer = class {
|
|
|
1507
1523
|
}
|
|
1508
1524
|
scheduleNext() {
|
|
1509
1525
|
if (!this.running) return;
|
|
1510
|
-
this.timer = setTimeout(
|
|
1511
|
-
() => this.tick().catch((err) => this.handleTickError(err)),
|
|
1512
|
-
this.pollIntervalMs
|
|
1513
|
-
);
|
|
1526
|
+
this.timer = setTimeout(() => this.tick().catch((err) => this.handleTickError(err)), this.pollIntervalMs);
|
|
1514
1527
|
}
|
|
1515
1528
|
// -------------------------------------------------------------------------
|
|
1516
1529
|
// Block scanning
|
|
1517
1530
|
// -------------------------------------------------------------------------
|
|
1518
1531
|
/**
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1532
|
+
* Scan `[from, to]` inclusive for mint events in `batchSize` chunks.
|
|
1533
|
+
* Callers can use this directly to backfill a specific range without
|
|
1534
|
+
* engaging `start()`. On completion, the cursor is advanced to `to + 1`.
|
|
1535
|
+
*/
|
|
1523
1536
|
async processBlockRange(from, to) {
|
|
1524
1537
|
if (from > to) return;
|
|
1525
1538
|
let cursor = from;
|
|
@@ -1543,15 +1556,17 @@ var PointIndexer = class {
|
|
|
1543
1556
|
// Event fetching — two modes (wrapper vs direct)
|
|
1544
1557
|
// -------------------------------------------------------------------------
|
|
1545
1558
|
/**
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1559
|
+
* Wrapper mode: listen for `MintWithFee` on the wrapper,
|
|
1560
|
+
* filtered to events for THIS pointToken only. The event's `to` field
|
|
1561
|
+
* is the actual end user, and `grossAmount` matches the lock amount.
|
|
1562
|
+
*/
|
|
1550
1563
|
async fetchWrapperMintEvents(fromBlock, toBlock) {
|
|
1551
1564
|
const logs = await this.provider.getLogs({
|
|
1552
1565
|
address: this.mintFeeWrapperAddress,
|
|
1553
1566
|
event: MINT_WITH_FEE_EVENT,
|
|
1554
|
-
args: {
|
|
1567
|
+
args: {
|
|
1568
|
+
pointToken: this.pointTokenAddress
|
|
1569
|
+
},
|
|
1555
1570
|
fromBlock,
|
|
1556
1571
|
toBlock
|
|
1557
1572
|
});
|
|
@@ -1573,14 +1588,16 @@ var PointIndexer = class {
|
|
|
1573
1588
|
return out;
|
|
1574
1589
|
}
|
|
1575
1590
|
/**
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1591
|
+
* Direct mode (legacy / chains without wrapper): listen for
|
|
1592
|
+
* `Transfer(from=0x0 → to)` on the PointToken itself.
|
|
1593
|
+
*/
|
|
1579
1594
|
async fetchTransferMintEvents(fromBlock, toBlock) {
|
|
1580
1595
|
const logs = await this.provider.getLogs({
|
|
1581
1596
|
address: this.pointTokenAddress,
|
|
1582
1597
|
event: TRANSFER_EVENT,
|
|
1583
|
-
args: {
|
|
1598
|
+
args: {
|
|
1599
|
+
from: ZERO_ADDRESS
|
|
1600
|
+
},
|
|
1584
1601
|
fromBlock,
|
|
1585
1602
|
toBlock
|
|
1586
1603
|
});
|
|
@@ -1607,42 +1624,30 @@ var PointIndexer = class {
|
|
|
1607
1624
|
// Finalization
|
|
1608
1625
|
// -------------------------------------------------------------------------
|
|
1609
1626
|
/**
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1627
|
+
* Finalize a single mint event: match it to a PENDING lock in the
|
|
1628
|
+
* ledger, then call `deductBalance` (which also resolves the lock in
|
|
1629
|
+
* the default `MemoryPointLedger`).
|
|
1630
|
+
*
|
|
1631
|
+
* No-matching-lock is a valid state: it means either the lock already
|
|
1632
|
+
* expired, or the mint was authorized out-of-band (e.g. a direct
|
|
1633
|
+
* `PointToken.mint()` from an EOA minter for testing). In that case we
|
|
1634
|
+
* do NOT touch the ledger — crediting here would silently allow the
|
|
1635
|
+
* issuer to mint without going through the gateway.
|
|
1636
|
+
*/
|
|
1620
1637
|
async finalize(evt) {
|
|
1621
|
-
const locks = await this.ledger.getLockedRequests(
|
|
1622
|
-
evt.to,
|
|
1623
|
-
this.pointTokenAddress
|
|
1624
|
-
);
|
|
1638
|
+
const locks = await this.ledger.getLockedRequests(evt.to, this.pointTokenAddress);
|
|
1625
1639
|
const match = pickMatchingLock(locks, evt.amount);
|
|
1626
1640
|
if (!match) return;
|
|
1627
1641
|
try {
|
|
1628
|
-
await this.ledger.deductBalance(
|
|
1629
|
-
evt.to,
|
|
1630
|
-
evt.amount,
|
|
1631
|
-
evt.txHash,
|
|
1632
|
-
this.pointTokenAddress
|
|
1633
|
-
);
|
|
1642
|
+
await this.ledger.deductBalance(evt.to, evt.amount, evt.txHash, this.pointTokenAddress);
|
|
1634
1643
|
} catch (err) {
|
|
1635
|
-
throw new PointIndexerFinalizeError(
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
blockNumber: evt.blockNumber
|
|
1643
|
-
},
|
|
1644
|
-
err
|
|
1645
|
-
);
|
|
1644
|
+
throw new PointIndexerFinalizeError(`PointIndexer.deductBalance failed for tx ${evt.txHash} (to=${evt.to}, amount=${evt.amount}, block=${evt.blockNumber}); cursor will NOT advance, next tick retries.`, {
|
|
1645
|
+
pointToken: this.pointTokenAddress,
|
|
1646
|
+
to: evt.to,
|
|
1647
|
+
amount: evt.amount,
|
|
1648
|
+
txHash: evt.txHash,
|
|
1649
|
+
blockNumber: evt.blockNumber
|
|
1650
|
+
}, err);
|
|
1646
1651
|
}
|
|
1647
1652
|
try {
|
|
1648
1653
|
await this.ledger.updateMintStatus(match.lockId, "MINTED", evt.txHash);
|
|
@@ -1661,32 +1666,35 @@ function pickMatchingLock(locks, amount) {
|
|
|
1661
1666
|
}
|
|
1662
1667
|
return best;
|
|
1663
1668
|
}
|
|
1669
|
+
__name(pickMatchingLock, "pickMatchingLock");
|
|
1664
1670
|
|
|
1665
1671
|
// src/indexer/burnIndexer.ts
|
|
1666
1672
|
var import_viem5 = require("viem");
|
|
1667
1673
|
var BurnIndexerFinalizeError = class extends Error {
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
this.context = context;
|
|
1671
|
-
this.cause = cause;
|
|
1672
|
-
this.name = "BurnIndexerFinalizeError";
|
|
1674
|
+
static {
|
|
1675
|
+
__name(this, "BurnIndexerFinalizeError");
|
|
1673
1676
|
}
|
|
1674
1677
|
context;
|
|
1675
1678
|
cause;
|
|
1679
|
+
constructor(message, context, cause) {
|
|
1680
|
+
super(message), this.context = context, this.cause = cause;
|
|
1681
|
+
this.name = "BurnIndexerFinalizeError";
|
|
1682
|
+
}
|
|
1676
1683
|
};
|
|
1677
|
-
var TRANSFER_EVENT2 = (0, import_viem5.parseAbiItem)(
|
|
1678
|
-
"event Transfer(address indexed from, address indexed to, uint256 value)"
|
|
1679
|
-
);
|
|
1684
|
+
var TRANSFER_EVENT2 = (0, import_viem5.parseAbiItem)("event Transfer(address indexed from, address indexed to, uint256 value)");
|
|
1680
1685
|
var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
|
|
1681
1686
|
var DEFAULT_CONFIRMATIONS2 = 3;
|
|
1682
1687
|
var DEFAULT_BATCH_SIZE2 = 2000n;
|
|
1683
1688
|
var DEFAULT_POLL_INTERVAL_MS2 = 5e3;
|
|
1684
1689
|
var BurnIndexer = class {
|
|
1690
|
+
static {
|
|
1691
|
+
__name(this, "BurnIndexer");
|
|
1692
|
+
}
|
|
1685
1693
|
provider;
|
|
1686
1694
|
/**
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1695
|
+
* The PointToken this indexer watches. Exposed so callers can key
|
|
1696
|
+
* leader-election locks / cursor stores by token
|
|
1697
|
+
*/
|
|
1690
1698
|
pointTokenAddress;
|
|
1691
1699
|
ledger;
|
|
1692
1700
|
cursorStore;
|
|
@@ -1700,24 +1708,19 @@ var BurnIndexer = class {
|
|
|
1700
1708
|
timer;
|
|
1701
1709
|
constructor(config) {
|
|
1702
1710
|
if (!config.provider) throw new Error("BurnIndexer: provider required");
|
|
1703
|
-
if (!config.pointTokenAddress)
|
|
1704
|
-
throw new Error("BurnIndexer: pointTokenAddress required");
|
|
1711
|
+
if (!config.pointTokenAddress) throw new Error("BurnIndexer: pointTokenAddress required");
|
|
1705
1712
|
if (!config.ledger) throw new Error("BurnIndexer: ledger required");
|
|
1706
1713
|
this.provider = config.provider;
|
|
1707
1714
|
this.pointTokenAddress = config.pointTokenAddress;
|
|
1708
1715
|
this.ledger = config.ledger;
|
|
1709
1716
|
this.cursorStore = config.cursorStore ?? new InMemoryCursorStore();
|
|
1710
1717
|
this.startBlock = config.fromBlock ?? 0n;
|
|
1711
|
-
this.confirmations = BigInt(
|
|
1712
|
-
config.confirmations ?? DEFAULT_CONFIRMATIONS2
|
|
1713
|
-
);
|
|
1718
|
+
this.confirmations = BigInt(config.confirmations ?? DEFAULT_CONFIRMATIONS2);
|
|
1714
1719
|
this.batchSize = BigInt(config.batchSize ?? Number(DEFAULT_BATCH_SIZE2));
|
|
1715
1720
|
this.pollIntervalMs = config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
1716
1721
|
if (config.onTickError) this.onTickError = config.onTickError;
|
|
1717
1722
|
if (!config.matchLockId) {
|
|
1718
|
-
throw new Error(
|
|
1719
|
-
"BurnIndexer: matchLockId is required. Provide a function that maps a burn event to its pending credit lockId. Without it, no on-chain burns will ever grant off-chain credits."
|
|
1720
|
-
);
|
|
1723
|
+
throw new Error("BurnIndexer: matchLockId is required. Provide a function that maps a burn event to its pending credit lockId. Without it, no on-chain burns will ever grant off-chain credits.");
|
|
1721
1724
|
}
|
|
1722
1725
|
this.matchLockId = config.matchLockId;
|
|
1723
1726
|
}
|
|
@@ -1767,16 +1770,13 @@ var BurnIndexer = class {
|
|
|
1767
1770
|
}
|
|
1768
1771
|
scheduleNext() {
|
|
1769
1772
|
if (!this.running) return;
|
|
1770
|
-
this.timer = setTimeout(
|
|
1771
|
-
() => this.tick().catch((err) => this.handleTickError(err)),
|
|
1772
|
-
this.pollIntervalMs
|
|
1773
|
-
);
|
|
1773
|
+
this.timer = setTimeout(() => this.tick().catch((err) => this.handleTickError(err)), this.pollIntervalMs);
|
|
1774
1774
|
}
|
|
1775
1775
|
/**
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1776
|
+
* Scan `[from, to]` inclusive for burn events. Callers can drive this
|
|
1777
|
+
* directly to backfill a specific range without `start()`. Cursor is
|
|
1778
|
+
* advanced to `to + 1` on completion.
|
|
1779
|
+
*/
|
|
1780
1780
|
async processBlockRange(from, to) {
|
|
1781
1781
|
if (from > to) return;
|
|
1782
1782
|
let cursor = from;
|
|
@@ -1785,8 +1785,9 @@ var BurnIndexer = class {
|
|
|
1785
1785
|
const logs = await this.provider.getLogs({
|
|
1786
1786
|
address: this.pointTokenAddress,
|
|
1787
1787
|
event: TRANSFER_EVENT2,
|
|
1788
|
-
args: {
|
|
1789
|
-
|
|
1788
|
+
args: {
|
|
1789
|
+
to: ZERO_ADDRESS2
|
|
1790
|
+
},
|
|
1790
1791
|
fromBlock: cursor,
|
|
1791
1792
|
toBlock: chunkEnd
|
|
1792
1793
|
});
|
|
@@ -1822,17 +1823,15 @@ var BurnIndexer = class {
|
|
|
1822
1823
|
return out;
|
|
1823
1824
|
}
|
|
1824
1825
|
/**
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1826
|
+
* Resolve a matching pending credit for this burn event and call
|
|
1827
|
+
* `ledger.resolveCreditByBurnTx(lockId, txHash)`. If no match found,
|
|
1828
|
+
* log + skip.
|
|
1829
|
+
*/
|
|
1829
1830
|
async finalize(evt) {
|
|
1830
1831
|
const txHash = evt.txHash;
|
|
1831
1832
|
const lockId = await this.matchLockId(evt);
|
|
1832
1833
|
if (lockId === void 0) {
|
|
1833
|
-
console.warn(
|
|
1834
|
-
"[PAFI] BurnIndexer: matchLockId returned undefined for burn tx " + txHash + ". This burn will NOT be credited. Implement matchLockId to map burn events to lock IDs."
|
|
1835
|
-
);
|
|
1834
|
+
console.warn("[PAFI] BurnIndexer: matchLockId returned undefined for burn tx " + txHash + ". This burn will NOT be credited. Implement matchLockId to map burn events to lock IDs.");
|
|
1836
1835
|
return;
|
|
1837
1836
|
}
|
|
1838
1837
|
if (!this.ledger.resolveCreditByBurnTx) {
|
|
@@ -1841,18 +1840,14 @@ var BurnIndexer = class {
|
|
|
1841
1840
|
try {
|
|
1842
1841
|
await this.ledger.resolveCreditByBurnTx(lockId, evt.txHash);
|
|
1843
1842
|
} catch (err) {
|
|
1844
|
-
throw new BurnIndexerFinalizeError(
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
lockId
|
|
1853
|
-
},
|
|
1854
|
-
err
|
|
1855
|
-
);
|
|
1843
|
+
throw new BurnIndexerFinalizeError(`BurnIndexer.resolveCreditByBurnTx failed for tx ${evt.txHash} (lockId=${lockId}, from=${evt.from}, amount=${evt.amount}, block=${evt.blockNumber}); cursor will NOT advance, next tick retries.`, {
|
|
1844
|
+
pointToken: this.pointTokenAddress,
|
|
1845
|
+
from: evt.from,
|
|
1846
|
+
amount: evt.amount,
|
|
1847
|
+
txHash: evt.txHash,
|
|
1848
|
+
blockNumber: evt.blockNumber,
|
|
1849
|
+
lockId
|
|
1850
|
+
}, err);
|
|
1856
1851
|
}
|
|
1857
1852
|
}
|
|
1858
1853
|
};
|
|
@@ -1862,10 +1857,9 @@ function makePostgresSingletonLock(runner) {
|
|
|
1862
1857
|
return {
|
|
1863
1858
|
async acquire(key) {
|
|
1864
1859
|
const lockId = hashKeyToInt64(key);
|
|
1865
|
-
const rows = await runner.query(
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
);
|
|
1860
|
+
const rows = await runner.query("SELECT pg_try_advisory_lock($1::bigint) AS got", [
|
|
1861
|
+
lockId
|
|
1862
|
+
]);
|
|
1869
1863
|
const got = rows[0]?.got === true;
|
|
1870
1864
|
if (!got) return null;
|
|
1871
1865
|
return {
|
|
@@ -1881,6 +1875,7 @@ function makePostgresSingletonLock(runner) {
|
|
|
1881
1875
|
}
|
|
1882
1876
|
};
|
|
1883
1877
|
}
|
|
1878
|
+
__name(makePostgresSingletonLock, "makePostgresSingletonLock");
|
|
1884
1879
|
function hashKeyToInt64(key) {
|
|
1885
1880
|
const FNV_OFFSET = 0xcbf29ce484222325n;
|
|
1886
1881
|
const FNV_PRIME = 0x100000001b3n;
|
|
@@ -1895,18 +1890,22 @@ function hashKeyToInt64(key) {
|
|
|
1895
1890
|
const signed = hash > SIGNED_MAX ? hash - TWO64 : hash;
|
|
1896
1891
|
return signed.toString();
|
|
1897
1892
|
}
|
|
1893
|
+
__name(hashKeyToInt64, "hashKeyToInt64");
|
|
1898
1894
|
|
|
1899
1895
|
// src/api/handlers.ts
|
|
1900
1896
|
var import_viem6 = require("viem");
|
|
1901
1897
|
var import_core6 = require("@pafi-dev/core");
|
|
1902
1898
|
var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
1899
|
+
static {
|
|
1900
|
+
__name(this, "IssuerApiHandlers");
|
|
1901
|
+
}
|
|
1903
1902
|
authService;
|
|
1904
1903
|
ledger;
|
|
1905
1904
|
provider;
|
|
1906
1905
|
/**
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1906
|
+
* Set of supported PointToken addresses (checksum-normalized). Handlers
|
|
1907
|
+
* validate the request's `pointTokenAddress` against this set.
|
|
1908
|
+
*/
|
|
1910
1909
|
supportedTokens;
|
|
1911
1910
|
chainId;
|
|
1912
1911
|
contracts;
|
|
@@ -1917,10 +1916,10 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
1917
1916
|
rateLimiter;
|
|
1918
1917
|
mintFeeWrapperAddress;
|
|
1919
1918
|
/**
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1919
|
+
* Per-token feeBps cache. Refreshed on /config when stale. feeBps
|
|
1920
|
+
* changes only when ops calls `wrapper.setRecipients`, so 5-min TTL
|
|
1921
|
+
* is more than safe for FE display purposes.
|
|
1922
|
+
*/
|
|
1924
1923
|
feeBpsCache = /* @__PURE__ */ new Map();
|
|
1925
1924
|
static FEE_BPS_TTL_MS = 5 * 60 * 1e3;
|
|
1926
1925
|
constructor(config) {
|
|
@@ -1928,11 +1927,11 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
1928
1927
|
this.ledger = config.ledger;
|
|
1929
1928
|
this.provider = config.provider;
|
|
1930
1929
|
this.rateLimiter = config.rateLimiter ?? new NoopRateLimiter();
|
|
1931
|
-
const raw = config.pointTokenAddresses && config.pointTokenAddresses.length > 0 ? config.pointTokenAddresses : config.pointTokenAddress ? [
|
|
1930
|
+
const raw = config.pointTokenAddresses && config.pointTokenAddresses.length > 0 ? config.pointTokenAddresses : config.pointTokenAddress ? [
|
|
1931
|
+
config.pointTokenAddress
|
|
1932
|
+
] : [];
|
|
1932
1933
|
if (raw.length === 0) {
|
|
1933
|
-
throw new Error(
|
|
1934
|
-
"IssuerApiHandlers: pointTokenAddress or pointTokenAddresses required"
|
|
1935
|
-
);
|
|
1934
|
+
throw new Error("IssuerApiHandlers: pointTokenAddress or pointTokenAddresses required");
|
|
1936
1935
|
}
|
|
1937
1936
|
const normalized = raw.map((a) => (0, import_viem6.getAddress)(a));
|
|
1938
1937
|
this.supportedTokens = new Set(normalized);
|
|
@@ -1950,63 +1949,48 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
1950
1949
|
// Public handlers (no auth required)
|
|
1951
1950
|
// =========================================================================
|
|
1952
1951
|
/**
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1952
|
+
* `GET /auth/nonce`
|
|
1953
|
+
*
|
|
1954
|
+
* @param rateLimitKey Caller-side rate-limit key (typically client IP).
|
|
1955
|
+
* The HTTP layer (controller/middleware) extracts
|
|
1956
|
+
* this from the request and passes it through.
|
|
1957
|
+
* When omitted, no rate limit applies — production
|
|
1958
|
+
* callers SHOULD always pass a key.
|
|
1959
|
+
*/
|
|
1961
1960
|
async handleGetNonce(rateLimitKey) {
|
|
1962
1961
|
if (rateLimitKey) {
|
|
1963
|
-
const result = await this.rateLimiter.consume(
|
|
1964
|
-
rateLimitKey,
|
|
1965
|
-
"auth_nonce"
|
|
1966
|
-
);
|
|
1962
|
+
const result = await this.rateLimiter.consume(rateLimitKey, "auth_nonce");
|
|
1967
1963
|
if (!result.allowed) {
|
|
1968
|
-
throw new import_core3.ValidationError(
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
retryAfterMs: result.retryAfterMs ?? 0,
|
|
1973
|
-
action: "auth_nonce"
|
|
1974
|
-
}
|
|
1975
|
-
);
|
|
1964
|
+
throw new import_core3.ValidationError("RATE_LIMIT_EXCEEDED", "handleGetNonce: too many requests", {
|
|
1965
|
+
retryAfterMs: result.retryAfterMs ?? 0,
|
|
1966
|
+
action: "auth_nonce"
|
|
1967
|
+
});
|
|
1976
1968
|
}
|
|
1977
1969
|
}
|
|
1978
1970
|
const nonce = await this.authService.getNonce();
|
|
1979
|
-
return {
|
|
1971
|
+
return {
|
|
1972
|
+
nonce
|
|
1973
|
+
};
|
|
1980
1974
|
}
|
|
1981
1975
|
/**
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1976
|
+
* `POST /auth/login`
|
|
1977
|
+
*
|
|
1978
|
+
* @param body Login message + signature.
|
|
1979
|
+
* @param rateLimitKey Caller-side rate-limit key (typically client IP
|
|
1980
|
+
* or `body.userAddress` if known). See `handleGetNonce`.
|
|
1981
|
+
*/
|
|
1988
1982
|
async handleLogin(body, rateLimitKey) {
|
|
1989
1983
|
if (rateLimitKey) {
|
|
1990
|
-
const result2 = await this.rateLimiter.consume(
|
|
1991
|
-
rateLimitKey,
|
|
1992
|
-
"auth_login"
|
|
1993
|
-
);
|
|
1984
|
+
const result2 = await this.rateLimiter.consume(rateLimitKey, "auth_login");
|
|
1994
1985
|
if (!result2.allowed) {
|
|
1995
|
-
throw new import_core3.ValidationError(
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
retryAfterMs: result2.retryAfterMs ?? 0,
|
|
2000
|
-
action: "auth_login"
|
|
2001
|
-
}
|
|
2002
|
-
);
|
|
1986
|
+
throw new import_core3.ValidationError("RATE_LIMIT_EXCEEDED", "handleLogin: too many requests", {
|
|
1987
|
+
retryAfterMs: result2.retryAfterMs ?? 0,
|
|
1988
|
+
action: "auth_login"
|
|
1989
|
+
});
|
|
2003
1990
|
}
|
|
2004
1991
|
}
|
|
2005
1992
|
if (!body || typeof body.message !== "string" || body.message.length === 0 || typeof body.signature !== "string" || body.signature.length <= 2) {
|
|
2006
|
-
throw new import_core3.ValidationError(
|
|
2007
|
-
"INVALID_LOGIN_BODY",
|
|
2008
|
-
"handleLogin: message and signature are required"
|
|
2009
|
-
);
|
|
1993
|
+
throw new import_core3.ValidationError("INVALID_LOGIN_BODY", "handleLogin: message and signature are required");
|
|
2010
1994
|
}
|
|
2011
1995
|
if (body.message.length > 4096) {
|
|
2012
1996
|
throw new import_core3.ValidationError("MESSAGE_TOO_LONG", "message too long");
|
|
@@ -2022,11 +2006,11 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
2022
2006
|
};
|
|
2023
2007
|
}
|
|
2024
2008
|
/**
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2009
|
+
* `GET /config?chainId=<id>`
|
|
2010
|
+
*
|
|
2011
|
+
* Returns the contract addresses and chain id that the frontend SDK
|
|
2012
|
+
* needs to build EIP-712 messages and interact with on-chain.
|
|
2013
|
+
*/
|
|
2030
2014
|
async handleConfig(chainId) {
|
|
2031
2015
|
if (!Number.isInteger(chainId) || chainId <= 0) {
|
|
2032
2016
|
throw new import_core3.ValidationError("INVALID_CHAIN_ID", "invalid chainId", {
|
|
@@ -2034,11 +2018,10 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
2034
2018
|
});
|
|
2035
2019
|
}
|
|
2036
2020
|
if (chainId !== this.chainId) {
|
|
2037
|
-
throw new import_core3.ValidationError(
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
);
|
|
2021
|
+
throw new import_core3.ValidationError("UNSUPPORTED_CHAIN_ID", `handleConfig: unsupported chainId ${chainId}`, {
|
|
2022
|
+
requested: chainId,
|
|
2023
|
+
supported: this.chainId
|
|
2024
|
+
});
|
|
2042
2025
|
}
|
|
2043
2026
|
const contracts = {
|
|
2044
2027
|
...this.contracts,
|
|
@@ -2052,60 +2035,55 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
2052
2035
|
contracts
|
|
2053
2036
|
};
|
|
2054
2037
|
if (this.mintFeeWrapperAddress) {
|
|
2055
|
-
const byToken = await this.resolveFeeBpsByToken(
|
|
2056
|
-
this.mintFeeWrapperAddress
|
|
2057
|
-
);
|
|
2038
|
+
const byToken = await this.resolveFeeBpsByToken(this.mintFeeWrapperAddress);
|
|
2058
2039
|
if (byToken) response.mintFeeBpsByToken = byToken;
|
|
2059
2040
|
}
|
|
2060
2041
|
if (this.pafiWebUrl) response.pafiWebUrl = this.pafiWebUrl;
|
|
2061
2042
|
return response;
|
|
2062
2043
|
}
|
|
2063
2044
|
/**
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2045
|
+
* Read `totalFeeBps(pointToken)` for every supported PointToken from
|
|
2046
|
+
* the wrapper. Cached per-token for 5 minutes. Returns `undefined`
|
|
2047
|
+
* (caller drops the field) if every read fails — FE must treat
|
|
2048
|
+
* "field missing" as "fee unknown, do not display".
|
|
2049
|
+
*/
|
|
2069
2050
|
async resolveFeeBpsByToken(wrapper) {
|
|
2070
2051
|
const now = Date.now();
|
|
2071
2052
|
const out = {};
|
|
2072
2053
|
let anyFresh = false;
|
|
2073
|
-
await Promise.all(
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2054
|
+
await Promise.all(Array.from(this.supportedTokens).map(async (token) => {
|
|
2055
|
+
const cached = this.feeBpsCache.get(token);
|
|
2056
|
+
if (cached && cached.expiresAt > now) {
|
|
2057
|
+
out[token.toLowerCase()] = cached.value;
|
|
2058
|
+
anyFresh = true;
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
try {
|
|
2062
|
+
const bps = await (0, import_core6.getMintFeeBps)(this.provider, wrapper, token);
|
|
2063
|
+
this.feeBpsCache.set(token, {
|
|
2064
|
+
value: bps,
|
|
2065
|
+
expiresAt: now + _IssuerApiHandlers.FEE_BPS_TTL_MS
|
|
2066
|
+
});
|
|
2067
|
+
out[token.toLowerCase()] = bps;
|
|
2068
|
+
anyFresh = true;
|
|
2069
|
+
} catch {
|
|
2070
|
+
if (cached) {
|
|
2077
2071
|
out[token.toLowerCase()] = cached.value;
|
|
2078
2072
|
anyFresh = true;
|
|
2079
|
-
return;
|
|
2080
2073
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
this.feeBpsCache.set(token, {
|
|
2084
|
-
value: bps,
|
|
2085
|
-
expiresAt: now + _IssuerApiHandlers.FEE_BPS_TTL_MS
|
|
2086
|
-
});
|
|
2087
|
-
out[token.toLowerCase()] = bps;
|
|
2088
|
-
anyFresh = true;
|
|
2089
|
-
} catch {
|
|
2090
|
-
if (cached) {
|
|
2091
|
-
out[token.toLowerCase()] = cached.value;
|
|
2092
|
-
anyFresh = true;
|
|
2093
|
-
}
|
|
2094
|
-
}
|
|
2095
|
-
})
|
|
2096
|
-
);
|
|
2074
|
+
}
|
|
2075
|
+
}));
|
|
2097
2076
|
return anyFresh ? out : void 0;
|
|
2098
2077
|
}
|
|
2099
2078
|
/** `GET /gas-fee` — quoted in USDT (6-decimal base units). */
|
|
2100
2079
|
async handleGasFee() {
|
|
2101
2080
|
if (!this.feeManager) {
|
|
2102
|
-
throw new ConfigurationError(
|
|
2103
|
-
"FEE_MANAGER_NOT_CONFIGURED",
|
|
2104
|
-
"handleGasFee: feeManager is not configured on this issuer"
|
|
2105
|
-
);
|
|
2081
|
+
throw new ConfigurationError("FEE_MANAGER_NOT_CONFIGURED", "handleGasFee: feeManager is not configured on this issuer");
|
|
2106
2082
|
}
|
|
2107
2083
|
const gasFeeUsdt = await this.feeManager.estimateGasFee();
|
|
2108
|
-
return {
|
|
2084
|
+
return {
|
|
2085
|
+
gasFeeUsdt
|
|
2086
|
+
};
|
|
2109
2087
|
}
|
|
2110
2088
|
// =========================================================================
|
|
2111
2089
|
// Protected handlers (JWT required — userAddress extracted by middleware)
|
|
@@ -2115,57 +2093,49 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
2115
2093
|
await this.authService.logout(token);
|
|
2116
2094
|
}
|
|
2117
2095
|
/**
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2096
|
+
* `GET /pools?chainId=<id>&pointToken=<addr>`
|
|
2097
|
+
*
|
|
2098
|
+
* Delegates to the injected `PoolsProvider`. The handler itself does
|
|
2099
|
+
* not know where pools come from — that's an issuer decision.
|
|
2100
|
+
*/
|
|
2123
2101
|
async handlePools(_userAddress, request) {
|
|
2124
2102
|
if (!this.poolsProvider) {
|
|
2125
|
-
throw new ConfigurationError(
|
|
2126
|
-
"POOLS_PROVIDER_NOT_CONFIGURED",
|
|
2127
|
-
"handlePools: poolsProvider is not configured on this issuer"
|
|
2128
|
-
);
|
|
2103
|
+
throw new ConfigurationError("POOLS_PROVIDER_NOT_CONFIGURED", "handlePools: poolsProvider is not configured on this issuer");
|
|
2129
2104
|
}
|
|
2130
2105
|
if (request.chainId !== this.chainId) {
|
|
2131
|
-
throw new import_core3.ValidationError(
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
);
|
|
2106
|
+
throw new import_core3.ValidationError("UNSUPPORTED_CHAIN_ID", `handlePools: unsupported chainId ${request.chainId}`, {
|
|
2107
|
+
requested: request.chainId,
|
|
2108
|
+
supported: this.chainId
|
|
2109
|
+
});
|
|
2136
2110
|
}
|
|
2137
2111
|
return this.poolsProvider(request);
|
|
2138
2112
|
}
|
|
2139
2113
|
/**
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2114
|
+
* `GET /user?chainId=<id>&user=<addr>&pointToken=<addr>`
|
|
2115
|
+
*
|
|
2116
|
+
* Returns per-user state the frontend needs to build a fresh mint:
|
|
2117
|
+
* on-chain nonces + minter status + off-chain balance.
|
|
2118
|
+
*/
|
|
2145
2119
|
async handleUser(userAddress, request) {
|
|
2146
2120
|
if (request.chainId !== this.chainId) {
|
|
2147
|
-
throw new import_core3.ValidationError(
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
);
|
|
2121
|
+
throw new import_core3.ValidationError("UNSUPPORTED_CHAIN_ID", `handleUser: unsupported chainId ${request.chainId}`, {
|
|
2122
|
+
requested: request.chainId,
|
|
2123
|
+
supported: this.chainId
|
|
2124
|
+
});
|
|
2152
2125
|
}
|
|
2153
2126
|
const normalizedAuthed = (0, import_viem6.getAddress)(userAddress);
|
|
2154
2127
|
const normalizedRequest = (0, import_viem6.getAddress)(request.userAddress);
|
|
2155
2128
|
if (normalizedAuthed !== normalizedRequest) {
|
|
2156
|
-
throw new import_core3.ValidationError(
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
);
|
|
2129
|
+
throw new import_core3.ValidationError("USER_ADDRESS_MISMATCH", "handleUser: request userAddress must match authenticated user", {
|
|
2130
|
+
authenticated: normalizedAuthed,
|
|
2131
|
+
requested: normalizedRequest
|
|
2132
|
+
});
|
|
2161
2133
|
}
|
|
2162
2134
|
const pointToken = (0, import_viem6.getAddress)(request.pointTokenAddress);
|
|
2163
2135
|
if (!this.supportedTokens.has(pointToken)) {
|
|
2164
|
-
throw new import_core3.ValidationError(
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
{ requested: pointToken }
|
|
2168
|
-
);
|
|
2136
|
+
throw new import_core3.ValidationError("UNSUPPORTED_POINT_TOKEN", `handleUser: unsupported pointToken ${pointToken}`, {
|
|
2137
|
+
requested: pointToken
|
|
2138
|
+
});
|
|
2169
2139
|
}
|
|
2170
2140
|
const [offChainBalance, onChainBalance, minter] = await Promise.all([
|
|
2171
2141
|
this.ledger.getBalance(normalizedAuthed, pointToken),
|
|
@@ -2177,7 +2147,6 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
2177
2147
|
onChainBalance,
|
|
2178
2148
|
totalBalance: offChainBalance + onChainBalance,
|
|
2179
2149
|
balance: offChainBalance,
|
|
2180
|
-
// deprecated alias
|
|
2181
2150
|
isMinter: minter
|
|
2182
2151
|
};
|
|
2183
2152
|
}
|
|
@@ -2185,56 +2154,41 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
2185
2154
|
// removed in 0.5.43 — callers should use `PTClaimHandler` directly or
|
|
2186
2155
|
// wire `IssuerApiAdapter.claim()` which composes the full flow.
|
|
2187
2156
|
/**
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2157
|
+
* `GET /redemption/preview?pointToken=<addr>`
|
|
2158
|
+
*
|
|
2159
|
+
* Returns the headroom currently available to `userAddress` under the
|
|
2160
|
+
* configured RedemptionPolicy. Pure read — does not record anything.
|
|
2161
|
+
* Use this for UI to render "X PT redeemable now / next available at …".
|
|
2162
|
+
*/
|
|
2194
2163
|
async handleRedemptionPreview(userAddress, request) {
|
|
2195
2164
|
if (!this.redemption) {
|
|
2196
|
-
throw new ConfigurationError(
|
|
2197
|
-
"REDEMPTION_NOT_CONFIGURED",
|
|
2198
|
-
"handleRedemptionPreview: redemption is not configured on this issuer"
|
|
2199
|
-
);
|
|
2165
|
+
throw new ConfigurationError("REDEMPTION_NOT_CONFIGURED", "handleRedemptionPreview: redemption is not configured on this issuer");
|
|
2200
2166
|
}
|
|
2201
2167
|
const tokenAddress = request.pointTokenAddress ? this.requireSupportedToken((0, import_viem6.getAddress)(request.pointTokenAddress), "handleRedemptionPreview") : void 0;
|
|
2202
|
-
const preview = await this.redemption.preview(
|
|
2203
|
-
(0, import_viem6.getAddress)(userAddress),
|
|
2204
|
-
tokenAddress
|
|
2205
|
-
);
|
|
2168
|
+
const preview = await this.redemption.preview((0, import_viem6.getAddress)(userAddress), tokenAddress);
|
|
2206
2169
|
return preview;
|
|
2207
2170
|
}
|
|
2208
2171
|
/**
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2172
|
+
* `POST /redemption/evaluate`
|
|
2173
|
+
*
|
|
2174
|
+
* Pre-flight check before the issuer signs a BurnRequest. Returns
|
|
2175
|
+
* { allowed, denial?, preview }. Caller (the burn-orchestrator) MUST
|
|
2176
|
+
* re-check on the actual initiate path — evaluate is read-only and a
|
|
2177
|
+
* caller could race two requests under the same headroom. The intended
|
|
2178
|
+
* write path is: evaluate → sign BurnRequest → reserve pending credit
|
|
2179
|
+
* → call `service.redemption.recordSuccessfulInitiate()`.
|
|
2180
|
+
*/
|
|
2218
2181
|
async handleRedemptionEvaluate(userAddress, request) {
|
|
2219
2182
|
if (!this.redemption) {
|
|
2220
|
-
throw new ConfigurationError(
|
|
2221
|
-
"REDEMPTION_NOT_CONFIGURED",
|
|
2222
|
-
"handleRedemptionEvaluate: redemption is not configured on this issuer"
|
|
2223
|
-
);
|
|
2183
|
+
throw new ConfigurationError("REDEMPTION_NOT_CONFIGURED", "handleRedemptionEvaluate: redemption is not configured on this issuer");
|
|
2224
2184
|
}
|
|
2225
2185
|
if (request.amountPt <= 0n) {
|
|
2226
|
-
throw new import_core3.ValidationError(
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
{ amountPt: request.amountPt.toString() }
|
|
2230
|
-
);
|
|
2186
|
+
throw new import_core3.ValidationError("INVALID_AMOUNT", "handleRedemptionEvaluate: amountPt must be positive", {
|
|
2187
|
+
amountPt: request.amountPt.toString()
|
|
2188
|
+
});
|
|
2231
2189
|
}
|
|
2232
2190
|
const tokenAddress = request.pointTokenAddress ? this.requireSupportedToken((0, import_viem6.getAddress)(request.pointTokenAddress), "handleRedemptionEvaluate") : void 0;
|
|
2233
|
-
const decision = await this.redemption.evaluate(
|
|
2234
|
-
(0, import_viem6.getAddress)(userAddress),
|
|
2235
|
-
request.amountPt,
|
|
2236
|
-
tokenAddress
|
|
2237
|
-
);
|
|
2191
|
+
const decision = await this.redemption.evaluate((0, import_viem6.getAddress)(userAddress), request.amountPt, tokenAddress);
|
|
2238
2192
|
const response = {
|
|
2239
2193
|
allowed: decision.allowed,
|
|
2240
2194
|
preview: decision.preview
|
|
@@ -2244,11 +2198,9 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
2244
2198
|
}
|
|
2245
2199
|
requireSupportedToken(pointToken, handler) {
|
|
2246
2200
|
if (!this.supportedTokens.has(pointToken)) {
|
|
2247
|
-
throw new import_core3.ValidationError(
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
{ requested: pointToken }
|
|
2251
|
-
);
|
|
2201
|
+
throw new import_core3.ValidationError("UNSUPPORTED_POINT_TOKEN", `${handler}: unsupported pointToken ${pointToken}`, {
|
|
2202
|
+
requested: pointToken
|
|
2203
|
+
});
|
|
2252
2204
|
}
|
|
2253
2205
|
return pointToken;
|
|
2254
2206
|
}
|
|
@@ -2262,10 +2214,17 @@ var NAME_ABI = [
|
|
|
2262
2214
|
name: "name",
|
|
2263
2215
|
stateMutability: "view",
|
|
2264
2216
|
inputs: [],
|
|
2265
|
-
outputs: [
|
|
2217
|
+
outputs: [
|
|
2218
|
+
{
|
|
2219
|
+
type: "string"
|
|
2220
|
+
}
|
|
2221
|
+
]
|
|
2266
2222
|
}
|
|
2267
2223
|
];
|
|
2268
2224
|
var PointTokenDomainResolver = class {
|
|
2225
|
+
static {
|
|
2226
|
+
__name(this, "PointTokenDomainResolver");
|
|
2227
|
+
}
|
|
2269
2228
|
provider;
|
|
2270
2229
|
overrides;
|
|
2271
2230
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -2312,6 +2271,9 @@ var DEFAULT_REDEEM_LOCK_MS = 15 * 60 * 1e3;
|
|
|
2312
2271
|
var M11_SAFETY_MARGIN_MS = 30 * 1e3;
|
|
2313
2272
|
var DEFAULT_SIG_DEADLINE_SEC = (DEFAULT_REDEEM_LOCK_MS - M11_SAFETY_MARGIN_MS) / 1e3;
|
|
2314
2273
|
var PTRedeemError = class extends import_core.PafiSdkError {
|
|
2274
|
+
static {
|
|
2275
|
+
__name(this, "PTRedeemError");
|
|
2276
|
+
}
|
|
2315
2277
|
httpStatus = "unprocessable";
|
|
2316
2278
|
code;
|
|
2317
2279
|
policyDenialCode;
|
|
@@ -2324,6 +2286,9 @@ var PTRedeemError = class extends import_core.PafiSdkError {
|
|
|
2324
2286
|
}
|
|
2325
2287
|
};
|
|
2326
2288
|
var PTRedeemHandler = class {
|
|
2289
|
+
static {
|
|
2290
|
+
__name(this, "PTRedeemHandler");
|
|
2291
|
+
}
|
|
2327
2292
|
ledger;
|
|
2328
2293
|
relayService;
|
|
2329
2294
|
provider;
|
|
@@ -2338,31 +2303,25 @@ var PTRedeemHandler = class {
|
|
|
2338
2303
|
now;
|
|
2339
2304
|
redemptionService;
|
|
2340
2305
|
/**
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2306
|
+
* Per-user in-flight nonce guard (single-process only).
|
|
2307
|
+
*
|
|
2308
|
+
* Prevents two concurrent requests from reading the same on-chain
|
|
2309
|
+
* burnRequestNonce before either has completed, which would produce two
|
|
2310
|
+
* signed UserOps with the same nonce — only one succeeds on-chain; the
|
|
2311
|
+
* other leaves an orphaned pending credit and a wasted signer call.
|
|
2312
|
+
*
|
|
2313
|
+
* NOTE: This guard is effective only within a single Node.js process. For
|
|
2314
|
+
* multi-instance deployments (k8s, PM2 cluster), enforce mutual exclusion
|
|
2315
|
+
* via a distributed lock (Redis SETNX / Postgres advisory lock) keyed on
|
|
2316
|
+
* `(userAddress, pointTokenAddress)` BEFORE calling `handle()`.
|
|
2317
|
+
*/
|
|
2353
2318
|
inFlightNonces = /* @__PURE__ */ new Map();
|
|
2354
2319
|
constructor(config) {
|
|
2355
2320
|
if (!config.ledger.reservePendingCredit) {
|
|
2356
|
-
throw new PTRedeemError(
|
|
2357
|
-
"LEDGER_NOT_SUPPORTED",
|
|
2358
|
-
"PTRedeemHandler requires a ledger that implements reservePendingCredit() (v0.3.0+)"
|
|
2359
|
-
);
|
|
2321
|
+
throw new PTRedeemError("LEDGER_NOT_SUPPORTED", "PTRedeemHandler requires a ledger that implements reservePendingCredit() (v0.3.0+)");
|
|
2360
2322
|
}
|
|
2361
2323
|
if (!config.burnerSignerWallet) {
|
|
2362
|
-
throw new PTRedeemError(
|
|
2363
|
-
"SIGNING_FAILED",
|
|
2364
|
-
"PTRedeemHandler requires burnerSignerWallet (issuer burner signer)"
|
|
2365
|
-
);
|
|
2324
|
+
throw new PTRedeemError("SIGNING_FAILED", "PTRedeemHandler requires burnerSignerWallet (issuer burner signer)");
|
|
2366
2325
|
}
|
|
2367
2326
|
this.ledger = config.ledger;
|
|
2368
2327
|
this.relayService = config.relayService;
|
|
@@ -2373,10 +2332,7 @@ var PTRedeemHandler = class {
|
|
|
2373
2332
|
this.domainResolver = config.domainResolver;
|
|
2374
2333
|
this.burnerSignerWallet = config.burnerSignerWallet;
|
|
2375
2334
|
if (!config.supportedTokens) {
|
|
2376
|
-
throw new PTRedeemError(
|
|
2377
|
-
"UNSUPPORTED_POINT_TOKEN",
|
|
2378
|
-
"PTRedeemHandler requires `supportedTokens` (issuer's allow-listed PointToken contracts)."
|
|
2379
|
-
);
|
|
2335
|
+
throw new PTRedeemError("UNSUPPORTED_POINT_TOKEN", "PTRedeemHandler requires `supportedTokens` (issuer's allow-listed PointToken contracts).");
|
|
2380
2336
|
}
|
|
2381
2337
|
this.supportedTokens = config.supportedTokens;
|
|
2382
2338
|
if (this.burnerSignerWallet?.account?.type === "local") {
|
|
@@ -2387,10 +2343,7 @@ var PTRedeemHandler = class {
|
|
|
2387
2343
|
this.now = config.now ?? (() => Date.now());
|
|
2388
2344
|
const maxAllowedSignatureMs = this.redeemLockDurationMs - M11_SAFETY_MARGIN_MS;
|
|
2389
2345
|
if (this.signatureDeadlineSeconds * 1e3 > maxAllowedSignatureMs) {
|
|
2390
|
-
throw new PTRedeemError(
|
|
2391
|
-
"INVALID_AMOUNT",
|
|
2392
|
-
`PTRedeemHandler config: signatureDeadlineSeconds (${this.signatureDeadlineSeconds}s) must be at most redeemLockDurationMs - safety margin = ${maxAllowedSignatureMs / 1e3}s (redeemLockDurationMs=${this.redeemLockDurationMs / 1e3}s, safety=${M11_SAFETY_MARGIN_MS / 1e3}s).`
|
|
2393
|
-
);
|
|
2346
|
+
throw new PTRedeemError("INVALID_AMOUNT", `PTRedeemHandler config: signatureDeadlineSeconds (${this.signatureDeadlineSeconds}s) must be at most redeemLockDurationMs - safety margin = ${maxAllowedSignatureMs / 1e3}s (redeemLockDurationMs=${this.redeemLockDurationMs / 1e3}s, safety=${M11_SAFETY_MARGIN_MS / 1e3}s).`);
|
|
2394
2347
|
}
|
|
2395
2348
|
if (config.redemptionService) {
|
|
2396
2349
|
this.redemptionService = config.redemptionService;
|
|
@@ -2398,46 +2351,31 @@ var PTRedeemHandler = class {
|
|
|
2398
2351
|
}
|
|
2399
2352
|
async handle(request) {
|
|
2400
2353
|
if ((0, import_viem8.getAddress)(request.authenticatedAddress) !== (0, import_viem8.getAddress)(request.userAddress)) {
|
|
2401
|
-
throw new PTRedeemError(
|
|
2402
|
-
"UNAUTHORIZED",
|
|
2403
|
-
`userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
|
|
2404
|
-
);
|
|
2354
|
+
throw new PTRedeemError("UNAUTHORIZED", `userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`);
|
|
2405
2355
|
}
|
|
2406
2356
|
if (request.amount <= 0n) {
|
|
2407
2357
|
throw new PTRedeemError("INVALID_AMOUNT", "redeem amount must be positive");
|
|
2408
2358
|
}
|
|
2409
2359
|
const pointTokenAddress = (0, import_viem8.getAddress)(request.pointTokenAddress);
|
|
2410
2360
|
if (!this.supportedTokens.has(pointTokenAddress)) {
|
|
2411
|
-
throw new PTRedeemError(
|
|
2412
|
-
"UNSUPPORTED_POINT_TOKEN",
|
|
2413
|
-
`redeem: pointTokenAddress ${pointTokenAddress} is not in the issuer's supported-token allowlist. Check IssuerApiHandlers.supportedTokens and PTRedeemHandler.config.supportedTokens point at the same set.`
|
|
2414
|
-
);
|
|
2361
|
+
throw new PTRedeemError("UNSUPPORTED_POINT_TOKEN", `redeem: pointTokenAddress ${pointTokenAddress} is not in the issuer's supported-token allowlist. Check IssuerApiHandlers.supportedTokens and PTRedeemHandler.config.supportedTokens point at the same set.`);
|
|
2415
2362
|
}
|
|
2416
2363
|
if (this.redemptionService) {
|
|
2417
2364
|
let decision;
|
|
2418
2365
|
try {
|
|
2419
|
-
decision = await this.redemptionService.evaluate(
|
|
2420
|
-
request.userAddress,
|
|
2421
|
-
request.amount,
|
|
2422
|
-
pointTokenAddress
|
|
2423
|
-
);
|
|
2366
|
+
decision = await this.redemptionService.evaluate(request.userAddress, request.amount, pointTokenAddress);
|
|
2424
2367
|
} catch (err) {
|
|
2425
2368
|
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
2426
2369
|
if (code === "POLICY_PROVIDER_UNAVAILABLE") {
|
|
2427
|
-
throw new PTRedeemError(
|
|
2428
|
-
"REDEMPTION_POLICY_UNAVAILABLE",
|
|
2429
|
-
"Redemption policy temporarily unavailable \u2014 please try again shortly."
|
|
2430
|
-
);
|
|
2370
|
+
throw new PTRedeemError("REDEMPTION_POLICY_UNAVAILABLE", "Redemption policy temporarily unavailable \u2014 please try again shortly.");
|
|
2431
2371
|
}
|
|
2432
2372
|
throw err;
|
|
2433
2373
|
}
|
|
2434
2374
|
if (!decision.allowed) {
|
|
2435
2375
|
const denial = decision.denial;
|
|
2436
|
-
throw new PTRedeemError(
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
{ policyDenialCode: denial.code }
|
|
2440
|
-
);
|
|
2376
|
+
throw new PTRedeemError("REDEMPTION_POLICY_DENIED", `redemption denied: ${denial.message}`, {
|
|
2377
|
+
policyDenialCode: denial.code
|
|
2378
|
+
});
|
|
2441
2379
|
}
|
|
2442
2380
|
}
|
|
2443
2381
|
let burnNonce;
|
|
@@ -2446,13 +2384,12 @@ var PTRedeemHandler = class {
|
|
|
2446
2384
|
address: pointTokenAddress,
|
|
2447
2385
|
abi: import_core7.POINT_TOKEN_ABI,
|
|
2448
2386
|
functionName: "burnRequestNonces",
|
|
2449
|
-
args: [
|
|
2387
|
+
args: [
|
|
2388
|
+
request.userAddress
|
|
2389
|
+
]
|
|
2450
2390
|
});
|
|
2451
2391
|
} catch (err) {
|
|
2452
|
-
throw new PTRedeemError(
|
|
2453
|
-
"NONCE_READ_FAILED",
|
|
2454
|
-
`failed to read burnRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
|
|
2455
|
-
);
|
|
2392
|
+
throw new PTRedeemError("NONCE_READ_FAILED", `failed to read burnRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`);
|
|
2456
2393
|
}
|
|
2457
2394
|
const nonceKey = `${(0, import_viem8.getAddress)(request.userAddress).toLowerCase()}:${pointTokenAddress.toLowerCase()}`;
|
|
2458
2395
|
let userNonces = this.inFlightNonces.get(nonceKey);
|
|
@@ -2461,10 +2398,7 @@ var PTRedeemHandler = class {
|
|
|
2461
2398
|
this.inFlightNonces.set(nonceKey, userNonces);
|
|
2462
2399
|
}
|
|
2463
2400
|
if (userNonces.has(burnNonce)) {
|
|
2464
|
-
throw new PTRedeemError(
|
|
2465
|
-
"NONCE_IN_FLIGHT",
|
|
2466
|
-
`A burn request for nonce ${burnNonce} is already in progress for ${request.userAddress} on ${pointTokenAddress}. Retry after the current request completes.`
|
|
2467
|
-
);
|
|
2401
|
+
throw new PTRedeemError("NONCE_IN_FLIGHT", `A burn request for nonce ${burnNonce} is already in progress for ${request.userAddress} on ${pointTokenAddress}. Retry after the current request completes.`);
|
|
2468
2402
|
}
|
|
2469
2403
|
userNonces.add(burnNonce);
|
|
2470
2404
|
try {
|
|
@@ -2478,12 +2412,8 @@ var PTRedeemHandler = class {
|
|
|
2478
2412
|
const referenceMs = this.now();
|
|
2479
2413
|
const projectedLockExpiresAtMs = referenceMs + this.redeemLockDurationMs;
|
|
2480
2414
|
const requestedDeadlineSec = Math.floor(referenceMs / 1e3) + this.signatureDeadlineSeconds;
|
|
2481
|
-
const lockBoundedDeadlineSec = Math.floor(
|
|
2482
|
-
|
|
2483
|
-
);
|
|
2484
|
-
const previewDeadline = BigInt(
|
|
2485
|
-
Math.min(requestedDeadlineSec, lockBoundedDeadlineSec)
|
|
2486
|
-
);
|
|
2415
|
+
const lockBoundedDeadlineSec = Math.floor((projectedLockExpiresAtMs - M11_SAFETY_MARGIN_MS) / 1e3);
|
|
2416
|
+
const previewDeadline = BigInt(Math.min(requestedDeadlineSec, lockBoundedDeadlineSec));
|
|
2487
2417
|
let fee;
|
|
2488
2418
|
if (request.feeAmount !== void 0) {
|
|
2489
2419
|
fee = request.feeAmount > 0n ? request.feeAmount : 0n;
|
|
@@ -2509,21 +2439,11 @@ var PTRedeemHandler = class {
|
|
|
2509
2439
|
}
|
|
2510
2440
|
const feeRecipient = request.feeRecipient ?? (request.chainId !== void 0 ? (0, import_core7.getContractAddresses)(request.chainId).pafiFeeRecipient : (0, import_core7.getContractAddresses)(this.chainId).pafiFeeRecipient);
|
|
2511
2441
|
if (fee > 0n && fee >= request.amount) {
|
|
2512
|
-
throw new PTRedeemError(
|
|
2513
|
-
"INVALID_AMOUNT",
|
|
2514
|
-
`fee (${fee}) must be strictly less than redeem amount (${request.amount})`
|
|
2515
|
-
);
|
|
2442
|
+
throw new PTRedeemError("INVALID_AMOUNT", `fee (${fee}) must be strictly less than redeem amount (${request.amount})`);
|
|
2516
2443
|
}
|
|
2517
|
-
const onChainBalance = await (0, import_core7.getPointTokenBalance)(
|
|
2518
|
-
this.provider,
|
|
2519
|
-
pointTokenAddress,
|
|
2520
|
-
request.userAddress
|
|
2521
|
-
);
|
|
2444
|
+
const onChainBalance = await (0, import_core7.getPointTokenBalance)(this.provider, pointTokenAddress, request.userAddress);
|
|
2522
2445
|
if (onChainBalance < request.amount) {
|
|
2523
|
-
throw new PTRedeemError(
|
|
2524
|
-
"INVALID_AMOUNT",
|
|
2525
|
-
`insufficient on-chain PT balance: have ${onChainBalance}, need ${request.amount}`
|
|
2526
|
-
);
|
|
2446
|
+
throw new PTRedeemError("INVALID_AMOUNT", `insufficient on-chain PT balance: have ${onChainBalance}, need ${request.amount}`);
|
|
2527
2447
|
}
|
|
2528
2448
|
const deadline = previewDeadline;
|
|
2529
2449
|
const domainName = await this.domainResolver.resolve(pointTokenAddress);
|
|
@@ -2545,17 +2465,9 @@ var PTRedeemHandler = class {
|
|
|
2545
2465
|
try {
|
|
2546
2466
|
sponsoredSig = (await (0, import_core7.signBurnRequest)(this.burnerSignerWallet, domain, sponsoredBurnRequest)).serialized;
|
|
2547
2467
|
} catch (err) {
|
|
2548
|
-
throw new PTRedeemError(
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
);
|
|
2552
|
-
}
|
|
2553
|
-
const sponsoredLockId = await this.ledger.reservePendingCredit(
|
|
2554
|
-
request.userAddress,
|
|
2555
|
-
sponsoredBurnAmount,
|
|
2556
|
-
this.redeemLockDurationMs,
|
|
2557
|
-
pointTokenAddress
|
|
2558
|
-
);
|
|
2468
|
+
throw new PTRedeemError("SIGNING_FAILED", `failed to sign sponsored BurnRequest: ${err instanceof Error ? err.message : String(err)}`);
|
|
2469
|
+
}
|
|
2470
|
+
const sponsoredLockId = await this.ledger.reservePendingCredit(request.userAddress, sponsoredBurnAmount, this.redeemLockDurationMs, pointTokenAddress);
|
|
2559
2471
|
try {
|
|
2560
2472
|
const sponsoredUserOp = await this.relayService.prepareBurn({
|
|
2561
2473
|
mode: "burnWithSig",
|
|
@@ -2581,17 +2493,9 @@ var PTRedeemHandler = class {
|
|
|
2581
2493
|
try {
|
|
2582
2494
|
fallbackSig = (await (0, import_core7.signBurnRequest)(this.burnerSignerWallet, domain, fallbackBurnRequest)).serialized;
|
|
2583
2495
|
} catch (err) {
|
|
2584
|
-
throw new PTRedeemError(
|
|
2585
|
-
"SIGNING_FAILED",
|
|
2586
|
-
`failed to sign fallback BurnRequest: ${err instanceof Error ? err.message : String(err)}`
|
|
2587
|
-
);
|
|
2496
|
+
throw new PTRedeemError("SIGNING_FAILED", `failed to sign fallback BurnRequest: ${err instanceof Error ? err.message : String(err)}`);
|
|
2588
2497
|
}
|
|
2589
|
-
const fallbackLockId = await this.ledger.reservePendingCredit(
|
|
2590
|
-
request.userAddress,
|
|
2591
|
-
request.amount,
|
|
2592
|
-
this.redeemLockDurationMs,
|
|
2593
|
-
pointTokenAddress
|
|
2594
|
-
);
|
|
2498
|
+
const fallbackLockId = await this.ledger.reservePendingCredit(request.userAddress, request.amount, this.redeemLockDurationMs, pointTokenAddress);
|
|
2595
2499
|
let fallbackUserOp;
|
|
2596
2500
|
try {
|
|
2597
2501
|
fallbackUserOp = await this.relayService.prepareBurn({
|
|
@@ -2649,15 +2553,11 @@ var PTRedeemHandler = class {
|
|
|
2649
2553
|
var DEFAULT_STATUS_CONFIRMATIONS = 3;
|
|
2650
2554
|
async function isReceiptPastConfirmations(receipt, provider, confirmations, onWarning, handlerName) {
|
|
2651
2555
|
if (!provider) {
|
|
2652
|
-
onWarning?.(
|
|
2653
|
-
`${handlerName}: provider missing \u2014 cannot enforce confirmation depth; deferring receipt fallback to on-chain indexer.`
|
|
2654
|
-
);
|
|
2556
|
+
onWarning?.(`${handlerName}: provider missing \u2014 cannot enforce confirmation depth; deferring receipt fallback to on-chain indexer.`);
|
|
2655
2557
|
return false;
|
|
2656
2558
|
}
|
|
2657
2559
|
if (!receipt.blockNumber) {
|
|
2658
|
-
onWarning?.(
|
|
2659
|
-
`${handlerName}: receipt has no blockNumber \u2014 cannot enforce confirmation depth; deferring to indexer.`
|
|
2660
|
-
);
|
|
2560
|
+
onWarning?.(`${handlerName}: receipt has no blockNumber \u2014 cannot enforce confirmation depth; deferring to indexer.`);
|
|
2661
2561
|
return false;
|
|
2662
2562
|
}
|
|
2663
2563
|
const requiredConfs = BigInt(confirmations ?? DEFAULT_STATUS_CONFIRMATIONS);
|
|
@@ -2665,18 +2565,14 @@ async function isReceiptPastConfirmations(receipt, provider, confirmations, onWa
|
|
|
2665
2565
|
try {
|
|
2666
2566
|
receiptBlock = BigInt(receipt.blockNumber);
|
|
2667
2567
|
} catch {
|
|
2668
|
-
onWarning?.(
|
|
2669
|
-
`${handlerName}: malformed receipt blockNumber (${receipt.blockNumber}) \u2014 deferring to indexer.`
|
|
2670
|
-
);
|
|
2568
|
+
onWarning?.(`${handlerName}: malformed receipt blockNumber (${receipt.blockNumber}) \u2014 deferring to indexer.`);
|
|
2671
2569
|
return false;
|
|
2672
2570
|
}
|
|
2673
2571
|
let head;
|
|
2674
2572
|
try {
|
|
2675
2573
|
head = await provider.getBlockNumber();
|
|
2676
2574
|
} catch (err) {
|
|
2677
|
-
onWarning?.(
|
|
2678
|
-
`${handlerName}: getBlockNumber failed (${err instanceof Error ? err.message : String(err)}) \u2014 deferring to indexer.`
|
|
2679
|
-
);
|
|
2575
|
+
onWarning?.(`${handlerName}: getBlockNumber failed (${err instanceof Error ? err.message : String(err)}) \u2014 deferring to indexer.`);
|
|
2680
2576
|
return false;
|
|
2681
2577
|
}
|
|
2682
2578
|
const depth = head - receiptBlock;
|
|
@@ -2685,7 +2581,11 @@ async function isReceiptPastConfirmations(receipt, provider, confirmations, onWa
|
|
|
2685
2581
|
}
|
|
2686
2582
|
return true;
|
|
2687
2583
|
}
|
|
2688
|
-
|
|
2584
|
+
__name(isReceiptPastConfirmations, "isReceiptPastConfirmations");
|
|
2585
|
+
var LockNotFoundError = class LockNotFoundError2 extends import_core.PafiSdkError {
|
|
2586
|
+
static {
|
|
2587
|
+
__name(this, "LockNotFoundError");
|
|
2588
|
+
}
|
|
2689
2589
|
code = "LOCK_NOT_FOUND";
|
|
2690
2590
|
httpStatus = "not_found";
|
|
2691
2591
|
constructor() {
|
|
@@ -2694,14 +2594,9 @@ var LockNotFoundError = class extends import_core.PafiSdkError {
|
|
|
2694
2594
|
};
|
|
2695
2595
|
async function handleClaimStatus(params) {
|
|
2696
2596
|
if (!params.ledger.getMintLock) {
|
|
2697
|
-
throw new Error(
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
}
|
|
2701
|
-
const lock = await params.ledger.getMintLock(
|
|
2702
|
-
params.lockId,
|
|
2703
|
-
params.userAddress
|
|
2704
|
-
);
|
|
2597
|
+
throw new Error("handleClaimStatus: ledger does not implement `getMintLock` \u2014 implement the optional method on `IPointLedger` or write a custom status handler.");
|
|
2598
|
+
}
|
|
2599
|
+
const lock = await params.ledger.getMintLock(params.lockId, params.userAddress);
|
|
2705
2600
|
if (!lock || lock.userAddress.toLowerCase() !== params.userAddress.toLowerCase()) {
|
|
2706
2601
|
throw new LockNotFoundError();
|
|
2707
2602
|
}
|
|
@@ -2709,17 +2604,9 @@ async function handleClaimStatus(params) {
|
|
|
2709
2604
|
let txHash = lock.txHash ?? null;
|
|
2710
2605
|
if (status === "PENDING" && lock.userOpHash && params.pafiBackendClient) {
|
|
2711
2606
|
try {
|
|
2712
|
-
const receipt = await params.pafiBackendClient.getUserOpReceipt(
|
|
2713
|
-
lock.userOpHash
|
|
2714
|
-
);
|
|
2607
|
+
const receipt = await params.pafiBackendClient.getUserOpReceipt(lock.userOpHash);
|
|
2715
2608
|
if (receipt) {
|
|
2716
|
-
const passesConfirmationDepth = await isReceiptPastConfirmations(
|
|
2717
|
-
receipt,
|
|
2718
|
-
params.provider,
|
|
2719
|
-
params.confirmations,
|
|
2720
|
-
params.onWarning,
|
|
2721
|
-
"handleClaimStatus"
|
|
2722
|
-
);
|
|
2609
|
+
const passesConfirmationDepth = await isReceiptPastConfirmations(receipt, params.provider, params.confirmations, params.onWarning, "handleClaimStatus");
|
|
2723
2610
|
if (!passesConfirmationDepth) {
|
|
2724
2611
|
return {
|
|
2725
2612
|
lockId: lock.lockId,
|
|
@@ -2732,46 +2619,31 @@ async function handleClaimStatus(params) {
|
|
|
2732
2619
|
}
|
|
2733
2620
|
if (receipt.success && receipt.txHash) {
|
|
2734
2621
|
if (!lock.tokenAddress) {
|
|
2735
|
-
params.onWarning?.(
|
|
2736
|
-
`handleClaimStatus: lock ${lock.lockId} has no tokenAddress; falling back to status-only flip \u2014 atomic debit+flip cannot run on a legacy single-token row. Migrate the ledger to the multi-token schema.`
|
|
2737
|
-
);
|
|
2622
|
+
params.onWarning?.(`handleClaimStatus: lock ${lock.lockId} has no tokenAddress; falling back to status-only flip \u2014 atomic debit+flip cannot run on a legacy single-token row. Migrate the ledger to the multi-token schema.`);
|
|
2738
2623
|
await params.ledger.updateMintStatus(lock.lockId, "MINTED", receipt.txHash).catch((err) => {
|
|
2739
|
-
params.onWarning?.(
|
|
2740
|
-
`handleClaimStatus: ledger updateMintStatus failed for lock ${lock.lockId}: ${err}`
|
|
2741
|
-
);
|
|
2624
|
+
params.onWarning?.(`handleClaimStatus: ledger updateMintStatus failed for lock ${lock.lockId}: ${err}`);
|
|
2742
2625
|
});
|
|
2743
2626
|
status = "MINTED";
|
|
2744
2627
|
txHash = receipt.txHash;
|
|
2745
2628
|
} else {
|
|
2746
2629
|
try {
|
|
2747
|
-
await params.ledger.deductBalance(
|
|
2748
|
-
lock.userAddress,
|
|
2749
|
-
lock.amount,
|
|
2750
|
-
receipt.txHash,
|
|
2751
|
-
lock.tokenAddress
|
|
2752
|
-
);
|
|
2630
|
+
await params.ledger.deductBalance(lock.userAddress, lock.amount, receipt.txHash, lock.tokenAddress);
|
|
2753
2631
|
status = "MINTED";
|
|
2754
2632
|
txHash = receipt.txHash;
|
|
2755
2633
|
} catch (deductErr) {
|
|
2756
|
-
params.onWarning?.(
|
|
2757
|
-
`handleClaimStatus: deductBalance failed for lock ${lock.lockId}: ${deductErr}`
|
|
2758
|
-
);
|
|
2634
|
+
params.onWarning?.(`handleClaimStatus: deductBalance failed for lock ${lock.lockId}: ${deductErr}`);
|
|
2759
2635
|
}
|
|
2760
2636
|
}
|
|
2761
2637
|
} else {
|
|
2762
2638
|
await params.ledger.updateMintStatus(lock.lockId, "FAILED", receipt.txHash).catch((err) => {
|
|
2763
|
-
params.onWarning?.(
|
|
2764
|
-
`handleClaimStatus: ledger updateMintStatus failed for lock ${lock.lockId}: ${err}`
|
|
2765
|
-
);
|
|
2639
|
+
params.onWarning?.(`handleClaimStatus: ledger updateMintStatus failed for lock ${lock.lockId}: ${err}`);
|
|
2766
2640
|
});
|
|
2767
2641
|
status = "FAILED";
|
|
2768
2642
|
txHash = receipt.txHash;
|
|
2769
2643
|
}
|
|
2770
2644
|
}
|
|
2771
2645
|
} catch (err) {
|
|
2772
|
-
params.onWarning?.(
|
|
2773
|
-
`handleClaimStatus: bundler-receipt fallback failed for lock ${lock.lockId}: ${err}`
|
|
2774
|
-
);
|
|
2646
|
+
params.onWarning?.(`handleClaimStatus: bundler-receipt fallback failed for lock ${lock.lockId}: ${err}`);
|
|
2775
2647
|
}
|
|
2776
2648
|
}
|
|
2777
2649
|
return {
|
|
@@ -2783,16 +2655,12 @@ async function handleClaimStatus(params) {
|
|
|
2783
2655
|
expiresAt: new Date(lock.expiresAt).toISOString()
|
|
2784
2656
|
};
|
|
2785
2657
|
}
|
|
2658
|
+
__name(handleClaimStatus, "handleClaimStatus");
|
|
2786
2659
|
async function handleRedeemStatus(params) {
|
|
2787
2660
|
if (!params.ledger.getPendingCredit) {
|
|
2788
|
-
throw new Error(
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
}
|
|
2792
|
-
const credit = await params.ledger.getPendingCredit(
|
|
2793
|
-
params.lockId,
|
|
2794
|
-
params.userAddress
|
|
2795
|
-
);
|
|
2661
|
+
throw new Error("handleRedeemStatus: ledger does not implement `getPendingCredit` \u2014 implement the optional method on `IPointLedger` or write a custom status handler.");
|
|
2662
|
+
}
|
|
2663
|
+
const credit = await params.ledger.getPendingCredit(params.lockId, params.userAddress);
|
|
2796
2664
|
if (!credit || credit.userAddress.toLowerCase() !== params.userAddress.toLowerCase()) {
|
|
2797
2665
|
throw new LockNotFoundError();
|
|
2798
2666
|
}
|
|
@@ -2800,33 +2668,21 @@ async function handleRedeemStatus(params) {
|
|
|
2800
2668
|
let txHash = credit.txHash ?? null;
|
|
2801
2669
|
if (status === "PENDING" && credit.userOpHash && params.pafiBackendClient) {
|
|
2802
2670
|
try {
|
|
2803
|
-
const receipt = await params.pafiBackendClient.getUserOpReceipt(
|
|
2804
|
-
credit.userOpHash
|
|
2805
|
-
);
|
|
2671
|
+
const receipt = await params.pafiBackendClient.getUserOpReceipt(credit.userOpHash);
|
|
2806
2672
|
if (receipt && receipt.success) {
|
|
2807
|
-
const passesConfirmationDepth = await isReceiptPastConfirmations(
|
|
2808
|
-
receipt,
|
|
2809
|
-
params.provider,
|
|
2810
|
-
params.confirmations,
|
|
2811
|
-
params.onWarning,
|
|
2812
|
-
"handleRedeemStatus"
|
|
2813
|
-
);
|
|
2673
|
+
const passesConfirmationDepth = await isReceiptPastConfirmations(receipt, params.provider, params.confirmations, params.onWarning, "handleRedeemStatus");
|
|
2814
2674
|
if (passesConfirmationDepth) {
|
|
2815
2675
|
status = "RESOLVED";
|
|
2816
2676
|
txHash = receipt.txHash;
|
|
2817
2677
|
if (params.ledger.resolveCreditByBurnTx) {
|
|
2818
2678
|
await params.ledger.resolveCreditByBurnTx(credit.lockId, receipt.txHash).catch((err) => {
|
|
2819
|
-
params.onWarning?.(
|
|
2820
|
-
`handleRedeemStatus: resolveCreditByBurnTx failed for lock ${credit.lockId}: ${err}`
|
|
2821
|
-
);
|
|
2679
|
+
params.onWarning?.(`handleRedeemStatus: resolveCreditByBurnTx failed for lock ${credit.lockId}: ${err}`);
|
|
2822
2680
|
});
|
|
2823
2681
|
}
|
|
2824
2682
|
}
|
|
2825
2683
|
}
|
|
2826
2684
|
} catch (err) {
|
|
2827
|
-
params.onWarning?.(
|
|
2828
|
-
`handleRedeemStatus: bundler-receipt fallback failed for lock ${credit.lockId}: ${err}`
|
|
2829
|
-
);
|
|
2685
|
+
params.onWarning?.(`handleRedeemStatus: bundler-receipt fallback failed for lock ${credit.lockId}: ${err}`);
|
|
2830
2686
|
}
|
|
2831
2687
|
}
|
|
2832
2688
|
return {
|
|
@@ -2839,6 +2695,7 @@ async function handleRedeemStatus(params) {
|
|
|
2839
2695
|
resolvedAt: credit.resolvedAt ? new Date(credit.resolvedAt).toISOString() : null
|
|
2840
2696
|
};
|
|
2841
2697
|
}
|
|
2698
|
+
__name(handleRedeemStatus, "handleRedeemStatus");
|
|
2842
2699
|
|
|
2843
2700
|
// src/api/mobileHandlers.ts
|
|
2844
2701
|
var import_viem9 = require("viem");
|
|
@@ -2849,46 +2706,41 @@ var import_core8 = require("@pafi-dev/core");
|
|
|
2849
2706
|
function serializeEntryToJsonRpc(entry, signature, variant = "sponsored") {
|
|
2850
2707
|
if (variant === "fallback") {
|
|
2851
2708
|
if (!entry.fallback) {
|
|
2852
|
-
throw new Error(
|
|
2853
|
-
"serializeEntryToJsonRpc: variant=fallback requested but the stored entry has no `fallback` branch \u2014 caller should resubmit with variant='sponsored' or re-prepare with a fee configured."
|
|
2854
|
-
);
|
|
2709
|
+
throw new Error("serializeEntryToJsonRpc: variant=fallback requested but the stored entry has no `fallback` branch \u2014 caller should resubmit with variant='sponsored' or re-prepare with a fee configured.");
|
|
2855
2710
|
}
|
|
2856
|
-
return (0, import_core8.serializeUserOpToJsonRpc)(
|
|
2857
|
-
{
|
|
2858
|
-
sender: entry.sender,
|
|
2859
|
-
nonce: BigInt(entry.nonce),
|
|
2860
|
-
callData: entry.fallback.callData,
|
|
2861
|
-
callGasLimit: BigInt(entry.fallback.callGasLimit),
|
|
2862
|
-
verificationGasLimit: BigInt(entry.fallback.verificationGasLimit),
|
|
2863
|
-
preVerificationGas: BigInt(entry.fallback.preVerificationGas),
|
|
2864
|
-
maxFeePerGas: BigInt(entry.maxFeePerGas),
|
|
2865
|
-
maxPriorityFeePerGas: BigInt(entry.maxPriorityFeePerGas)
|
|
2866
|
-
// intentionally no paymaster — user pays ETH gas
|
|
2867
|
-
},
|
|
2868
|
-
signature
|
|
2869
|
-
);
|
|
2870
|
-
}
|
|
2871
|
-
return (0, import_core8.serializeUserOpToJsonRpc)(
|
|
2872
|
-
{
|
|
2711
|
+
return (0, import_core8.serializeUserOpToJsonRpc)({
|
|
2873
2712
|
sender: entry.sender,
|
|
2874
2713
|
nonce: BigInt(entry.nonce),
|
|
2875
|
-
callData: entry.callData,
|
|
2876
|
-
callGasLimit: BigInt(entry.callGasLimit),
|
|
2877
|
-
verificationGasLimit: BigInt(entry.verificationGasLimit),
|
|
2878
|
-
preVerificationGas: BigInt(entry.preVerificationGas),
|
|
2714
|
+
callData: entry.fallback.callData,
|
|
2715
|
+
callGasLimit: BigInt(entry.fallback.callGasLimit),
|
|
2716
|
+
verificationGasLimit: BigInt(entry.fallback.verificationGasLimit),
|
|
2717
|
+
preVerificationGas: BigInt(entry.fallback.preVerificationGas),
|
|
2879
2718
|
maxFeePerGas: BigInt(entry.maxFeePerGas),
|
|
2880
|
-
maxPriorityFeePerGas: BigInt(entry.maxPriorityFeePerGas)
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2719
|
+
maxPriorityFeePerGas: BigInt(entry.maxPriorityFeePerGas)
|
|
2720
|
+
}, signature);
|
|
2721
|
+
}
|
|
2722
|
+
return (0, import_core8.serializeUserOpToJsonRpc)({
|
|
2723
|
+
sender: entry.sender,
|
|
2724
|
+
nonce: BigInt(entry.nonce),
|
|
2725
|
+
callData: entry.callData,
|
|
2726
|
+
callGasLimit: BigInt(entry.callGasLimit),
|
|
2727
|
+
verificationGasLimit: BigInt(entry.verificationGasLimit),
|
|
2728
|
+
preVerificationGas: BigInt(entry.preVerificationGas),
|
|
2729
|
+
maxFeePerGas: BigInt(entry.maxFeePerGas),
|
|
2730
|
+
maxPriorityFeePerGas: BigInt(entry.maxPriorityFeePerGas),
|
|
2731
|
+
paymaster: entry.paymaster,
|
|
2732
|
+
paymasterVerificationGasLimit: entry.paymasterVerificationGasLimit != null ? BigInt(entry.paymasterVerificationGasLimit) : void 0,
|
|
2733
|
+
paymasterPostOpGasLimit: entry.paymasterPostOpGasLimit != null ? BigInt(entry.paymasterPostOpGasLimit) : void 0,
|
|
2734
|
+
paymasterData: entry.paymasterData
|
|
2735
|
+
}, signature);
|
|
2888
2736
|
}
|
|
2737
|
+
__name(serializeEntryToJsonRpc, "serializeEntryToJsonRpc");
|
|
2889
2738
|
|
|
2890
2739
|
// src/userop-store/memoryStore.ts
|
|
2891
2740
|
var MemoryPendingUserOpStore = class {
|
|
2741
|
+
static {
|
|
2742
|
+
__name(this, "MemoryPendingUserOpStore");
|
|
2743
|
+
}
|
|
2892
2744
|
entries = /* @__PURE__ */ new Map();
|
|
2893
2745
|
now;
|
|
2894
2746
|
constructor(now = () => Date.now()) {
|
|
@@ -2916,25 +2768,6 @@ var MemoryPendingUserOpStore = class {
|
|
|
2916
2768
|
|
|
2917
2769
|
// src/userop-store/prepareUserOp.ts
|
|
2918
2770
|
var import_core9 = require("@pafi-dev/core");
|
|
2919
|
-
function serializeUserOpTypedData(td) {
|
|
2920
|
-
return {
|
|
2921
|
-
domain: td.domain,
|
|
2922
|
-
types: td.types,
|
|
2923
|
-
primaryType: td.primaryType,
|
|
2924
|
-
message: {
|
|
2925
|
-
sender: td.message.sender,
|
|
2926
|
-
nonce: `0x${td.message.nonce.toString(16)}`,
|
|
2927
|
-
initCode: td.message.initCode,
|
|
2928
|
-
callData: td.message.callData,
|
|
2929
|
-
accountGasLimits: td.message.accountGasLimits,
|
|
2930
|
-
preVerificationGas: `0x${td.message.preVerificationGas.toString(
|
|
2931
|
-
16
|
|
2932
|
-
)}`,
|
|
2933
|
-
gasFees: td.message.gasFees,
|
|
2934
|
-
paymasterAndData: td.message.paymasterAndData
|
|
2935
|
-
}
|
|
2936
|
-
};
|
|
2937
|
-
}
|
|
2938
2771
|
function mergePaymasterFields(userOp, paymasterFields) {
|
|
2939
2772
|
if (!paymasterFields) return userOp;
|
|
2940
2773
|
const merged = {
|
|
@@ -2945,36 +2778,26 @@ function mergePaymasterFields(userOp, paymasterFields) {
|
|
|
2945
2778
|
}
|
|
2946
2779
|
return merged;
|
|
2947
2780
|
}
|
|
2781
|
+
__name(mergePaymasterFields, "mergePaymasterFields");
|
|
2948
2782
|
function applyPaymasterGasEstimates(partialUserOp, paymasterFields, chainId) {
|
|
2949
|
-
const userOp = mergePaymasterFields(
|
|
2950
|
-
partialUserOp,
|
|
2951
|
-
paymasterFields
|
|
2952
|
-
);
|
|
2783
|
+
const userOp = mergePaymasterFields(partialUserOp, paymasterFields);
|
|
2953
2784
|
return {
|
|
2954
2785
|
userOp,
|
|
2955
|
-
userOpHash: (0, import_core9.computeUserOpHash)(userOp, chainId)
|
|
2956
|
-
typedData: serializeUserOpTypedData((0, import_core9.buildUserOpTypedData)(userOp, chainId))
|
|
2786
|
+
userOpHash: (0, import_core9.computeUserOpHash)(userOp, chainId)
|
|
2957
2787
|
};
|
|
2958
2788
|
}
|
|
2789
|
+
__name(applyPaymasterGasEstimates, "applyPaymasterGasEstimates");
|
|
2959
2790
|
async function prepareMobileUserOp(params) {
|
|
2960
|
-
const sponsored = applyPaymasterGasEstimates(
|
|
2961
|
-
params.partialUserOp,
|
|
2962
|
-
params.paymasterFields,
|
|
2963
|
-
params.chainId
|
|
2964
|
-
);
|
|
2791
|
+
const sponsored = applyPaymasterGasEstimates(params.partialUserOp, params.paymasterFields, params.chainId);
|
|
2965
2792
|
const { userOp, userOpHash } = sponsored;
|
|
2966
2793
|
let fallback;
|
|
2967
2794
|
let fallbackEntry;
|
|
2968
2795
|
if (params.partialUserOpFallback) {
|
|
2969
|
-
fallback = applyPaymasterGasEstimates(
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
},
|
|
2975
|
-
void 0,
|
|
2976
|
-
params.chainId
|
|
2977
|
-
);
|
|
2796
|
+
fallback = applyPaymasterGasEstimates({
|
|
2797
|
+
...params.partialUserOpFallback,
|
|
2798
|
+
maxFeePerGas: userOp.maxFeePerGas,
|
|
2799
|
+
maxPriorityFeePerGas: userOp.maxPriorityFeePerGas
|
|
2800
|
+
}, void 0, params.chainId);
|
|
2978
2801
|
fallbackEntry = {
|
|
2979
2802
|
callData: fallback.userOp.callData,
|
|
2980
2803
|
callGasLimit: fallback.userOp.callGasLimit.toString(),
|
|
@@ -3011,23 +2834,24 @@ async function prepareMobileUserOp(params) {
|
|
|
3011
2834
|
entry
|
|
3012
2835
|
};
|
|
3013
2836
|
}
|
|
2837
|
+
__name(prepareMobileUserOp, "prepareMobileUserOp");
|
|
3014
2838
|
|
|
3015
2839
|
// src/pafi-backend/types.ts
|
|
3016
2840
|
var PafiBackendError = class extends Error {
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
this.code = code;
|
|
3020
|
-
this.httpStatus = httpStatus;
|
|
3021
|
-
this.details = details;
|
|
3022
|
-
this.name = "PafiBackendError";
|
|
3023
|
-
if (opts?.retryAfter !== void 0) this.retryAfter = opts.retryAfter;
|
|
3024
|
-
if (opts?.safeToRetry !== void 0) this.serverSafeToRetry = opts.safeToRetry;
|
|
2841
|
+
static {
|
|
2842
|
+
__name(this, "PafiBackendError");
|
|
3025
2843
|
}
|
|
3026
2844
|
code;
|
|
3027
2845
|
httpStatus;
|
|
3028
2846
|
details;
|
|
3029
2847
|
retryAfter;
|
|
3030
2848
|
serverSafeToRetry;
|
|
2849
|
+
constructor(code, message, httpStatus, details, opts) {
|
|
2850
|
+
super(message), this.code = code, this.httpStatus = httpStatus, this.details = details;
|
|
2851
|
+
this.name = "PafiBackendError";
|
|
2852
|
+
if (opts?.retryAfter !== void 0) this.retryAfter = opts.retryAfter;
|
|
2853
|
+
if (opts?.safeToRetry !== void 0) this.serverSafeToRetry = opts.safeToRetry;
|
|
2854
|
+
}
|
|
3031
2855
|
get safeToRetry() {
|
|
3032
2856
|
if (this.serverSafeToRetry !== void 0) return this.serverSafeToRetry;
|
|
3033
2857
|
switch (this.code) {
|
|
@@ -3049,15 +2873,19 @@ var PafiBackendError = class extends Error {
|
|
|
3049
2873
|
|
|
3050
2874
|
// src/pafi-backend/helpers.ts
|
|
3051
2875
|
var BundlerNotConfiguredError = class extends import_core.PafiSdkError {
|
|
2876
|
+
static {
|
|
2877
|
+
__name(this, "BundlerNotConfiguredError");
|
|
2878
|
+
}
|
|
3052
2879
|
code = "BUNDLER_NOT_CONFIGURED";
|
|
3053
2880
|
httpStatus = "service_unavailable";
|
|
3054
2881
|
constructor() {
|
|
3055
|
-
super(
|
|
3056
|
-
"PAFI backend client not configured \u2014 set PAFI_BACKEND_URL, PAFI_ISSUER_ID, PAFI_API_KEY to enable mobile submit."
|
|
3057
|
-
);
|
|
2882
|
+
super("PAFI backend client not configured \u2014 set PAFI_BACKEND_URL, PAFI_ISSUER_ID, PAFI_API_KEY to enable mobile submit.");
|
|
3058
2883
|
}
|
|
3059
2884
|
};
|
|
3060
2885
|
var BundlerRejectedError = class extends import_core.PafiSdkError {
|
|
2886
|
+
static {
|
|
2887
|
+
__name(this, "BundlerRejectedError");
|
|
2888
|
+
}
|
|
3061
2889
|
code = "BUNDLER_REJECTED";
|
|
3062
2890
|
httpStatus = "unprocessable";
|
|
3063
2891
|
cause;
|
|
@@ -3079,7 +2907,9 @@ async function requestPaymaster(params) {
|
|
|
3079
2907
|
function: fn,
|
|
3080
2908
|
pointToken: params.pointTokenAddress
|
|
3081
2909
|
},
|
|
3082
|
-
...params.eip7702Auth ? {
|
|
2910
|
+
...params.eip7702Auth ? {
|
|
2911
|
+
eip7702Auth: params.eip7702Auth
|
|
2912
|
+
} : {}
|
|
3083
2913
|
});
|
|
3084
2914
|
} catch (err) {
|
|
3085
2915
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -3090,6 +2920,7 @@ async function requestPaymaster(params) {
|
|
|
3090
2920
|
throw err;
|
|
3091
2921
|
}
|
|
3092
2922
|
}
|
|
2923
|
+
__name(requestPaymaster, "requestPaymaster");
|
|
3093
2924
|
function isTransientPaymasterError(code) {
|
|
3094
2925
|
switch (code) {
|
|
3095
2926
|
case "NETWORK_ERROR":
|
|
@@ -3102,6 +2933,7 @@ function isTransientPaymasterError(code) {
|
|
|
3102
2933
|
return false;
|
|
3103
2934
|
}
|
|
3104
2935
|
}
|
|
2936
|
+
__name(isTransientPaymasterError, "isTransientPaymasterError");
|
|
3105
2937
|
function defaultFunctionForScenario(scenario) {
|
|
3106
2938
|
switch (scenario) {
|
|
3107
2939
|
case "mint":
|
|
@@ -3116,6 +2948,7 @@ function defaultFunctionForScenario(scenario) {
|
|
|
3116
2948
|
return scenario;
|
|
3117
2949
|
}
|
|
3118
2950
|
}
|
|
2951
|
+
__name(defaultFunctionForScenario, "defaultFunctionForScenario");
|
|
3119
2952
|
async function relayUserOp(params) {
|
|
3120
2953
|
if (!params.client) {
|
|
3121
2954
|
throw new BundlerNotConfiguredError();
|
|
@@ -3126,36 +2959,43 @@ async function relayUserOp(params) {
|
|
|
3126
2959
|
entryPoint: params.entryPoint,
|
|
3127
2960
|
eip7702Auth: params.eip7702Auth
|
|
3128
2961
|
});
|
|
3129
|
-
return {
|
|
2962
|
+
return {
|
|
2963
|
+
userOpHash: result.userOpHash
|
|
2964
|
+
};
|
|
3130
2965
|
} catch (err) {
|
|
3131
2966
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3132
2967
|
throw new BundlerRejectedError(msg, err);
|
|
3133
2968
|
}
|
|
3134
2969
|
}
|
|
2970
|
+
__name(relayUserOp, "relayUserOp");
|
|
3135
2971
|
|
|
3136
2972
|
// src/api/mobileHandlers.ts
|
|
3137
2973
|
var PendingUserOpNotFoundError = class extends import_core.PafiSdkError {
|
|
2974
|
+
static {
|
|
2975
|
+
__name(this, "PendingUserOpNotFoundError");
|
|
2976
|
+
}
|
|
3138
2977
|
code = "PENDING_USEROP_NOT_FOUND";
|
|
3139
2978
|
httpStatus = "not_found";
|
|
3140
2979
|
constructor(lockId) {
|
|
3141
|
-
super(
|
|
3142
|
-
`No pending UserOp found for lockId ${lockId} \u2014 it may have expired or already been submitted.`
|
|
3143
|
-
);
|
|
2980
|
+
super(`No pending UserOp found for lockId ${lockId} \u2014 it may have expired or already been submitted.`);
|
|
3144
2981
|
}
|
|
3145
2982
|
};
|
|
3146
2983
|
var PendingUserOpForbiddenError = class extends import_core.PafiSdkError {
|
|
2984
|
+
static {
|
|
2985
|
+
__name(this, "PendingUserOpForbiddenError");
|
|
2986
|
+
}
|
|
3147
2987
|
code = "PENDING_USEROP_FORBIDDEN";
|
|
3148
2988
|
httpStatus = "forbidden";
|
|
3149
2989
|
constructor(lockId) {
|
|
3150
|
-
super(
|
|
3151
|
-
`Pending UserOp ${lockId} does not belong to the authenticated user.`
|
|
3152
|
-
);
|
|
2990
|
+
super(`Pending UserOp ${lockId} does not belong to the authenticated user.`);
|
|
3153
2991
|
}
|
|
3154
2992
|
};
|
|
3155
2993
|
async function handleMobilePrepare(params) {
|
|
3156
2994
|
const [fees, userCode] = await Promise.all([
|
|
3157
2995
|
params.provider.estimateFeesPerGas(),
|
|
3158
|
-
params.provider.getCode({
|
|
2996
|
+
params.provider.getCode({
|
|
2997
|
+
address: params.userAddress
|
|
2998
|
+
})
|
|
3159
2999
|
]);
|
|
3160
3000
|
const needsDelegation = !params.eip7702Auth && (0, import_core10.parseEip7702DelegatedAddress)(userCode) === null;
|
|
3161
3001
|
const sponsoredOp = {
|
|
@@ -3189,6 +3029,7 @@ async function handleMobilePrepare(params) {
|
|
|
3189
3029
|
needsDelegation
|
|
3190
3030
|
};
|
|
3191
3031
|
}
|
|
3032
|
+
__name(handleMobilePrepare, "handleMobilePrepare");
|
|
3192
3033
|
async function handleMobileSubmit(params) {
|
|
3193
3034
|
const entry = await params.store.get(params.lockId);
|
|
3194
3035
|
if (!entry) {
|
|
@@ -3208,8 +3049,11 @@ async function handleMobileSubmit(params) {
|
|
|
3208
3049
|
const targetLockId = variant === "fallback" && entry.fallback?.lockId ? entry.fallback.lockId : params.lockId;
|
|
3209
3050
|
await params.bindUserOpHash(targetLockId, result.userOpHash);
|
|
3210
3051
|
await params.store.delete(params.lockId);
|
|
3211
|
-
return {
|
|
3052
|
+
return {
|
|
3053
|
+
userOpHash: result.userOpHash
|
|
3054
|
+
};
|
|
3212
3055
|
}
|
|
3056
|
+
__name(handleMobileSubmit, "handleMobileSubmit");
|
|
3213
3057
|
|
|
3214
3058
|
// src/api/handlers/ptClaimHandler.ts
|
|
3215
3059
|
var import_viem10 = require("viem");
|
|
@@ -3217,6 +3061,9 @@ var import_core11 = require("@pafi-dev/core");
|
|
|
3217
3061
|
|
|
3218
3062
|
// src/issuer-state/types.ts
|
|
3219
3063
|
var IssuerStateError = class extends import_core.PafiSdkError {
|
|
3064
|
+
static {
|
|
3065
|
+
__name(this, "IssuerStateError");
|
|
3066
|
+
}
|
|
3220
3067
|
httpStatus = "unprocessable";
|
|
3221
3068
|
code;
|
|
3222
3069
|
details;
|
|
@@ -3231,6 +3078,9 @@ var IssuerStateError = class extends import_core.PafiSdkError {
|
|
|
3231
3078
|
|
|
3232
3079
|
// src/api/handlers/ptClaimHandler.ts
|
|
3233
3080
|
var PTClaimError = class extends import_core.PafiSdkError {
|
|
3081
|
+
static {
|
|
3082
|
+
__name(this, "PTClaimError");
|
|
3083
|
+
}
|
|
3234
3084
|
httpStatus = "unprocessable";
|
|
3235
3085
|
code;
|
|
3236
3086
|
details;
|
|
@@ -3245,32 +3095,29 @@ function isNoWrapper2(address) {
|
|
|
3245
3095
|
const lower = address.toLowerCase();
|
|
3246
3096
|
return lower === "0x0000000000000000000000000000000000000000" || lower === "0x000000000000000000000000000000000000dead";
|
|
3247
3097
|
}
|
|
3098
|
+
__name(isNoWrapper2, "isNoWrapper");
|
|
3248
3099
|
var DEFAULT_LOCK_MS = 15 * 60 * 1e3;
|
|
3249
3100
|
var M11_SAFETY_MARGIN_MS2 = 30 * 1e3;
|
|
3250
3101
|
var DEFAULT_SIG_DEADLINE_SEC2 = (DEFAULT_LOCK_MS - M11_SAFETY_MARGIN_MS2) / 1e3;
|
|
3251
3102
|
var PTClaimHandler = class {
|
|
3103
|
+
static {
|
|
3104
|
+
__name(this, "PTClaimHandler");
|
|
3105
|
+
}
|
|
3252
3106
|
cfg;
|
|
3253
3107
|
inFlightNonces = /* @__PURE__ */ new Map();
|
|
3254
3108
|
constructor(config) {
|
|
3255
3109
|
if (!config.supportedTokens) {
|
|
3256
|
-
throw new PTClaimError(
|
|
3257
|
-
"UNSUPPORTED_POINT_TOKEN",
|
|
3258
|
-
"PTClaimHandler requires `supportedTokens` (issuer's allow-listed PointToken contracts)."
|
|
3259
|
-
);
|
|
3110
|
+
throw new PTClaimError("UNSUPPORTED_POINT_TOKEN", "PTClaimHandler requires `supportedTokens` (issuer's allow-listed PointToken contracts).");
|
|
3260
3111
|
}
|
|
3261
3112
|
const lockDurationMs = config.lockDurationMs ?? DEFAULT_LOCK_MS;
|
|
3262
3113
|
const signatureDeadlineSeconds = config.signatureDeadlineSeconds ?? DEFAULT_SIG_DEADLINE_SEC2;
|
|
3263
3114
|
const maxAllowedSignatureMs = lockDurationMs - M11_SAFETY_MARGIN_MS2;
|
|
3264
3115
|
if (signatureDeadlineSeconds * 1e3 > maxAllowedSignatureMs) {
|
|
3265
|
-
throw new PTClaimError(
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
signatureDeadlineSeconds,
|
|
3271
|
-
maxAllowedSignatureSec: maxAllowedSignatureMs / 1e3
|
|
3272
|
-
}
|
|
3273
|
-
);
|
|
3116
|
+
throw new PTClaimError("VALIDATION_FAILED", `PTClaimHandler config: signatureDeadlineSeconds (${signatureDeadlineSeconds}s) must be at most lockDurationMs - safety margin = ${maxAllowedSignatureMs / 1e3}s (lockDurationMs=${lockDurationMs / 1e3}s, safety=${M11_SAFETY_MARGIN_MS2 / 1e3}s).`, {
|
|
3117
|
+
lockDurationMs,
|
|
3118
|
+
signatureDeadlineSeconds,
|
|
3119
|
+
maxAllowedSignatureSec: maxAllowedSignatureMs / 1e3
|
|
3120
|
+
});
|
|
3274
3121
|
}
|
|
3275
3122
|
this.cfg = {
|
|
3276
3123
|
...config,
|
|
@@ -3281,51 +3128,39 @@ var PTClaimHandler = class {
|
|
|
3281
3128
|
}
|
|
3282
3129
|
async handle(request) {
|
|
3283
3130
|
if ((0, import_viem10.getAddress)(request.authenticatedAddress) !== (0, import_viem10.getAddress)(request.userAddress)) {
|
|
3284
|
-
throw new PTClaimError(
|
|
3285
|
-
"VALIDATION_FAILED",
|
|
3286
|
-
`userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
|
|
3287
|
-
);
|
|
3131
|
+
throw new PTClaimError("VALIDATION_FAILED", `userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`);
|
|
3288
3132
|
}
|
|
3289
3133
|
if (request.amount <= 0n) {
|
|
3290
3134
|
throw new PTClaimError("INVALID_AMOUNT", "claim amount must be positive");
|
|
3291
3135
|
}
|
|
3292
3136
|
const pointTokenAddress = (0, import_viem10.getAddress)(request.pointTokenAddress);
|
|
3293
3137
|
if (!this.cfg.supportedTokens.has(pointTokenAddress)) {
|
|
3294
|
-
throw new PTClaimError(
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
{ requested: pointTokenAddress }
|
|
3298
|
-
);
|
|
3138
|
+
throw new PTClaimError("UNSUPPORTED_POINT_TOKEN", `claim: pointTokenAddress ${pointTokenAddress} is not in the issuer's supported-token allowlist. Check IssuerApiHandlers.supportedTokens and PTClaimHandler.config.supportedTokens point at the same set.`, {
|
|
3139
|
+
requested: pointTokenAddress
|
|
3140
|
+
});
|
|
3299
3141
|
}
|
|
3300
3142
|
if (this.cfg.issuerStateValidator) {
|
|
3301
3143
|
try {
|
|
3302
|
-
await this.cfg.issuerStateValidator.preValidateMint(
|
|
3303
|
-
request.pointTokenAddress,
|
|
3304
|
-
request.amount
|
|
3305
|
-
);
|
|
3144
|
+
await this.cfg.issuerStateValidator.preValidateMint(request.pointTokenAddress, request.amount);
|
|
3306
3145
|
} catch (err) {
|
|
3307
3146
|
if (err instanceof IssuerStateError) throw err;
|
|
3308
|
-
throw new PTClaimError(
|
|
3309
|
-
"VALIDATION_FAILED",
|
|
3310
|
-
`issuer-state pre-validate failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3311
|
-
);
|
|
3147
|
+
throw new PTClaimError("VALIDATION_FAILED", `issuer-state pre-validate failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3312
3148
|
}
|
|
3313
3149
|
}
|
|
3314
3150
|
const chainAddresses = (0, import_core11.getContractAddresses)(request.chainId);
|
|
3315
|
-
const {
|
|
3151
|
+
const { kernel: batchExecutorAddress } = chainAddresses;
|
|
3316
3152
|
let mintRequestNonce;
|
|
3317
3153
|
try {
|
|
3318
3154
|
mintRequestNonce = await this.cfg.provider.readContract({
|
|
3319
3155
|
address: request.pointTokenAddress,
|
|
3320
3156
|
abi: import_core11.POINT_TOKEN_ABI,
|
|
3321
3157
|
functionName: "mintRequestNonces",
|
|
3322
|
-
args: [
|
|
3158
|
+
args: [
|
|
3159
|
+
request.userAddress
|
|
3160
|
+
]
|
|
3323
3161
|
});
|
|
3324
3162
|
} catch (err) {
|
|
3325
|
-
throw new PTClaimError(
|
|
3326
|
-
"NONCE_READ_FAILED",
|
|
3327
|
-
`failed to read mintRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
|
|
3328
|
-
);
|
|
3163
|
+
throw new PTClaimError("NONCE_READ_FAILED", `failed to read mintRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`);
|
|
3329
3164
|
}
|
|
3330
3165
|
const nonceKey = `${(0, import_viem10.getAddress)(request.userAddress).toLowerCase()}:${request.pointTokenAddress.toLowerCase()}`;
|
|
3331
3166
|
let userNonces = this.inFlightNonces.get(nonceKey);
|
|
@@ -3334,11 +3169,11 @@ var PTClaimHandler = class {
|
|
|
3334
3169
|
this.inFlightNonces.set(nonceKey, userNonces);
|
|
3335
3170
|
}
|
|
3336
3171
|
if (userNonces.has(mintRequestNonce)) {
|
|
3337
|
-
throw new PTClaimError(
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
);
|
|
3172
|
+
throw new PTClaimError("NONCE_IN_FLIGHT", `concurrent claim for nonce ${mintRequestNonce} in progress; retry after the prior request completes`, {
|
|
3173
|
+
userAddress: request.userAddress,
|
|
3174
|
+
pointToken: request.pointTokenAddress,
|
|
3175
|
+
nonce: mintRequestNonce.toString()
|
|
3176
|
+
});
|
|
3342
3177
|
}
|
|
3343
3178
|
userNonces.add(mintRequestNonce);
|
|
3344
3179
|
const wrapperOverride = this.cfg.mintFeeWrapperAddress;
|
|
@@ -3347,20 +3182,11 @@ var PTClaimHandler = class {
|
|
|
3347
3182
|
try {
|
|
3348
3183
|
const lockCreatedAtMs = this.cfg.now();
|
|
3349
3184
|
const lockExpiresAtMs = lockCreatedAtMs + this.cfg.lockDurationMs;
|
|
3350
|
-
const lockId = await this.cfg.ledger.lockForMinting(
|
|
3351
|
-
request.userAddress,
|
|
3352
|
-
request.amount,
|
|
3353
|
-
this.cfg.lockDurationMs,
|
|
3354
|
-
request.pointTokenAddress
|
|
3355
|
-
);
|
|
3185
|
+
const lockId = await this.cfg.ledger.lockForMinting(request.userAddress, request.amount, this.cfg.lockDurationMs, request.pointTokenAddress);
|
|
3356
3186
|
try {
|
|
3357
3187
|
const requestedDeadlineSec = Math.floor(lockCreatedAtMs / 1e3) + this.cfg.signatureDeadlineSeconds;
|
|
3358
|
-
const lockBoundedDeadlineSec = Math.floor(
|
|
3359
|
-
|
|
3360
|
-
);
|
|
3361
|
-
const signatureDeadline = BigInt(
|
|
3362
|
-
Math.min(requestedDeadlineSec, lockBoundedDeadlineSec)
|
|
3363
|
-
);
|
|
3188
|
+
const lockBoundedDeadlineSec = Math.floor((lockExpiresAtMs - M11_SAFETY_MARGIN_MS2) / 1e3);
|
|
3189
|
+
const signatureDeadline = BigInt(Math.min(requestedDeadlineSec, lockBoundedDeadlineSec));
|
|
3364
3190
|
const previewUserOp = this.cfg.relayService.previewMintUserOp({
|
|
3365
3191
|
userAddress: request.userAddress,
|
|
3366
3192
|
aaNonce: request.aaNonce,
|
|
@@ -3379,15 +3205,12 @@ var PTClaimHandler = class {
|
|
|
3379
3205
|
}
|
|
3380
3206
|
}) : 0n;
|
|
3381
3207
|
if (feeAmount > 0n && feeAmount >= request.amount) {
|
|
3382
|
-
throw new PTClaimError(
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
);
|
|
3208
|
+
throw new PTClaimError("INVALID_AMOUNT", `fee (${feeAmount}) must be strictly less than claim amount (${request.amount})`, {
|
|
3209
|
+
feeAmount: feeAmount.toString(),
|
|
3210
|
+
amount: request.amount.toString()
|
|
3211
|
+
});
|
|
3387
3212
|
}
|
|
3388
|
-
const domainName = await this.cfg.domainResolver.resolve(
|
|
3389
|
-
request.pointTokenAddress
|
|
3390
|
-
);
|
|
3213
|
+
const domainName = await this.cfg.domainResolver.resolve(request.pointTokenAddress);
|
|
3391
3214
|
const domain = {
|
|
3392
3215
|
name: domainName,
|
|
3393
3216
|
chainId: request.chainId,
|
|
@@ -3417,10 +3240,7 @@ var PTClaimHandler = class {
|
|
|
3417
3240
|
feeAmount
|
|
3418
3241
|
});
|
|
3419
3242
|
} catch (err) {
|
|
3420
|
-
throw new PTClaimError(
|
|
3421
|
-
"BUILD_FAILED",
|
|
3422
|
-
`prepareMint failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3423
|
-
);
|
|
3243
|
+
throw new PTClaimError("BUILD_FAILED", `prepareMint failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3424
3244
|
}
|
|
3425
3245
|
let fallback;
|
|
3426
3246
|
if (feeAmount > 0n) {
|
|
@@ -3439,14 +3259,11 @@ var PTClaimHandler = class {
|
|
|
3439
3259
|
mintFeeWrapperAddress: resolvedWrapper
|
|
3440
3260
|
});
|
|
3441
3261
|
} catch (err) {
|
|
3442
|
-
throw new PTClaimError(
|
|
3443
|
-
"BUILD_FAILED",
|
|
3444
|
-
`prepareMint (fallback) failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3445
|
-
);
|
|
3262
|
+
throw new PTClaimError("BUILD_FAILED", `prepareMint (fallback) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3446
3263
|
}
|
|
3447
3264
|
}
|
|
3448
|
-
const calls = (0, import_core11.
|
|
3449
|
-
const callsFallback = fallback ? (0, import_core11.
|
|
3265
|
+
const calls = (0, import_core11.decodeKernelExecuteCalls)(userOp.callData);
|
|
3266
|
+
const callsFallback = fallback ? (0, import_core11.decodeKernelExecuteCalls)(fallback.callData) : void 0;
|
|
3450
3267
|
return {
|
|
3451
3268
|
userOp,
|
|
3452
3269
|
fallback,
|
|
@@ -3472,6 +3289,9 @@ var PTClaimHandler = class {
|
|
|
3472
3289
|
// src/api/handlers/perpDepositHandler.ts
|
|
3473
3290
|
var import_core12 = require("@pafi-dev/core");
|
|
3474
3291
|
var PerpDepositError = class extends import_core.PafiSdkError {
|
|
3292
|
+
static {
|
|
3293
|
+
__name(this, "PerpDepositError");
|
|
3294
|
+
}
|
|
3475
3295
|
httpStatus = "unprocessable";
|
|
3476
3296
|
code;
|
|
3477
3297
|
safeToRetry;
|
|
@@ -3483,6 +3303,9 @@ var PerpDepositError = class extends import_core.PafiSdkError {
|
|
|
3483
3303
|
};
|
|
3484
3304
|
var DEFAULT_MAX_FEE_PREMIUM_BPS = 5e3;
|
|
3485
3305
|
var PerpDepositHandler = class {
|
|
3306
|
+
static {
|
|
3307
|
+
__name(this, "PerpDepositHandler");
|
|
3308
|
+
}
|
|
3486
3309
|
cfg;
|
|
3487
3310
|
constructor(config) {
|
|
3488
3311
|
this.cfg = {
|
|
@@ -3498,10 +3321,7 @@ var PerpDepositHandler = class {
|
|
|
3498
3321
|
const tokenHash = import_core12.TOKEN_HASHES.USDC;
|
|
3499
3322
|
const vault = import_core12.ORDERLY_VAULT_ADDRESSES[request.chainId];
|
|
3500
3323
|
if (!vault) {
|
|
3501
|
-
throw new PerpDepositError(
|
|
3502
|
-
"PERP_DEPOSIT_UNAVAILABLE",
|
|
3503
|
-
`no Orderly Vault for chainId ${request.chainId}`
|
|
3504
|
-
);
|
|
3324
|
+
throw new PerpDepositError("PERP_DEPOSIT_UNAVAILABLE", `no Orderly Vault for chainId ${request.chainId}`);
|
|
3505
3325
|
}
|
|
3506
3326
|
const { orderlyRelay: relayAddress, pafiFeeRecipient } = (0, import_core12.getContractAddresses)(request.chainId);
|
|
3507
3327
|
const [usdcAddress, brokerAllowed] = await Promise.all([
|
|
@@ -3509,20 +3329,21 @@ var PerpDepositHandler = class {
|
|
|
3509
3329
|
address: vault,
|
|
3510
3330
|
abi: import_core12.ORDERLY_VAULT_ABI,
|
|
3511
3331
|
functionName: "getAllowedToken",
|
|
3512
|
-
args: [
|
|
3332
|
+
args: [
|
|
3333
|
+
tokenHash
|
|
3334
|
+
]
|
|
3513
3335
|
}),
|
|
3514
3336
|
this.cfg.provider.readContract({
|
|
3515
3337
|
address: vault,
|
|
3516
3338
|
abi: import_core12.ORDERLY_VAULT_ABI,
|
|
3517
3339
|
functionName: "getAllowedBroker",
|
|
3518
|
-
args: [
|
|
3340
|
+
args: [
|
|
3341
|
+
brokerHash
|
|
3342
|
+
]
|
|
3519
3343
|
})
|
|
3520
3344
|
]);
|
|
3521
3345
|
if (!brokerAllowed) {
|
|
3522
|
-
throw new PerpDepositError(
|
|
3523
|
-
"BROKER_NOT_WHITELISTED",
|
|
3524
|
-
`broker "${request.brokerId}" is not whitelisted on Orderly Vault`
|
|
3525
|
-
);
|
|
3346
|
+
throw new PerpDepositError("BROKER_NOT_WHITELISTED", `broker "${request.brokerId}" is not whitelisted on Orderly Vault`);
|
|
3526
3347
|
}
|
|
3527
3348
|
const accountId = (0, import_core12.computeAccountId)(request.userAddress, brokerHash);
|
|
3528
3349
|
const requestForQuote = {
|
|
@@ -3537,7 +3358,9 @@ var PerpDepositHandler = class {
|
|
|
3537
3358
|
address: relayAddress,
|
|
3538
3359
|
abi: import_core12.ORDERLY_RELAY_ABI,
|
|
3539
3360
|
functionName: "quoteTokenFee",
|
|
3540
|
-
args: [
|
|
3361
|
+
args: [
|
|
3362
|
+
requestForQuote
|
|
3363
|
+
]
|
|
3541
3364
|
}),
|
|
3542
3365
|
(0, import_core12.quoteOperatorFeeUsdt)({
|
|
3543
3366
|
provider: this.cfg.provider,
|
|
@@ -3547,16 +3370,10 @@ var PerpDepositHandler = class {
|
|
|
3547
3370
|
})
|
|
3548
3371
|
]);
|
|
3549
3372
|
if (relayTokenFee >= request.amount) {
|
|
3550
|
-
throw new PerpDepositError(
|
|
3551
|
-
"RELAY_FEE_EXCEEDS_AMOUNT",
|
|
3552
|
-
`Relay quoted fee ${relayTokenFee} >= deposit amount ${request.amount}`
|
|
3553
|
-
);
|
|
3373
|
+
throw new PerpDepositError("RELAY_FEE_EXCEEDS_AMOUNT", `Relay quoted fee ${relayTokenFee} >= deposit amount ${request.amount}`);
|
|
3554
3374
|
}
|
|
3555
3375
|
if (usdcGasFee > 0n && usdcGasFee >= request.amount) {
|
|
3556
|
-
throw new PerpDepositError(
|
|
3557
|
-
"FEE_EXCEEDS_AMOUNT",
|
|
3558
|
-
`USDC gas fee ${usdcGasFee} >= deposit amount ${request.amount}`
|
|
3559
|
-
);
|
|
3376
|
+
throw new PerpDepositError("FEE_EXCEEDS_AMOUNT", `USDC gas fee ${usdcGasFee} >= deposit amount ${request.amount}`);
|
|
3560
3377
|
}
|
|
3561
3378
|
const maxFee = relayTokenFee * BigInt(1e4 + this.cfg.maxFeePremiumBps) / 10000n;
|
|
3562
3379
|
const depositReq = {
|
|
@@ -3591,125 +3408,19 @@ var PerpDepositHandler = class {
|
|
|
3591
3408
|
brokerHash,
|
|
3592
3409
|
usdcAddress,
|
|
3593
3410
|
relayAddress,
|
|
3594
|
-
calls: (0, import_core12.
|
|
3595
|
-
callsFallback: fallbackOp ? (0, import_core12.
|
|
3411
|
+
calls: (0, import_core12.decodeKernelExecuteCalls)(sponsoredOp.callData),
|
|
3412
|
+
callsFallback: fallbackOp ? (0, import_core12.decodeKernelExecuteCalls)(fallbackOp.callData) : void 0
|
|
3596
3413
|
};
|
|
3597
3414
|
}
|
|
3598
3415
|
};
|
|
3599
3416
|
|
|
3600
|
-
// src/api/delegateHandler.ts
|
|
3601
|
-
var import_core13 = require("@pafi-dev/core");
|
|
3602
|
-
var import_viem11 = require("viem");
|
|
3603
|
-
var DEFAULT_DELEGATE_GAS = {
|
|
3604
|
-
callGasLimit: 100000n,
|
|
3605
|
-
verificationGasLimit: 150000n,
|
|
3606
|
-
preVerificationGas: 50000n
|
|
3607
|
-
};
|
|
3608
|
-
async function handleDelegatePrepare(params) {
|
|
3609
|
-
const { batchExecutor } = (0, import_core13.getContractAddresses)(params.chainId);
|
|
3610
|
-
const partial = (0, import_core13.buildDelegationUserOp)({
|
|
3611
|
-
userAddress: params.userAddress,
|
|
3612
|
-
aaNonce: params.aaNonce,
|
|
3613
|
-
gasLimits: {
|
|
3614
|
-
callGasLimit: params.gasLimits?.callGasLimit ?? DEFAULT_DELEGATE_GAS.callGasLimit,
|
|
3615
|
-
verificationGasLimit: params.gasLimits?.verificationGasLimit ?? DEFAULT_DELEGATE_GAS.verificationGasLimit,
|
|
3616
|
-
preVerificationGas: params.gasLimits?.preVerificationGas ?? DEFAULT_DELEGATE_GAS.preVerificationGas
|
|
3617
|
-
}
|
|
3618
|
-
});
|
|
3619
|
-
const userOp = {
|
|
3620
|
-
sender: partial.sender,
|
|
3621
|
-
nonce: partial.nonce,
|
|
3622
|
-
callData: partial.callData,
|
|
3623
|
-
callGasLimit: partial.callGasLimit,
|
|
3624
|
-
verificationGasLimit: partial.verificationGasLimit,
|
|
3625
|
-
preVerificationGas: partial.preVerificationGas,
|
|
3626
|
-
maxFeePerGas: params.fees.maxFeePerGas ?? 0n,
|
|
3627
|
-
maxPriorityFeePerGas: params.fees.maxPriorityFeePerGas ?? 0n
|
|
3628
|
-
};
|
|
3629
|
-
const authorization = (0, import_core13.buildEip7702Authorization)({
|
|
3630
|
-
chainId: params.chainId,
|
|
3631
|
-
address: batchExecutor,
|
|
3632
|
-
nonce: params.delegationNonce,
|
|
3633
|
-
authSig: params.authSig
|
|
3634
|
-
});
|
|
3635
|
-
const paymasterFields = await requestPaymaster({
|
|
3636
|
-
client: params.pafiBackendClient,
|
|
3637
|
-
chainId: params.chainId,
|
|
3638
|
-
scenario: "delegate",
|
|
3639
|
-
userOp,
|
|
3640
|
-
pointTokenAddress: batchExecutor,
|
|
3641
|
-
eip7702Auth: authorization,
|
|
3642
|
-
onWarning: params.onWarning
|
|
3643
|
-
});
|
|
3644
|
-
const prepared = applyPaymasterGasEstimates(
|
|
3645
|
-
userOp,
|
|
3646
|
-
paymasterFields,
|
|
3647
|
-
params.chainId
|
|
3648
|
-
);
|
|
3649
|
-
const merged = prepared.userOp;
|
|
3650
|
-
const userOpHash = prepared.userOpHash;
|
|
3651
|
-
await params.store.save(
|
|
3652
|
-
params.lockId,
|
|
3653
|
-
{
|
|
3654
|
-
sender: merged.sender,
|
|
3655
|
-
nonce: merged.nonce.toString(10),
|
|
3656
|
-
callData: merged.callData,
|
|
3657
|
-
callGasLimit: merged.callGasLimit.toString(10),
|
|
3658
|
-
verificationGasLimit: merged.verificationGasLimit.toString(10),
|
|
3659
|
-
preVerificationGas: merged.preVerificationGas.toString(10),
|
|
3660
|
-
maxFeePerGas: merged.maxFeePerGas.toString(10),
|
|
3661
|
-
maxPriorityFeePerGas: merged.maxPriorityFeePerGas.toString(10),
|
|
3662
|
-
...merged.paymaster ? { paymaster: merged.paymaster } : {},
|
|
3663
|
-
...merged.paymasterVerificationGasLimit ? {
|
|
3664
|
-
paymasterVerificationGasLimit: merged.paymasterVerificationGasLimit.toString(10)
|
|
3665
|
-
} : {},
|
|
3666
|
-
...merged.paymasterPostOpGasLimit ? {
|
|
3667
|
-
paymasterPostOpGasLimit: merged.paymasterPostOpGasLimit.toString(10)
|
|
3668
|
-
} : {},
|
|
3669
|
-
...merged.paymasterData ? { paymasterData: merged.paymasterData } : {},
|
|
3670
|
-
chainId: params.chainId,
|
|
3671
|
-
userOpHash,
|
|
3672
|
-
eip7702Auth: authorization
|
|
3673
|
-
},
|
|
3674
|
-
params.ttlSeconds
|
|
3675
|
-
);
|
|
3676
|
-
return {
|
|
3677
|
-
lockId: params.lockId,
|
|
3678
|
-
userOpHash,
|
|
3679
|
-
typedData: prepared.typedData,
|
|
3680
|
-
expiresInSeconds: params.ttlSeconds,
|
|
3681
|
-
isSponsored: !!paymasterFields
|
|
3682
|
-
};
|
|
3683
|
-
}
|
|
3684
|
-
async function handleDelegateSubmit(params) {
|
|
3685
|
-
const entry = await params.store.get(params.lockId);
|
|
3686
|
-
if (!entry) {
|
|
3687
|
-
throw new PendingUserOpNotFoundError(params.lockId);
|
|
3688
|
-
}
|
|
3689
|
-
if ((0, import_viem11.getAddress)(entry.sender) !== (0, import_viem11.getAddress)(params.authenticatedAddress)) {
|
|
3690
|
-
throw new PendingUserOpForbiddenError(params.lockId);
|
|
3691
|
-
}
|
|
3692
|
-
if (!entry.eip7702Auth) {
|
|
3693
|
-
throw new Error(
|
|
3694
|
-
`delegate entry ${params.lockId} missing eip7702Auth \u2014 prepare step did not run correctly`
|
|
3695
|
-
);
|
|
3696
|
-
}
|
|
3697
|
-
const userOpJson = serializeEntryToJsonRpc(entry, params.userOpSig, "sponsored");
|
|
3698
|
-
const result = await relayUserOp({
|
|
3699
|
-
client: params.pafiBackendClient,
|
|
3700
|
-
userOp: userOpJson,
|
|
3701
|
-
entryPoint: params.entryPoint ?? import_core13.ENTRY_POINT_V08,
|
|
3702
|
-
eip7702Auth: entry.eip7702Auth
|
|
3703
|
-
});
|
|
3704
|
-
await params.store.delete(params.lockId);
|
|
3705
|
-
return { userOpHash: result.userOpHash };
|
|
3706
|
-
}
|
|
3707
|
-
|
|
3708
3417
|
// src/api/issuerApiAdapter.ts
|
|
3709
|
-
var
|
|
3710
|
-
var
|
|
3711
|
-
var import_core14 = require("@pafi-dev/core");
|
|
3418
|
+
var import_viem11 = require("viem");
|
|
3419
|
+
var import_core13 = require("@pafi-dev/core");
|
|
3712
3420
|
var AdapterMisconfiguredError = class extends Error {
|
|
3421
|
+
static {
|
|
3422
|
+
__name(this, "AdapterMisconfiguredError");
|
|
3423
|
+
}
|
|
3713
3424
|
code = "ADAPTER_MISCONFIGURED";
|
|
3714
3425
|
constructor(message) {
|
|
3715
3426
|
super(message);
|
|
@@ -3717,35 +3428,28 @@ var AdapterMisconfiguredError = class extends Error {
|
|
|
3717
3428
|
}
|
|
3718
3429
|
};
|
|
3719
3430
|
var IssuerApiAdapter = class {
|
|
3431
|
+
static {
|
|
3432
|
+
__name(this, "IssuerApiAdapter");
|
|
3433
|
+
}
|
|
3720
3434
|
cfg;
|
|
3721
3435
|
constructor(config) {
|
|
3722
3436
|
if (config.ptClaimHandler) {
|
|
3723
3437
|
if (typeof config.ledger.bindMintUserOpHash !== "function") {
|
|
3724
|
-
throw new AdapterMisconfiguredError(
|
|
3725
|
-
"ledger.bindMintUserOpHash is required when ptClaimHandler is wired (mobile claim flow). Implement it on your IPointLedger or omit ptClaimHandler from IssuerApiAdapter config."
|
|
3726
|
-
);
|
|
3438
|
+
throw new AdapterMisconfiguredError("ledger.bindMintUserOpHash is required when ptClaimHandler is wired (mobile claim flow). Implement it on your IPointLedger or omit ptClaimHandler from IssuerApiAdapter config.");
|
|
3727
3439
|
}
|
|
3728
3440
|
if (typeof config.ledger.getMintLock !== "function") {
|
|
3729
|
-
throw new AdapterMisconfiguredError(
|
|
3730
|
-
"ledger.getMintLock is required when ptClaimHandler is wired \u2014 claimStatus uses it to look up the lock."
|
|
3731
|
-
);
|
|
3441
|
+
throw new AdapterMisconfiguredError("ledger.getMintLock is required when ptClaimHandler is wired \u2014 claimStatus uses it to look up the lock.");
|
|
3732
3442
|
}
|
|
3733
3443
|
}
|
|
3734
3444
|
if (config.ptRedeemHandler) {
|
|
3735
3445
|
if (typeof config.ledger.reservePendingCredit !== "function") {
|
|
3736
|
-
throw new AdapterMisconfiguredError(
|
|
3737
|
-
"ledger.reservePendingCredit is required when ptRedeemHandler is wired (burn/redeem reverse flow). PTRedeemHandler also enforces this at construction; see ledger/types.ts comments."
|
|
3738
|
-
);
|
|
3446
|
+
throw new AdapterMisconfiguredError("ledger.reservePendingCredit is required when ptRedeemHandler is wired (burn/redeem reverse flow). PTRedeemHandler also enforces this at construction; see ledger/types.ts comments.");
|
|
3739
3447
|
}
|
|
3740
3448
|
if (typeof config.ledger.bindCreditUserOpHash !== "function") {
|
|
3741
|
-
throw new AdapterMisconfiguredError(
|
|
3742
|
-
"ledger.bindCreditUserOpHash is required when ptRedeemHandler is wired (mobile redeem flow)."
|
|
3743
|
-
);
|
|
3449
|
+
throw new AdapterMisconfiguredError("ledger.bindCreditUserOpHash is required when ptRedeemHandler is wired (mobile redeem flow).");
|
|
3744
3450
|
}
|
|
3745
3451
|
if (typeof config.ledger.getPendingCredit !== "function") {
|
|
3746
|
-
throw new AdapterMisconfiguredError(
|
|
3747
|
-
"ledger.getPendingCredit is required when ptRedeemHandler is wired \u2014 redeemStatus uses it to look up the credit."
|
|
3748
|
-
);
|
|
3452
|
+
throw new AdapterMisconfiguredError("ledger.getPendingCredit is required when ptRedeemHandler is wired \u2014 redeemStatus uses it to look up the credit.");
|
|
3749
3453
|
}
|
|
3750
3454
|
}
|
|
3751
3455
|
this.cfg = config;
|
|
@@ -3760,24 +3464,25 @@ var IssuerApiAdapter = class {
|
|
|
3760
3464
|
}
|
|
3761
3465
|
async gasFee() {
|
|
3762
3466
|
const result = await this.cfg.issuerService.api.handleGasFee();
|
|
3763
|
-
return {
|
|
3467
|
+
return {
|
|
3468
|
+
gasFeeUsdt: result.gasFeeUsdt.toString()
|
|
3469
|
+
};
|
|
3764
3470
|
}
|
|
3765
3471
|
async pools(authenticatedAddress, chainId, pointTokenAddress) {
|
|
3766
|
-
const result = await this.cfg.issuerService.api.handlePools(
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
);
|
|
3770
|
-
return {
|
|
3472
|
+
const result = await this.cfg.issuerService.api.handlePools(authenticatedAddress, {
|
|
3473
|
+
chainId,
|
|
3474
|
+
pointTokenAddress: (0, import_viem11.getAddress)(pointTokenAddress)
|
|
3475
|
+
});
|
|
3476
|
+
return {
|
|
3477
|
+
pools: result.pools
|
|
3478
|
+
};
|
|
3771
3479
|
}
|
|
3772
3480
|
async user(authenticatedAddress, chainId, userAddress, pointTokenAddress) {
|
|
3773
|
-
const result = await this.cfg.issuerService.api.handleUser(
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
pointTokenAddress: (0, import_viem12.getAddress)(pointTokenAddress)
|
|
3779
|
-
}
|
|
3780
|
-
);
|
|
3481
|
+
const result = await this.cfg.issuerService.api.handleUser(authenticatedAddress, {
|
|
3482
|
+
chainId,
|
|
3483
|
+
userAddress: (0, import_viem11.getAddress)(userAddress),
|
|
3484
|
+
pointTokenAddress: (0, import_viem11.getAddress)(pointTokenAddress)
|
|
3485
|
+
});
|
|
3781
3486
|
return {
|
|
3782
3487
|
offChainBalance: result.offChainBalance.toString(),
|
|
3783
3488
|
onChainBalance: result.onChainBalance.toString(),
|
|
@@ -3790,12 +3495,8 @@ var IssuerApiAdapter = class {
|
|
|
3790
3495
|
// directly. Issuer SDK doesn't ship swap/quote anymore.
|
|
3791
3496
|
// ------------------------------ Action endpoints -------------------------
|
|
3792
3497
|
async claim(input) {
|
|
3793
|
-
const ptClaimHandler = this.assertHandler(
|
|
3794
|
-
|
|
3795
|
-
"ptClaimHandler",
|
|
3796
|
-
"claim"
|
|
3797
|
-
);
|
|
3798
|
-
const pointTokenAddress = (0, import_viem12.getAddress)(input.pointTokenAddress);
|
|
3498
|
+
const ptClaimHandler = this.assertHandler(this.cfg.ptClaimHandler, "ptClaimHandler", "claim");
|
|
3499
|
+
const pointTokenAddress = (0, import_viem11.getAddress)(input.pointTokenAddress);
|
|
3799
3500
|
const result = await ptClaimHandler.handle({
|
|
3800
3501
|
authenticatedAddress: input.authenticatedAddress,
|
|
3801
3502
|
userAddress: input.authenticatedAddress,
|
|
@@ -3804,12 +3505,7 @@ var IssuerApiAdapter = class {
|
|
|
3804
3505
|
chainId: input.chainId,
|
|
3805
3506
|
aaNonce: input.aaNonce
|
|
3806
3507
|
});
|
|
3807
|
-
const sponsorAuth = await this.buildSponsorAuth(
|
|
3808
|
-
input.authenticatedAddress,
|
|
3809
|
-
result.userOp.callData,
|
|
3810
|
-
input.chainId,
|
|
3811
|
-
"mint"
|
|
3812
|
-
);
|
|
3508
|
+
const sponsorAuth = await this.buildSponsorAuth(input.authenticatedAddress, result.userOp.callData, input.chainId, "mint");
|
|
3813
3509
|
return {
|
|
3814
3510
|
calls: result.calls,
|
|
3815
3511
|
callsFallback: result.callsFallback,
|
|
@@ -3821,7 +3517,7 @@ var IssuerApiAdapter = class {
|
|
|
3821
3517
|
}
|
|
3822
3518
|
async redeem(input) {
|
|
3823
3519
|
this.assertRedeemHandler();
|
|
3824
|
-
const pointTokenAddress = (0,
|
|
3520
|
+
const pointTokenAddress = (0, import_viem11.getAddress)(input.pointTokenAddress);
|
|
3825
3521
|
const response = await this.cfg.ptRedeemHandler.handle({
|
|
3826
3522
|
userAddress: input.authenticatedAddress,
|
|
3827
3523
|
authenticatedAddress: input.authenticatedAddress,
|
|
@@ -3830,15 +3526,10 @@ var IssuerApiAdapter = class {
|
|
|
3830
3526
|
aaNonce: input.aaNonce,
|
|
3831
3527
|
chainId: input.chainId
|
|
3832
3528
|
});
|
|
3833
|
-
const sponsorAuth = await this.buildSponsorAuth(
|
|
3834
|
-
input.authenticatedAddress,
|
|
3835
|
-
response.userOp.callData,
|
|
3836
|
-
input.chainId,
|
|
3837
|
-
"burn"
|
|
3838
|
-
);
|
|
3529
|
+
const sponsorAuth = await this.buildSponsorAuth(input.authenticatedAddress, response.userOp.callData, input.chainId, "burn");
|
|
3839
3530
|
return {
|
|
3840
|
-
calls: (0,
|
|
3841
|
-
callsFallback: response.fallback ? (0,
|
|
3531
|
+
calls: (0, import_core13.decodeKernelExecuteCalls)(response.userOp.callData),
|
|
3532
|
+
callsFallback: response.fallback ? (0, import_core13.decodeKernelExecuteCalls)(response.fallback.userOp.callData) : void 0,
|
|
3842
3533
|
feeAmount: response.feeAmount.toString(),
|
|
3843
3534
|
lockId: response.lockId,
|
|
3844
3535
|
lockIdFallback: response.fallback?.lockId,
|
|
@@ -3852,11 +3543,7 @@ var IssuerApiAdapter = class {
|
|
|
3852
3543
|
// swap() removed (2026-04-27) — moved to @pafi-dev/trading.
|
|
3853
3544
|
// PAFI's web FE calls TradingHandlers.handleSwap directly.
|
|
3854
3545
|
async perpDeposit(input) {
|
|
3855
|
-
const perpHandler = this.assertHandler(
|
|
3856
|
-
this.cfg.perpHandler,
|
|
3857
|
-
"perpHandler",
|
|
3858
|
-
"perpDeposit"
|
|
3859
|
-
);
|
|
3546
|
+
const perpHandler = this.assertHandler(this.cfg.perpHandler, "perpHandler", "perpDeposit");
|
|
3860
3547
|
const result = await perpHandler.handle({
|
|
3861
3548
|
userAddress: input.authenticatedAddress,
|
|
3862
3549
|
chainId: input.chainId,
|
|
@@ -3864,12 +3551,7 @@ var IssuerApiAdapter = class {
|
|
|
3864
3551
|
brokerId: input.brokerId,
|
|
3865
3552
|
aaNonce: input.aaNonce
|
|
3866
3553
|
});
|
|
3867
|
-
const sponsorAuth = await this.buildSponsorAuth(
|
|
3868
|
-
input.authenticatedAddress,
|
|
3869
|
-
result.userOp.callData,
|
|
3870
|
-
input.chainId,
|
|
3871
|
-
"perp-deposit"
|
|
3872
|
-
);
|
|
3554
|
+
const sponsorAuth = await this.buildSponsorAuth(input.authenticatedAddress, result.userOp.callData, input.chainId, "perp-deposit");
|
|
3873
3555
|
return {
|
|
3874
3556
|
calls: result.calls,
|
|
3875
3557
|
callsFallback: result.callsFallback,
|
|
@@ -3886,12 +3568,8 @@ var IssuerApiAdapter = class {
|
|
|
3886
3568
|
}
|
|
3887
3569
|
// ------------------------------ Mobile endpoints -------------------------
|
|
3888
3570
|
async claimPrepare(input) {
|
|
3889
|
-
const ptClaimHandler = this.assertHandler(
|
|
3890
|
-
|
|
3891
|
-
"ptClaimHandler",
|
|
3892
|
-
"claimPrepare"
|
|
3893
|
-
);
|
|
3894
|
-
const pointTokenAddress = (0, import_viem12.getAddress)(input.pointTokenAddress);
|
|
3571
|
+
const ptClaimHandler = this.assertHandler(this.cfg.ptClaimHandler, "ptClaimHandler", "claimPrepare");
|
|
3572
|
+
const pointTokenAddress = (0, import_viem11.getAddress)(input.pointTokenAddress);
|
|
3895
3573
|
const claimResult = await ptClaimHandler.handle({
|
|
3896
3574
|
authenticatedAddress: input.authenticatedAddress,
|
|
3897
3575
|
userAddress: input.authenticatedAddress,
|
|
@@ -3900,23 +3578,11 @@ var IssuerApiAdapter = class {
|
|
|
3900
3578
|
chainId: input.chainId,
|
|
3901
3579
|
aaNonce: input.aaNonce
|
|
3902
3580
|
});
|
|
3903
|
-
const prepared = await this.runMobilePrepare(
|
|
3904
|
-
input.authenticatedAddress,
|
|
3905
|
-
input.chainId,
|
|
3906
|
-
claimResult.lockId,
|
|
3907
|
-
claimResult.userOp,
|
|
3908
|
-
claimResult.fallback,
|
|
3909
|
-
"mint",
|
|
3910
|
-
pointTokenAddress,
|
|
3911
|
-
claimResult.expiresInSeconds,
|
|
3912
|
-
input.eip7702Auth
|
|
3913
|
-
);
|
|
3581
|
+
const prepared = await this.runMobilePrepare(input.authenticatedAddress, input.chainId, claimResult.lockId, claimResult.userOp, claimResult.fallback, "mint", pointTokenAddress, claimResult.expiresInSeconds, input.eip7702Auth);
|
|
3914
3582
|
return {
|
|
3915
3583
|
lockId: claimResult.lockId,
|
|
3916
3584
|
userOpHash: prepared.sponsored.userOpHash,
|
|
3917
|
-
typedData: prepared.sponsored.typedData,
|
|
3918
3585
|
userOpHashFallback: prepared.fallback?.userOpHash,
|
|
3919
|
-
typedDataFallback: prepared.fallback?.typedData,
|
|
3920
3586
|
feeAmount: claimResult.feeAmount.toString(),
|
|
3921
3587
|
signatureDeadline: claimResult.signatureDeadline.toString(),
|
|
3922
3588
|
expiresInSeconds: claimResult.expiresInSeconds,
|
|
@@ -3931,13 +3597,13 @@ var IssuerApiAdapter = class {
|
|
|
3931
3597
|
signature: input.signature,
|
|
3932
3598
|
variant: input.variant,
|
|
3933
3599
|
store: this.cfg.pendingUserOpStore,
|
|
3934
|
-
bindUserOpHash: (lockId, hash) => this.cfg.ledger.bindMintUserOpHash(lockId, hash),
|
|
3600
|
+
bindUserOpHash: /* @__PURE__ */ __name((lockId, hash) => this.cfg.ledger.bindMintUserOpHash(lockId, hash), "bindUserOpHash"),
|
|
3935
3601
|
pafiBackendClient: this.cfg.pafiBackendClient
|
|
3936
3602
|
});
|
|
3937
3603
|
}
|
|
3938
3604
|
async redeemPrepare(input) {
|
|
3939
3605
|
this.assertRedeemHandler();
|
|
3940
|
-
const pointTokenAddress = (0,
|
|
3606
|
+
const pointTokenAddress = (0, import_viem11.getAddress)(input.pointTokenAddress);
|
|
3941
3607
|
const redeemResponse = await this.cfg.ptRedeemHandler.handle({
|
|
3942
3608
|
userAddress: input.authenticatedAddress,
|
|
3943
3609
|
authenticatedAddress: input.authenticatedAddress,
|
|
@@ -3946,25 +3612,12 @@ var IssuerApiAdapter = class {
|
|
|
3946
3612
|
aaNonce: input.aaNonce,
|
|
3947
3613
|
chainId: input.chainId
|
|
3948
3614
|
});
|
|
3949
|
-
const prepared = await this.runMobilePrepare(
|
|
3950
|
-
input.authenticatedAddress,
|
|
3951
|
-
input.chainId,
|
|
3952
|
-
redeemResponse.lockId,
|
|
3953
|
-
redeemResponse.userOp,
|
|
3954
|
-
redeemResponse.fallback?.userOp,
|
|
3955
|
-
"burn",
|
|
3956
|
-
pointTokenAddress,
|
|
3957
|
-
redeemResponse.expiresInSeconds,
|
|
3958
|
-
input.eip7702Auth,
|
|
3959
|
-
redeemResponse.fallback?.lockId
|
|
3960
|
-
);
|
|
3615
|
+
const prepared = await this.runMobilePrepare(input.authenticatedAddress, input.chainId, redeemResponse.lockId, redeemResponse.userOp, redeemResponse.fallback?.userOp, "burn", pointTokenAddress, redeemResponse.expiresInSeconds, input.eip7702Auth, redeemResponse.fallback?.lockId);
|
|
3961
3616
|
return {
|
|
3962
3617
|
lockId: redeemResponse.lockId,
|
|
3963
3618
|
lockIdFallback: redeemResponse.fallback?.lockId,
|
|
3964
3619
|
userOpHash: prepared.sponsored.userOpHash,
|
|
3965
|
-
typedData: prepared.sponsored.typedData,
|
|
3966
3620
|
userOpHashFallback: prepared.fallback?.userOpHash,
|
|
3967
|
-
typedDataFallback: prepared.fallback?.typedData,
|
|
3968
3621
|
netCreditAmount: redeemResponse.netCreditAmount.toString(),
|
|
3969
3622
|
netCreditAmountFallback: redeemResponse.fallback?.netCreditAmount.toString(),
|
|
3970
3623
|
feeAmount: redeemResponse.feeAmount.toString(),
|
|
@@ -3981,7 +3634,7 @@ var IssuerApiAdapter = class {
|
|
|
3981
3634
|
signature: input.signature,
|
|
3982
3635
|
variant: input.variant,
|
|
3983
3636
|
store: this.cfg.pendingUserOpStore,
|
|
3984
|
-
bindUserOpHash: (lockId, hash) => this.cfg.ledger.bindCreditUserOpHash(lockId, hash),
|
|
3637
|
+
bindUserOpHash: /* @__PURE__ */ __name((lockId, hash) => this.cfg.ledger.bindCreditUserOpHash(lockId, hash), "bindUserOpHash"),
|
|
3985
3638
|
pafiBackendClient: this.cfg.pafiBackendClient
|
|
3986
3639
|
});
|
|
3987
3640
|
}
|
|
@@ -4005,86 +3658,18 @@ var IssuerApiAdapter = class {
|
|
|
4005
3658
|
onWarning: this.cfg.onWarning
|
|
4006
3659
|
});
|
|
4007
3660
|
}
|
|
4008
|
-
//
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
this.cfg.provider.getCode({ address: authenticatedAddress }),
|
|
4013
|
-
this.cfg.provider.getTransactionCount({
|
|
4014
|
-
address: authenticatedAddress,
|
|
4015
|
-
blockTag: "pending"
|
|
4016
|
-
})
|
|
4017
|
-
]);
|
|
4018
|
-
return {
|
|
4019
|
-
isDelegated: (0, import_core14.parseEip7702DelegatedAddress)(code) !== null,
|
|
4020
|
-
batchExecutorAddress: batchExecutor,
|
|
4021
|
-
delegationNonce: nonce.toString(),
|
|
4022
|
-
chainId
|
|
4023
|
-
};
|
|
4024
|
-
}
|
|
4025
|
-
/**
|
|
4026
|
-
* Build the delegation-anchor UserOp + obtain paymaster sponsorship
|
|
4027
|
-
* + persist as a pending entry. Mobile must:
|
|
4028
|
-
*
|
|
4029
|
-
* 1. Sign EIP-7702 authorization LOCALLY (Privy `signAuthorization`
|
|
4030
|
-
* with `{contractAddress: batchExecutorAddress, chainId,
|
|
4031
|
-
* nonce: delegationNonce}`) → 65-byte authSig hex.
|
|
4032
|
-
* 2. POST `/delegate/prepare` with `{ chainId, delegationNonce,
|
|
4033
|
-
* authSig }` → this method.
|
|
4034
|
-
* 3. Sign returned `userOpHash` LOCALLY (`signTypedData(typedData)`).
|
|
4035
|
-
* 4. POST `/delegate/submit` with `{ lockId, userOpSig }`.
|
|
4036
|
-
*
|
|
4037
|
-
* v0.7.7 — replaces single-shot delegateSubmit that tried to relay
|
|
4038
|
-
* a UserOp with empty `signature: "0x"` (Simple7702Account's
|
|
4039
|
-
* validateUserOp reverts `ECDSAInvalidSignatureLength` 0xfce698f7).
|
|
4040
|
-
*/
|
|
4041
|
-
async delegatePrepare(authenticatedAddress, input) {
|
|
4042
|
-
const { batchExecutor } = (0, import_core14.getContractAddresses)(input.chainId);
|
|
4043
|
-
const fees = await this.cfg.provider.estimateFeesPerGas();
|
|
4044
|
-
const lockId = (0, import_node_crypto3.randomUUID)();
|
|
4045
|
-
const result = await handleDelegatePrepare({
|
|
4046
|
-
userAddress: authenticatedAddress,
|
|
4047
|
-
chainId: input.chainId,
|
|
4048
|
-
delegationNonce: input.delegationNonce,
|
|
4049
|
-
aaNonce: input.aaNonce,
|
|
4050
|
-
authSig: input.authSig,
|
|
4051
|
-
fees,
|
|
4052
|
-
lockId,
|
|
4053
|
-
store: this.cfg.pendingUserOpStore,
|
|
4054
|
-
ttlSeconds: 15 * 60,
|
|
4055
|
-
// 15min — match claim/redeem mobile lock duration
|
|
4056
|
-
pafiBackendClient: this.cfg.pafiBackendClient,
|
|
4057
|
-
onWarning: this.cfg.onWarning
|
|
4058
|
-
});
|
|
4059
|
-
return {
|
|
4060
|
-
lockId: result.lockId,
|
|
4061
|
-
userOpHash: result.userOpHash,
|
|
4062
|
-
typedData: result.typedData,
|
|
4063
|
-
expiresInSeconds: result.expiresInSeconds,
|
|
4064
|
-
isSponsored: result.isSponsored,
|
|
4065
|
-
delegationNonce: input.delegationNonce.toString(),
|
|
4066
|
-
batchExecutorAddress: batchExecutor,
|
|
4067
|
-
chainId: input.chainId
|
|
4068
|
-
};
|
|
4069
|
-
}
|
|
4070
|
-
async delegateSubmit(input) {
|
|
4071
|
-
const result = await handleDelegateSubmit({
|
|
4072
|
-
lockId: input.lockId,
|
|
4073
|
-
authenticatedAddress: input.authenticatedAddress,
|
|
4074
|
-
userOpSig: input.userOpSig,
|
|
4075
|
-
store: this.cfg.pendingUserOpStore,
|
|
4076
|
-
pafiBackendClient: this.cfg.pafiBackendClient
|
|
4077
|
-
});
|
|
4078
|
-
return { userOpHash: result.userOpHash };
|
|
4079
|
-
}
|
|
3661
|
+
// Delegate endpoints removed — folded-in delegation (via
|
|
3662
|
+
// `eip7702Auth` on claim/redeem prepare) is now the only path. The
|
|
3663
|
+
// bundler applies SetCode + handleOps in one tx, so there is no
|
|
3664
|
+
// separate `/delegate/*` flow. See `handleMobilePrepare`.
|
|
4080
3665
|
// ------------------------------ Internal helpers -------------------------
|
|
4081
3666
|
/**
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
3667
|
+
* Build + sign a SponsorAuth payload. Returns `undefined` when no
|
|
3668
|
+
* issuer id is configured, so the controller can skip the field.
|
|
3669
|
+
*/
|
|
4085
3670
|
async buildSponsorAuth(authenticatedAddress, callData, chainId, scenario) {
|
|
4086
3671
|
if (!this.cfg.pafiIssuerId) return void 0;
|
|
4087
|
-
return (0,
|
|
3672
|
+
return (0, import_core13.buildAndSignSponsorAuth)({
|
|
4088
3673
|
userAddress: authenticatedAddress,
|
|
4089
3674
|
callData,
|
|
4090
3675
|
chainId,
|
|
@@ -4113,30 +3698,26 @@ var IssuerApiAdapter = class {
|
|
|
4113
3698
|
}
|
|
4114
3699
|
assertRedeemHandler() {
|
|
4115
3700
|
if (!this.cfg.ptRedeemHandler) {
|
|
4116
|
-
throw new Error(
|
|
4117
|
-
"PTRedeemHandler not wired \u2014 IssuerApiAdapter.redeem* require a configured ptRedeemHandler."
|
|
4118
|
-
);
|
|
3701
|
+
throw new Error("PTRedeemHandler not wired \u2014 IssuerApiAdapter.redeem* require a configured ptRedeemHandler.");
|
|
4119
3702
|
}
|
|
4120
3703
|
}
|
|
4121
3704
|
/**
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
3705
|
+
* Narrow an optional handler to non-null and throw a clear error when
|
|
3706
|
+
* the issuer wired the adapter without it. Lets issuers opt out of
|
|
3707
|
+
* flows they don't expose (gg56 ships only mobile claim/redeem, so
|
|
3708
|
+
* `swapHandler` + `perpHandler` aren't constructed).
|
|
3709
|
+
*/
|
|
4127
3710
|
assertHandler(handler, fieldName, methodName) {
|
|
4128
3711
|
if (handler === null || handler === void 0) {
|
|
4129
|
-
throw new Error(
|
|
4130
|
-
`${fieldName} not wired \u2014 IssuerApiAdapter.${methodName}() requires a configured ${fieldName}.`
|
|
4131
|
-
);
|
|
3712
|
+
throw new Error(`${fieldName} not wired \u2014 IssuerApiAdapter.${methodName}() requires a configured ${fieldName}.`);
|
|
4132
3713
|
}
|
|
4133
3714
|
return handler;
|
|
4134
3715
|
}
|
|
4135
3716
|
};
|
|
4136
3717
|
|
|
4137
3718
|
// src/pools/subgraphPoolsProvider.ts
|
|
4138
|
-
var
|
|
4139
|
-
var
|
|
3719
|
+
var import_viem12 = require("viem");
|
|
3720
|
+
var import_core14 = require("@pafi-dev/core");
|
|
4140
3721
|
var DEFAULT_CACHE_TTL_MS = 3e4;
|
|
4141
3722
|
var MAX_REASONABLE_FEE_TIER = 1e6;
|
|
4142
3723
|
var POOL_QUERY = `
|
|
@@ -4153,7 +3734,7 @@ var POOL_QUERY = `
|
|
|
4153
3734
|
}
|
|
4154
3735
|
`;
|
|
4155
3736
|
function createSubgraphPoolsProvider(config = {}) {
|
|
4156
|
-
const subgraphUrl = config.subgraphUrl ??
|
|
3737
|
+
const subgraphUrl = config.subgraphUrl ?? import_core14.PAFI_SUBGRAPH_URL;
|
|
4157
3738
|
try {
|
|
4158
3739
|
const parsed = new URL(subgraphUrl);
|
|
4159
3740
|
if (process.env.NODE_ENV === "production" && parsed.protocol !== "https:") {
|
|
@@ -4161,9 +3742,7 @@ function createSubgraphPoolsProvider(config = {}) {
|
|
|
4161
3742
|
}
|
|
4162
3743
|
} catch (err) {
|
|
4163
3744
|
if (err instanceof TypeError) {
|
|
4164
|
-
throw new Error(
|
|
4165
|
-
`subgraphPoolsProvider: invalid subgraphUrl: ${subgraphUrl}`
|
|
4166
|
-
);
|
|
3745
|
+
throw new Error(`subgraphPoolsProvider: invalid subgraphUrl: ${subgraphUrl}`);
|
|
4167
3746
|
}
|
|
4168
3747
|
throw err;
|
|
4169
3748
|
}
|
|
@@ -4173,64 +3752,61 @@ function createSubgraphPoolsProvider(config = {}) {
|
|
|
4173
3752
|
const onError = config.onError;
|
|
4174
3753
|
const cache = /* @__PURE__ */ new Map();
|
|
4175
3754
|
if (!fetchImpl) {
|
|
4176
|
-
throw new Error(
|
|
4177
|
-
"createSubgraphPoolsProvider: no fetch implementation available \u2014 pass `fetchImpl` or run on Node 18+"
|
|
4178
|
-
);
|
|
3755
|
+
throw new Error("createSubgraphPoolsProvider: no fetch implementation available \u2014 pass `fetchImpl` or run on Node 18+");
|
|
4179
3756
|
}
|
|
4180
|
-
const reportError = (err) => {
|
|
3757
|
+
const reportError = /* @__PURE__ */ __name((err) => {
|
|
4181
3758
|
if (!onError) return;
|
|
4182
3759
|
try {
|
|
4183
3760
|
onError(err);
|
|
4184
3761
|
} catch {
|
|
4185
3762
|
}
|
|
4186
|
-
};
|
|
3763
|
+
}, "reportError");
|
|
4187
3764
|
return async (request) => {
|
|
4188
3765
|
const cacheKey = `${request.chainId}:${request.pointTokenAddress.toLowerCase()}`;
|
|
4189
3766
|
if (cacheTtl > 0) {
|
|
4190
3767
|
const cached = cache.get(cacheKey);
|
|
4191
3768
|
if (cached && cached.expiresAt > now()) {
|
|
4192
|
-
return {
|
|
3769
|
+
return {
|
|
3770
|
+
pools: cached.pools
|
|
3771
|
+
};
|
|
4193
3772
|
}
|
|
4194
3773
|
}
|
|
4195
|
-
const pools = await fetchPoolsFromSubgraph(
|
|
4196
|
-
fetchImpl,
|
|
4197
|
-
subgraphUrl,
|
|
4198
|
-
request.pointTokenAddress,
|
|
4199
|
-
reportError
|
|
4200
|
-
);
|
|
3774
|
+
const pools = await fetchPoolsFromSubgraph(fetchImpl, subgraphUrl, request.pointTokenAddress, reportError);
|
|
4201
3775
|
if (cacheTtl > 0) {
|
|
4202
3776
|
cache.set(cacheKey, {
|
|
4203
3777
|
expiresAt: now() + cacheTtl,
|
|
4204
3778
|
pools
|
|
4205
3779
|
});
|
|
4206
3780
|
}
|
|
4207
|
-
return {
|
|
3781
|
+
return {
|
|
3782
|
+
pools
|
|
3783
|
+
};
|
|
4208
3784
|
};
|
|
4209
3785
|
}
|
|
3786
|
+
__name(createSubgraphPoolsProvider, "createSubgraphPoolsProvider");
|
|
4210
3787
|
async function fetchPoolsFromSubgraph(fetchImpl, subgraphUrl, pointTokenAddress, reportError) {
|
|
4211
3788
|
let response;
|
|
4212
3789
|
try {
|
|
4213
3790
|
response = await fetchImpl(subgraphUrl, {
|
|
4214
3791
|
method: "POST",
|
|
4215
|
-
headers: {
|
|
3792
|
+
headers: {
|
|
3793
|
+
"Content-Type": "application/json"
|
|
3794
|
+
},
|
|
4216
3795
|
body: JSON.stringify({
|
|
4217
3796
|
query: POOL_QUERY,
|
|
4218
|
-
variables: {
|
|
3797
|
+
variables: {
|
|
3798
|
+
id: pointTokenAddress.toLowerCase()
|
|
3799
|
+
}
|
|
4219
3800
|
})
|
|
4220
3801
|
});
|
|
4221
3802
|
} catch (err) {
|
|
4222
3803
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
4223
|
-
console.warn(
|
|
4224
|
-
"[subgraphPoolsProvider] subgraph unreachable:",
|
|
4225
|
-
error.message
|
|
4226
|
-
);
|
|
3804
|
+
console.warn("[subgraphPoolsProvider] subgraph unreachable:", error.message);
|
|
4227
3805
|
reportError(error);
|
|
4228
3806
|
return [];
|
|
4229
3807
|
}
|
|
4230
3808
|
if (!response.ok) {
|
|
4231
|
-
const error = new Error(
|
|
4232
|
-
`subgraph returned HTTP ${response.status}`
|
|
4233
|
-
);
|
|
3809
|
+
const error = new Error(`subgraph returned HTTP ${response.status}`);
|
|
4234
3810
|
console.warn(`[subgraphPoolsProvider] ${error.message}`);
|
|
4235
3811
|
reportError(error);
|
|
4236
3812
|
return [];
|
|
@@ -4240,10 +3816,7 @@ async function fetchPoolsFromSubgraph(fetchImpl, subgraphUrl, pointTokenAddress,
|
|
|
4240
3816
|
json = await response.json();
|
|
4241
3817
|
} catch (err) {
|
|
4242
3818
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
4243
|
-
console.warn(
|
|
4244
|
-
"[subgraphPoolsProvider] subgraph returned non-JSON:",
|
|
4245
|
-
error.message
|
|
4246
|
-
);
|
|
3819
|
+
console.warn("[subgraphPoolsProvider] subgraph returned non-JSON:", error.message);
|
|
4247
3820
|
reportError(error);
|
|
4248
3821
|
return [];
|
|
4249
3822
|
}
|
|
@@ -4258,37 +3831,40 @@ async function fetchPoolsFromSubgraph(fetchImpl, subgraphUrl, pointTokenAddress,
|
|
|
4258
3831
|
return [];
|
|
4259
3832
|
}
|
|
4260
3833
|
const { pool } = token;
|
|
4261
|
-
if (!(0,
|
|
4262
|
-
const error = new Error(
|
|
4263
|
-
"[PAFI] SubgraphPoolsProvider: invalid token address in response"
|
|
4264
|
-
);
|
|
3834
|
+
if (!(0, import_viem12.isAddress)(pool.token0.id) || !(0, import_viem12.isAddress)(pool.token1.id)) {
|
|
3835
|
+
const error = new Error("[PAFI] SubgraphPoolsProvider: invalid token address in response");
|
|
4265
3836
|
console.error(error.message, "\u2014 skipping pool");
|
|
4266
3837
|
reportError(error);
|
|
4267
3838
|
return [];
|
|
4268
3839
|
}
|
|
4269
3840
|
const feeNum = Number(pool.feeTier);
|
|
4270
3841
|
if (!Number.isInteger(feeNum) || feeNum < 0 || feeNum >= MAX_REASONABLE_FEE_TIER) {
|
|
4271
|
-
const error = new Error(
|
|
4272
|
-
`[PAFI] SubgraphPoolsProvider: invalid feeTier value: ${pool.feeTier}`
|
|
4273
|
-
);
|
|
3842
|
+
const error = new Error(`[PAFI] SubgraphPoolsProvider: invalid feeTier value: ${pool.feeTier}`);
|
|
4274
3843
|
console.error(error.message, "\u2014 skipping pool");
|
|
4275
3844
|
reportError(error);
|
|
4276
3845
|
return [];
|
|
4277
3846
|
}
|
|
4278
|
-
const [token0, token1] = sortTokens(
|
|
4279
|
-
pool.token0.id,
|
|
4280
|
-
pool.token1.id
|
|
4281
|
-
);
|
|
3847
|
+
const [token0, token1] = sortTokens(pool.token0.id, pool.token1.id);
|
|
4282
3848
|
const poolKey = {
|
|
4283
3849
|
token0,
|
|
4284
3850
|
token1,
|
|
4285
3851
|
fee: feeNum
|
|
4286
3852
|
};
|
|
4287
|
-
return [
|
|
3853
|
+
return [
|
|
3854
|
+
poolKey
|
|
3855
|
+
];
|
|
4288
3856
|
}
|
|
3857
|
+
__name(fetchPoolsFromSubgraph, "fetchPoolsFromSubgraph");
|
|
4289
3858
|
function sortTokens(a, b) {
|
|
4290
|
-
return a.toLowerCase() < b.toLowerCase() ? [
|
|
3859
|
+
return a.toLowerCase() < b.toLowerCase() ? [
|
|
3860
|
+
a,
|
|
3861
|
+
b
|
|
3862
|
+
] : [
|
|
3863
|
+
b,
|
|
3864
|
+
a
|
|
3865
|
+
];
|
|
4291
3866
|
}
|
|
3867
|
+
__name(sortTokens, "sortTokens");
|
|
4292
3868
|
|
|
4293
3869
|
// src/pools/subgraphNativeUsdtQuoter.ts
|
|
4294
3870
|
var DEFAULT_CACHE_TTL_MS2 = 3e4;
|
|
@@ -4303,7 +3879,7 @@ var PRICE_QUERY = `
|
|
|
4303
3879
|
}
|
|
4304
3880
|
`;
|
|
4305
3881
|
function createSubgraphNativeUsdtQuoter(config = {}) {
|
|
4306
|
-
const subgraphUrl = config.subgraphUrl ??
|
|
3882
|
+
const subgraphUrl = config.subgraphUrl ?? import_core14.PAFI_SUBGRAPH_URL;
|
|
4307
3883
|
try {
|
|
4308
3884
|
const parsed = new URL(subgraphUrl);
|
|
4309
3885
|
if (process.env.NODE_ENV === "production" && parsed.protocol !== "https:") {
|
|
@@ -4311,9 +3887,7 @@ function createSubgraphNativeUsdtQuoter(config = {}) {
|
|
|
4311
3887
|
}
|
|
4312
3888
|
} catch (err) {
|
|
4313
3889
|
if (err instanceof TypeError) {
|
|
4314
|
-
throw new Error(
|
|
4315
|
-
`createSubgraphNativeUsdtQuoter: invalid subgraphUrl: ${subgraphUrl}`
|
|
4316
|
-
);
|
|
3890
|
+
throw new Error(`createSubgraphNativeUsdtQuoter: invalid subgraphUrl: ${subgraphUrl}`);
|
|
4317
3891
|
}
|
|
4318
3892
|
throw err;
|
|
4319
3893
|
}
|
|
@@ -4324,23 +3898,15 @@ function createSubgraphNativeUsdtQuoter(config = {}) {
|
|
|
4324
3898
|
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
4325
3899
|
const now = config.now ?? (() => Date.now());
|
|
4326
3900
|
if (!fetchImpl) {
|
|
4327
|
-
throw new Error(
|
|
4328
|
-
"createSubgraphNativeUsdtQuoter: no fetch implementation available \u2014 pass `fetchImpl` or run on Node 18+"
|
|
4329
|
-
);
|
|
3901
|
+
throw new Error("createSubgraphNativeUsdtQuoter: no fetch implementation available \u2014 pass `fetchImpl` or run on Node 18+");
|
|
4330
3902
|
}
|
|
4331
3903
|
let cached;
|
|
4332
3904
|
async function getUsdtPerNative() {
|
|
4333
3905
|
if (cacheTtl > 0 && cached && cached.expiresAt > now()) {
|
|
4334
3906
|
return cached.usdtPerNative;
|
|
4335
3907
|
}
|
|
4336
|
-
const price = await fetchEthPriceFromSubgraph(
|
|
4337
|
-
|
|
4338
|
-
subgraphUrl
|
|
4339
|
-
);
|
|
4340
|
-
const usdtPerNative = toUsdtPerNative(
|
|
4341
|
-
price ?? fallbackPrice,
|
|
4342
|
-
usdtDecimals
|
|
4343
|
-
);
|
|
3908
|
+
const price = await fetchEthPriceFromSubgraph(fetchImpl, subgraphUrl);
|
|
3909
|
+
const usdtPerNative = toUsdtPerNative(price ?? fallbackPrice, usdtDecimals);
|
|
4344
3910
|
if (cacheTtl > 0) {
|
|
4345
3911
|
cached = {
|
|
4346
3912
|
usdtPerNative,
|
|
@@ -4349,70 +3915,66 @@ function createSubgraphNativeUsdtQuoter(config = {}) {
|
|
|
4349
3915
|
}
|
|
4350
3916
|
return usdtPerNative;
|
|
4351
3917
|
}
|
|
3918
|
+
__name(getUsdtPerNative, "getUsdtPerNative");
|
|
4352
3919
|
return async (amountNative) => {
|
|
4353
3920
|
if (amountNative === 0n) return 0n;
|
|
4354
3921
|
const usdtPerNative = await getUsdtPerNative();
|
|
4355
3922
|
return amountNative * usdtPerNative / 10n ** BigInt(nativeDecimals);
|
|
4356
3923
|
};
|
|
4357
3924
|
}
|
|
3925
|
+
__name(createSubgraphNativeUsdtQuoter, "createSubgraphNativeUsdtQuoter");
|
|
4358
3926
|
async function fetchEthPriceFromSubgraph(fetchImpl, subgraphUrl) {
|
|
4359
3927
|
let response;
|
|
4360
3928
|
try {
|
|
4361
3929
|
response = await fetchImpl(subgraphUrl, {
|
|
4362
3930
|
method: "POST",
|
|
4363
|
-
headers: {
|
|
4364
|
-
|
|
3931
|
+
headers: {
|
|
3932
|
+
"Content-Type": "application/json"
|
|
3933
|
+
},
|
|
3934
|
+
body: JSON.stringify({
|
|
3935
|
+
query: PRICE_QUERY
|
|
3936
|
+
})
|
|
4365
3937
|
});
|
|
4366
3938
|
} catch (err) {
|
|
4367
|
-
console.warn(
|
|
4368
|
-
"[subgraphNativeUsdtQuoter] subgraph unreachable:",
|
|
4369
|
-
err.message
|
|
4370
|
-
);
|
|
3939
|
+
console.warn("[subgraphNativeUsdtQuoter] subgraph unreachable:", err.message);
|
|
4371
3940
|
return null;
|
|
4372
3941
|
}
|
|
4373
3942
|
if (!response.ok) {
|
|
4374
|
-
console.warn(
|
|
4375
|
-
`[subgraphNativeUsdtQuoter] subgraph returned ${response.status}`
|
|
4376
|
-
);
|
|
3943
|
+
console.warn(`[subgraphNativeUsdtQuoter] subgraph returned ${response.status}`);
|
|
4377
3944
|
return null;
|
|
4378
3945
|
}
|
|
4379
3946
|
const json = await response.json();
|
|
4380
3947
|
if (json.errors && json.errors.length > 0) {
|
|
4381
|
-
console.warn(
|
|
4382
|
-
"[subgraphNativeUsdtQuoter] subgraph errors:",
|
|
4383
|
-
json.errors.map((e) => e.message).join("; ")
|
|
4384
|
-
);
|
|
3948
|
+
console.warn("[subgraphNativeUsdtQuoter] subgraph errors:", json.errors.map((e) => e.message).join("; "));
|
|
4385
3949
|
return null;
|
|
4386
3950
|
}
|
|
4387
3951
|
const raw = json.data?.bundle?.ethPriceUSD;
|
|
4388
3952
|
if (!raw) return null;
|
|
4389
3953
|
const parsed = Number(raw);
|
|
4390
3954
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
4391
|
-
console.warn(
|
|
4392
|
-
`[subgraphNativeUsdtQuoter] invalid ethPriceUSD from subgraph: ${raw}`
|
|
4393
|
-
);
|
|
3955
|
+
console.warn(`[subgraphNativeUsdtQuoter] invalid ethPriceUSD from subgraph: ${raw}`);
|
|
4394
3956
|
return null;
|
|
4395
3957
|
}
|
|
4396
3958
|
const MIN_REASONABLE_ETH_PRICE = 100;
|
|
4397
3959
|
const MAX_REASONABLE_ETH_PRICE = 1e5;
|
|
4398
3960
|
if (parsed < MIN_REASONABLE_ETH_PRICE || parsed > MAX_REASONABLE_ETH_PRICE) {
|
|
4399
|
-
console.warn(
|
|
4400
|
-
`[PAFI] SubgraphNativeUsdtQuoter: ETH/USD price ${parsed} is outside reasonable range. Using fallback.`
|
|
4401
|
-
);
|
|
3961
|
+
console.warn(`[PAFI] SubgraphNativeUsdtQuoter: ETH/USD price ${parsed} is outside reasonable range. Using fallback.`);
|
|
4402
3962
|
return null;
|
|
4403
3963
|
}
|
|
4404
3964
|
return parsed;
|
|
4405
3965
|
}
|
|
3966
|
+
__name(fetchEthPriceFromSubgraph, "fetchEthPriceFromSubgraph");
|
|
4406
3967
|
function toUsdtPerNative(priceFloat, usdtDecimals) {
|
|
4407
3968
|
const fixed = priceFloat.toFixed(usdtDecimals);
|
|
4408
3969
|
const [whole, fraction = ""] = fixed.split(".");
|
|
4409
3970
|
const padded = (fraction + "0".repeat(usdtDecimals)).slice(0, usdtDecimals);
|
|
4410
3971
|
return BigInt(whole + padded);
|
|
4411
3972
|
}
|
|
3973
|
+
__name(toUsdtPerNative, "toUsdtPerNative");
|
|
4412
3974
|
|
|
4413
3975
|
// src/pools/nativePtQuoter.ts
|
|
4414
|
-
var
|
|
4415
|
-
var CHAINLINK_ABI = (0,
|
|
3976
|
+
var import_viem13 = require("viem");
|
|
3977
|
+
var CHAINLINK_ABI = (0, import_viem13.parseAbi)([
|
|
4416
3978
|
"function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)"
|
|
4417
3979
|
]);
|
|
4418
3980
|
var CHAINLINK_MAX_AGE_S = 3600n;
|
|
@@ -4429,18 +3991,7 @@ var POOL_PRICE_QUERY = `
|
|
|
4429
3991
|
}
|
|
4430
3992
|
`;
|
|
4431
3993
|
function createNativePtQuoter(config) {
|
|
4432
|
-
const {
|
|
4433
|
-
provider,
|
|
4434
|
-
pointTokenAddress,
|
|
4435
|
-
chainlinkFeedAddress = "0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70",
|
|
4436
|
-
subgraphUrl = import_core15.PAFI_SUBGRAPH_URL,
|
|
4437
|
-
cacheTtlMs = 3e4,
|
|
4438
|
-
fallbackEthPriceUsd = 3e3,
|
|
4439
|
-
fallbackPtPriceUsdt = 0.1,
|
|
4440
|
-
failClosed = false,
|
|
4441
|
-
fetchImpl = globalThis.fetch,
|
|
4442
|
-
now = () => Date.now()
|
|
4443
|
-
} = config;
|
|
3994
|
+
const { provider, pointTokenAddress, chainlinkFeedAddress = "0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70", subgraphUrl = import_core14.PAFI_SUBGRAPH_URL, cacheTtlMs = 3e4, fallbackEthPriceUsd = 3e3, fallbackPtPriceUsdt = 0.1, failClosed = false, fetchImpl = globalThis.fetch, now = /* @__PURE__ */ __name(() => Date.now(), "now") } = config;
|
|
4444
3995
|
let ethPriceCache;
|
|
4445
3996
|
let ptPriceCache;
|
|
4446
3997
|
async function getEthPrice8dec() {
|
|
@@ -4459,28 +4010,34 @@ function createNativePtQuoter(config) {
|
|
|
4459
4010
|
if (ageS > CHAINLINK_MAX_AGE_S) {
|
|
4460
4011
|
throw new Error(`Chainlink: price stale by ${ageS}s`);
|
|
4461
4012
|
}
|
|
4462
|
-
ethPriceCache = {
|
|
4013
|
+
ethPriceCache = {
|
|
4014
|
+
value: answer,
|
|
4015
|
+
expiresAt: ts + cacheTtlMs
|
|
4016
|
+
};
|
|
4463
4017
|
return answer;
|
|
4464
4018
|
} catch (err) {
|
|
4465
4019
|
if (failClosed) {
|
|
4466
|
-
throw new Error(
|
|
4467
|
-
`[nativePtQuoter] Chainlink unavailable in fail-closed mode: ${err.message}`
|
|
4468
|
-
);
|
|
4020
|
+
throw new Error(`[nativePtQuoter] Chainlink unavailable in fail-closed mode: ${err.message}`);
|
|
4469
4021
|
}
|
|
4470
4022
|
console.warn("[nativePtQuoter] Chainlink unavailable, using fallback:", err.message);
|
|
4471
4023
|
return BigInt(Math.round(fallbackEthPriceUsd * 1e8));
|
|
4472
4024
|
}
|
|
4473
4025
|
}
|
|
4026
|
+
__name(getEthPrice8dec, "getEthPrice8dec");
|
|
4474
4027
|
async function getPtPerUsdt18dec() {
|
|
4475
4028
|
const ts = now();
|
|
4476
4029
|
if (ptPriceCache && ptPriceCache.expiresAt > ts) return ptPriceCache.value;
|
|
4477
4030
|
try {
|
|
4478
4031
|
const response = await fetchImpl(subgraphUrl, {
|
|
4479
4032
|
method: "POST",
|
|
4480
|
-
headers: {
|
|
4033
|
+
headers: {
|
|
4034
|
+
"Content-Type": "application/json"
|
|
4035
|
+
},
|
|
4481
4036
|
body: JSON.stringify({
|
|
4482
4037
|
query: POOL_PRICE_QUERY,
|
|
4483
|
-
variables: {
|
|
4038
|
+
variables: {
|
|
4039
|
+
id: pointTokenAddress.toLowerCase()
|
|
4040
|
+
}
|
|
4484
4041
|
})
|
|
4485
4042
|
});
|
|
4486
4043
|
if (!response.ok) throw new Error(`subgraph HTTP ${response.status}`);
|
|
@@ -4496,19 +4053,21 @@ function createNativePtQuoter(config) {
|
|
|
4496
4053
|
const raw = parseBigDecimalTo18(ptPerUsdtStr);
|
|
4497
4054
|
if (raw === 0n) throw new Error(`pool price parsed to zero: ${ptPerUsdtStr}`);
|
|
4498
4055
|
const value = 10n ** 24n / raw;
|
|
4499
|
-
ptPriceCache = {
|
|
4056
|
+
ptPriceCache = {
|
|
4057
|
+
value,
|
|
4058
|
+
expiresAt: ts + cacheTtlMs
|
|
4059
|
+
};
|
|
4500
4060
|
return value;
|
|
4501
4061
|
} catch (err) {
|
|
4502
4062
|
if (failClosed) {
|
|
4503
|
-
throw new Error(
|
|
4504
|
-
`[nativePtQuoter] subgraph miss for ${pointTokenAddress} in fail-closed mode: ${err.message}`
|
|
4505
|
-
);
|
|
4063
|
+
throw new Error(`[nativePtQuoter] subgraph miss for ${pointTokenAddress} in fail-closed mode: ${err.message}`);
|
|
4506
4064
|
}
|
|
4507
4065
|
console.warn("[nativePtQuoter] subgraph unavailable, using fallback:", err.message);
|
|
4508
4066
|
const ptPerUsdtHuman = 1 / fallbackPtPriceUsdt;
|
|
4509
4067
|
return parseBigDecimalTo18(ptPerUsdtHuman.toFixed(18));
|
|
4510
4068
|
}
|
|
4511
4069
|
}
|
|
4070
|
+
__name(getPtPerUsdt18dec, "getPtPerUsdt18dec");
|
|
4512
4071
|
return async (amountNative) => {
|
|
4513
4072
|
if (amountNative === 0n) return 0n;
|
|
4514
4073
|
const [ethPrice8dec, ptPerUsdt18dec] = await Promise.all([
|
|
@@ -4518,28 +4077,39 @@ function createNativePtQuoter(config) {
|
|
|
4518
4077
|
return amountNative * ethPrice8dec * ptPerUsdt18dec / 10n ** 26n;
|
|
4519
4078
|
};
|
|
4520
4079
|
}
|
|
4080
|
+
__name(createNativePtQuoter, "createNativePtQuoter");
|
|
4521
4081
|
function parseBigDecimalTo18(s) {
|
|
4522
4082
|
const SCALE = 18;
|
|
4523
4083
|
const [whole = "0", frac = ""] = s.split(".");
|
|
4524
4084
|
const padded = (frac + "0".repeat(SCALE)).slice(0, SCALE);
|
|
4525
4085
|
return BigInt(whole + padded);
|
|
4526
4086
|
}
|
|
4087
|
+
__name(parseBigDecimalTo18, "parseBigDecimalTo18");
|
|
4527
4088
|
|
|
4528
4089
|
// src/pafi-backend/client.ts
|
|
4529
|
-
var
|
|
4090
|
+
var import_core15 = require("@pafi-dev/core");
|
|
4530
4091
|
function extractPafiErrorFields(json, status) {
|
|
4531
4092
|
const inner = typeof json.error === "object" && json.error !== null ? json.error : null;
|
|
4532
4093
|
const code = inner?.code ?? json.code ?? "INTERNAL_ERROR";
|
|
4533
4094
|
const message = inner?.message ?? json.message ?? `HTTP ${status}`;
|
|
4534
|
-
return {
|
|
4095
|
+
return {
|
|
4096
|
+
code,
|
|
4097
|
+
message
|
|
4098
|
+
};
|
|
4535
4099
|
}
|
|
4100
|
+
__name(extractPafiErrorFields, "extractPafiErrorFields");
|
|
4536
4101
|
function serializeBigInt(_key, value) {
|
|
4537
4102
|
return typeof value === "bigint" ? value.toString(10) : value;
|
|
4538
4103
|
}
|
|
4104
|
+
__name(serializeBigInt, "serializeBigInt");
|
|
4539
4105
|
function sleep(ms) {
|
|
4540
4106
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4541
4107
|
}
|
|
4108
|
+
__name(sleep, "sleep");
|
|
4542
4109
|
var PafiBackendClient = class {
|
|
4110
|
+
static {
|
|
4111
|
+
__name(this, "PafiBackendClient");
|
|
4112
|
+
}
|
|
4543
4113
|
config;
|
|
4544
4114
|
baseUrl;
|
|
4545
4115
|
constructor(config) {
|
|
@@ -4547,7 +4117,7 @@ var PafiBackendClient = class {
|
|
|
4547
4117
|
if (!config.issuerId) throw new Error("PafiBackendClient: issuerId is required");
|
|
4548
4118
|
if (!config.apiKey) throw new Error("PafiBackendClient: apiKey is required");
|
|
4549
4119
|
this.config = config;
|
|
4550
|
-
this.baseUrl = (0,
|
|
4120
|
+
this.baseUrl = (0, import_core15.getPafiServiceUrls)(config.chainId, {
|
|
4551
4121
|
sponsorRelayer: config.baseUrl
|
|
4552
4122
|
}).sponsorRelayer;
|
|
4553
4123
|
}
|
|
@@ -4577,11 +4147,11 @@ var PafiBackendClient = class {
|
|
|
4577
4147
|
throw lastError;
|
|
4578
4148
|
}
|
|
4579
4149
|
/**
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4150
|
+
* Fetch ERC-4337 UserOp receipt via PAFI's authenticated bundler proxy.
|
|
4151
|
+
* Returns `null` when the bundler hasn't seen the userOp yet — caller
|
|
4152
|
+
* should keep polling. Used by status endpoints to short-circuit the
|
|
4153
|
+
* on-chain indexer when several PENDING locks share the same amount.
|
|
4154
|
+
*/
|
|
4585
4155
|
async getUserOpReceipt(userOpHash) {
|
|
4586
4156
|
const fetchFn = this.config.fetchImpl ?? fetch;
|
|
4587
4157
|
const url = `${this.baseUrl}/bundler/receipt`;
|
|
@@ -4594,14 +4164,12 @@ var PafiBackendClient = class {
|
|
|
4594
4164
|
Authorization: `Bearer ${this.config.apiKey}`,
|
|
4595
4165
|
"X-Issuer-Id": this.config.issuerId
|
|
4596
4166
|
},
|
|
4597
|
-
body: JSON.stringify({
|
|
4167
|
+
body: JSON.stringify({
|
|
4168
|
+
userOpHash
|
|
4169
|
+
})
|
|
4598
4170
|
});
|
|
4599
4171
|
} catch (err) {
|
|
4600
|
-
throw new PafiBackendError(
|
|
4601
|
-
"NETWORK_ERROR",
|
|
4602
|
-
`Network error: ${err instanceof Error ? err.message : String(err)}`,
|
|
4603
|
-
0
|
|
4604
|
-
);
|
|
4172
|
+
throw new PafiBackendError("NETWORK_ERROR", `Network error: ${err instanceof Error ? err.message : String(err)}`, 0);
|
|
4605
4173
|
}
|
|
4606
4174
|
const text = await response.text();
|
|
4607
4175
|
let json = {};
|
|
@@ -4635,11 +4203,7 @@ var PafiBackendClient = class {
|
|
|
4635
4203
|
body: JSON.stringify(request)
|
|
4636
4204
|
});
|
|
4637
4205
|
} catch (err) {
|
|
4638
|
-
throw new PafiBackendError(
|
|
4639
|
-
"NETWORK_ERROR",
|
|
4640
|
-
`Network error: ${err instanceof Error ? err.message : String(err)}`,
|
|
4641
|
-
0
|
|
4642
|
-
);
|
|
4206
|
+
throw new PafiBackendError("NETWORK_ERROR", `Network error: ${err instanceof Error ? err.message : String(err)}`, 0);
|
|
4643
4207
|
}
|
|
4644
4208
|
const text = await response.text();
|
|
4645
4209
|
let json = {};
|
|
@@ -4651,7 +4215,9 @@ var PafiBackendClient = class {
|
|
|
4651
4215
|
const { code, message } = extractPafiErrorFields(json, response.status);
|
|
4652
4216
|
throw new PafiBackendError(code, message, response.status, json);
|
|
4653
4217
|
}
|
|
4654
|
-
return {
|
|
4218
|
+
return {
|
|
4219
|
+
userOpHash: json.userOpHash
|
|
4220
|
+
};
|
|
4655
4221
|
}
|
|
4656
4222
|
async _doRequest(request) {
|
|
4657
4223
|
const fetchFn = this.config.fetchImpl ?? fetch;
|
|
@@ -4669,11 +4235,7 @@ var PafiBackendClient = class {
|
|
|
4669
4235
|
body
|
|
4670
4236
|
});
|
|
4671
4237
|
} catch (err) {
|
|
4672
|
-
throw new PafiBackendError(
|
|
4673
|
-
"NETWORK_ERROR",
|
|
4674
|
-
`Network error: ${err instanceof Error ? err.message : String(err)}`,
|
|
4675
|
-
0
|
|
4676
|
-
);
|
|
4238
|
+
throw new PafiBackendError("NETWORK_ERROR", `Network error: ${err instanceof Error ? err.message : String(err)}`, 0);
|
|
4677
4239
|
}
|
|
4678
4240
|
const text = await response.text();
|
|
4679
4241
|
let json = {};
|
|
@@ -4694,9 +4256,7 @@ var PafiBackendClient = class {
|
|
|
4694
4256
|
return {
|
|
4695
4257
|
paymaster: json.paymaster,
|
|
4696
4258
|
paymasterData: json.paymasterData,
|
|
4697
|
-
paymasterVerificationGasLimit: BigInt(
|
|
4698
|
-
json.paymasterVerificationGasLimit
|
|
4699
|
-
),
|
|
4259
|
+
paymasterVerificationGasLimit: BigInt(json.paymasterVerificationGasLimit),
|
|
4700
4260
|
paymasterPostOpGasLimit: BigInt(json.paymasterPostOpGasLimit),
|
|
4701
4261
|
callGasLimit: json.callGasLimit != null ? BigInt(json.callGasLimit) : void 0,
|
|
4702
4262
|
verificationGasLimit: json.verificationGasLimit != null ? BigInt(json.verificationGasLimit) : void 0,
|
|
@@ -4709,8 +4269,8 @@ var PafiBackendClient = class {
|
|
|
4709
4269
|
};
|
|
4710
4270
|
|
|
4711
4271
|
// src/config.ts
|
|
4712
|
-
var
|
|
4713
|
-
var
|
|
4272
|
+
var import_viem14 = require("viem");
|
|
4273
|
+
var import_core18 = require("@pafi-dev/core");
|
|
4714
4274
|
|
|
4715
4275
|
// src/redemption/evaluator.ts
|
|
4716
4276
|
var SECONDS_PER_DAY = 24 * 60 * 60;
|
|
@@ -4720,10 +4280,7 @@ function evaluateRedemption(input) {
|
|
|
4720
4280
|
const cooldownUntilUnixSec = history.lastRedeemedAtUnixSec !== null ? history.lastRedeemedAtUnixSec + policy.cooldownSec : null;
|
|
4721
4281
|
const inCooldown = cooldownUntilUnixSec !== null && cooldownUntilUnixSec > nowUnixSec;
|
|
4722
4282
|
const activeBlackout = findActiveBlackout(policy.blackoutWindows, nowUnixSec);
|
|
4723
|
-
const nextBlackoutEnd = nextBlackoutEndAfter(
|
|
4724
|
-
policy.blackoutWindows,
|
|
4725
|
-
nowUnixSec
|
|
4726
|
-
);
|
|
4283
|
+
const nextBlackoutEnd = nextBlackoutEndAfter(policy.blackoutWindows, nowUnixSec);
|
|
4727
4284
|
let availableAmountPt = 0n;
|
|
4728
4285
|
if (!inCooldown && !activeBlackout) {
|
|
4729
4286
|
const headroom = dailyRemaining < policy.perTxMaxPt ? dailyRemaining : policy.perTxMaxPt;
|
|
@@ -4740,7 +4297,11 @@ function evaluateRedemption(input) {
|
|
|
4740
4297
|
policySource
|
|
4741
4298
|
};
|
|
4742
4299
|
if (amountPt <= 0n) {
|
|
4743
|
-
return {
|
|
4300
|
+
return {
|
|
4301
|
+
allowed: false,
|
|
4302
|
+
preview,
|
|
4303
|
+
denial: rejectAmountBelowMin(policy)
|
|
4304
|
+
};
|
|
4744
4305
|
}
|
|
4745
4306
|
const denial = firstDenial({
|
|
4746
4307
|
amountPt,
|
|
@@ -4750,25 +4311,29 @@ function evaluateRedemption(input) {
|
|
|
4750
4311
|
cooldownUntilUnixSec,
|
|
4751
4312
|
activeBlackout
|
|
4752
4313
|
});
|
|
4753
|
-
if (denial) return {
|
|
4754
|
-
|
|
4314
|
+
if (denial) return {
|
|
4315
|
+
allowed: false,
|
|
4316
|
+
denial,
|
|
4317
|
+
preview
|
|
4318
|
+
};
|
|
4319
|
+
return {
|
|
4320
|
+
allowed: true,
|
|
4321
|
+
preview
|
|
4322
|
+
};
|
|
4755
4323
|
}
|
|
4324
|
+
__name(evaluateRedemption, "evaluateRedemption");
|
|
4756
4325
|
function firstDenial(args) {
|
|
4757
4326
|
const { amountPt, policy, dailyRemaining, inCooldown, cooldownUntilUnixSec, activeBlackout } = args;
|
|
4758
4327
|
if (activeBlackout) {
|
|
4759
4328
|
return {
|
|
4760
4329
|
code: "BLACKOUT_WINDOW",
|
|
4761
|
-
message: `Redemption is blocked until ${new Date(
|
|
4762
|
-
activeBlackout.endUnixSec * 1e3
|
|
4763
|
-
).toISOString()}${activeBlackout.reason ? ` (${activeBlackout.reason})` : ""}`
|
|
4330
|
+
message: `Redemption is blocked until ${new Date(activeBlackout.endUnixSec * 1e3).toISOString()}${activeBlackout.reason ? ` (${activeBlackout.reason})` : ""}`
|
|
4764
4331
|
};
|
|
4765
4332
|
}
|
|
4766
4333
|
if (inCooldown && cooldownUntilUnixSec !== null) {
|
|
4767
4334
|
return {
|
|
4768
4335
|
code: "COOLDOWN_ACTIVE",
|
|
4769
|
-
message: `Cooldown active until ${new Date(
|
|
4770
|
-
cooldownUntilUnixSec * 1e3
|
|
4771
|
-
).toISOString()}`
|
|
4336
|
+
message: `Cooldown active until ${new Date(cooldownUntilUnixSec * 1e3).toISOString()}`
|
|
4772
4337
|
};
|
|
4773
4338
|
}
|
|
4774
4339
|
if (amountPt < policy.perTxMinPt) {
|
|
@@ -4791,18 +4356,21 @@ function firstDenial(args) {
|
|
|
4791
4356
|
}
|
|
4792
4357
|
return null;
|
|
4793
4358
|
}
|
|
4359
|
+
__name(firstDenial, "firstDenial");
|
|
4794
4360
|
function rejectAmountBelowMin(policy) {
|
|
4795
4361
|
return {
|
|
4796
4362
|
code: "AMOUNT_BELOW_MIN",
|
|
4797
4363
|
message: `amount must be >= ${policy.perTxMinPt}`
|
|
4798
4364
|
};
|
|
4799
4365
|
}
|
|
4366
|
+
__name(rejectAmountBelowMin, "rejectAmountBelowMin");
|
|
4800
4367
|
function findActiveBlackout(windows, nowUnixSec) {
|
|
4801
4368
|
for (const w of windows) {
|
|
4802
4369
|
if (w.startUnixSec <= nowUnixSec && nowUnixSec < w.endUnixSec) return w;
|
|
4803
4370
|
}
|
|
4804
4371
|
return null;
|
|
4805
4372
|
}
|
|
4373
|
+
__name(findActiveBlackout, "findActiveBlackout");
|
|
4806
4374
|
function nextBlackoutEndAfter(windows, nowUnixSec) {
|
|
4807
4375
|
let earliest = null;
|
|
4808
4376
|
for (const w of windows) {
|
|
@@ -4812,22 +4380,26 @@ function nextBlackoutEndAfter(windows, nowUnixSec) {
|
|
|
4812
4380
|
}
|
|
4813
4381
|
return earliest;
|
|
4814
4382
|
}
|
|
4383
|
+
__name(nextBlackoutEndAfter, "nextBlackoutEndAfter");
|
|
4815
4384
|
var REDEMPTION_HISTORY_WINDOW_SEC = SECONDS_PER_DAY;
|
|
4816
4385
|
|
|
4817
4386
|
// src/redemption/policyProvider.ts
|
|
4818
|
-
var
|
|
4387
|
+
var import_core17 = require("@pafi-dev/core");
|
|
4819
4388
|
|
|
4820
4389
|
// src/redemption/settlementClient.ts
|
|
4821
|
-
var
|
|
4390
|
+
var import_core16 = require("@pafi-dev/core");
|
|
4822
4391
|
var DEFAULT_TIMEOUT_MS = 1e3;
|
|
4823
4392
|
var SettlementClient = class {
|
|
4393
|
+
static {
|
|
4394
|
+
__name(this, "SettlementClient");
|
|
4395
|
+
}
|
|
4824
4396
|
config;
|
|
4825
4397
|
constructor(config) {
|
|
4826
4398
|
if (!config.chainId) throw new Error("SettlementClient: chainId is required");
|
|
4827
4399
|
if (!config.issuerId) throw new Error("SettlementClient: issuerId is required");
|
|
4828
4400
|
if (!config.apiKey) throw new Error("SettlementClient: apiKey is required");
|
|
4829
4401
|
this.config = {
|
|
4830
|
-
baseUrl: (0,
|
|
4402
|
+
baseUrl: (0, import_core16.getPafiServiceUrls)(config.chainId, {
|
|
4831
4403
|
issuerApi: config.baseUrl
|
|
4832
4404
|
}).issuerApi.replace(/\/+$/, ""),
|
|
4833
4405
|
issuerId: config.issuerId,
|
|
@@ -4840,10 +4412,7 @@ var SettlementClient = class {
|
|
|
4840
4412
|
const fetchFn = this.config.fetchImpl ?? fetch;
|
|
4841
4413
|
const url = `${this.config.baseUrl}/issuers/${encodeURIComponent(this.config.issuerId)}/redemption-policy`;
|
|
4842
4414
|
const controller = new AbortController();
|
|
4843
|
-
const timer = setTimeout(
|
|
4844
|
-
() => controller.abort(),
|
|
4845
|
-
this.config.fetchTimeoutMs
|
|
4846
|
-
);
|
|
4415
|
+
const timer = setTimeout(() => controller.abort(), this.config.fetchTimeoutMs);
|
|
4847
4416
|
let response;
|
|
4848
4417
|
try {
|
|
4849
4418
|
response = await fetchFn(url, {
|
|
@@ -4857,30 +4426,56 @@ var SettlementClient = class {
|
|
|
4857
4426
|
});
|
|
4858
4427
|
} catch (err) {
|
|
4859
4428
|
const isAbort = err instanceof Error && (err.name === "AbortError" || /aborted|timeout/i.test(err.message ?? ""));
|
|
4860
|
-
return {
|
|
4429
|
+
return {
|
|
4430
|
+
ok: false,
|
|
4431
|
+
reason: isAbort ? "TIMEOUT" : "NETWORK"
|
|
4432
|
+
};
|
|
4861
4433
|
} finally {
|
|
4862
4434
|
clearTimeout(timer);
|
|
4863
4435
|
}
|
|
4864
4436
|
if (response.status === 404) {
|
|
4865
|
-
return {
|
|
4437
|
+
return {
|
|
4438
|
+
ok: false,
|
|
4439
|
+
reason: "NOT_FOUND",
|
|
4440
|
+
status: 404
|
|
4441
|
+
};
|
|
4866
4442
|
}
|
|
4867
4443
|
if (response.status === 401 || response.status === 403) {
|
|
4868
|
-
return {
|
|
4444
|
+
return {
|
|
4445
|
+
ok: false,
|
|
4446
|
+
reason: "UNAUTHORIZED",
|
|
4447
|
+
status: response.status
|
|
4448
|
+
};
|
|
4869
4449
|
}
|
|
4870
4450
|
if (!response.ok) {
|
|
4871
|
-
return {
|
|
4451
|
+
return {
|
|
4452
|
+
ok: false,
|
|
4453
|
+
reason: "SERVER_ERROR",
|
|
4454
|
+
status: response.status
|
|
4455
|
+
};
|
|
4872
4456
|
}
|
|
4873
4457
|
let raw;
|
|
4874
4458
|
try {
|
|
4875
4459
|
raw = await response.json();
|
|
4876
4460
|
} catch {
|
|
4877
|
-
return {
|
|
4461
|
+
return {
|
|
4462
|
+
ok: false,
|
|
4463
|
+
reason: "INVALID_RESPONSE",
|
|
4464
|
+
status: response.status
|
|
4465
|
+
};
|
|
4878
4466
|
}
|
|
4879
4467
|
const parsed = parsePolicyDto(raw);
|
|
4880
4468
|
if (!parsed) {
|
|
4881
|
-
return {
|
|
4469
|
+
return {
|
|
4470
|
+
ok: false,
|
|
4471
|
+
reason: "INVALID_RESPONSE",
|
|
4472
|
+
status: response.status
|
|
4473
|
+
};
|
|
4882
4474
|
}
|
|
4883
|
-
return {
|
|
4475
|
+
return {
|
|
4476
|
+
ok: true,
|
|
4477
|
+
policy: parsed
|
|
4478
|
+
};
|
|
4884
4479
|
}
|
|
4885
4480
|
};
|
|
4886
4481
|
function parsePolicyDto(raw) {
|
|
@@ -4903,6 +4498,7 @@ function parsePolicyDto(raw) {
|
|
|
4903
4498
|
return null;
|
|
4904
4499
|
}
|
|
4905
4500
|
}
|
|
4501
|
+
__name(parsePolicyDto, "parsePolicyDto");
|
|
4906
4502
|
function normalizeBlackout(raw) {
|
|
4907
4503
|
if (!raw || typeof raw !== "object") return null;
|
|
4908
4504
|
const win = raw;
|
|
@@ -4915,6 +4511,7 @@ function normalizeBlackout(raw) {
|
|
|
4915
4511
|
reason: typeof win.reason === "string" ? win.reason : void 0
|
|
4916
4512
|
};
|
|
4917
4513
|
}
|
|
4514
|
+
__name(normalizeBlackout, "normalizeBlackout");
|
|
4918
4515
|
|
|
4919
4516
|
// src/redemption/defaults.ts
|
|
4920
4517
|
var PT_DECIMALS = 10n ** 18n;
|
|
@@ -4928,23 +4525,34 @@ var DEFAULT_REDEMPTION_POLICY = {
|
|
|
4928
4525
|
version: "default-v1"
|
|
4929
4526
|
};
|
|
4930
4527
|
function defaultPolicyFor(issuerId) {
|
|
4931
|
-
return {
|
|
4528
|
+
return {
|
|
4529
|
+
...DEFAULT_REDEMPTION_POLICY,
|
|
4530
|
+
issuerId
|
|
4531
|
+
};
|
|
4932
4532
|
}
|
|
4533
|
+
__name(defaultPolicyFor, "defaultPolicyFor");
|
|
4933
4534
|
|
|
4934
4535
|
// src/redemption/policyProvider.ts
|
|
4935
4536
|
var DEFAULT_CACHE_TTL_MS3 = 5 * 60 * 1e3;
|
|
4936
|
-
var PolicyProviderUnavailableError = class extends
|
|
4537
|
+
var PolicyProviderUnavailableError = class extends import_core17.PafiSdkError {
|
|
4538
|
+
static {
|
|
4539
|
+
__name(this, "PolicyProviderUnavailableError");
|
|
4540
|
+
}
|
|
4937
4541
|
code = "POLICY_PROVIDER_UNAVAILABLE";
|
|
4938
4542
|
httpStatus = "service_unavailable";
|
|
4939
4543
|
details;
|
|
4940
4544
|
constructor(issuerId, reason) {
|
|
4941
|
-
super(
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4545
|
+
super(`Redemption policy provider unavailable for issuer ${issuerId}: ${reason}. Pre-flight redeem limit cannot be enforced \u2014 refusing to sign BurnRequest. Mobile FE: surface "try again shortly" and retry with backoff.`);
|
|
4546
|
+
this.details = {
|
|
4547
|
+
issuerId,
|
|
4548
|
+
reason
|
|
4549
|
+
};
|
|
4945
4550
|
}
|
|
4946
4551
|
};
|
|
4947
4552
|
var PolicyProvider = class {
|
|
4553
|
+
static {
|
|
4554
|
+
__name(this, "PolicyProvider");
|
|
4555
|
+
}
|
|
4948
4556
|
client;
|
|
4949
4557
|
issuerId;
|
|
4950
4558
|
cacheTtlMs;
|
|
@@ -4963,7 +4571,10 @@ var PolicyProvider = class {
|
|
|
4963
4571
|
}
|
|
4964
4572
|
async getPolicy() {
|
|
4965
4573
|
const fresh = this.readCache();
|
|
4966
|
-
if (fresh) return {
|
|
4574
|
+
if (fresh) return {
|
|
4575
|
+
policy: fresh,
|
|
4576
|
+
source: "cache"
|
|
4577
|
+
};
|
|
4967
4578
|
if (this.inflight) return this.inflight;
|
|
4968
4579
|
this.inflight = this.fetchAndStore().finally(() => {
|
|
4969
4580
|
this.inflight = null;
|
|
@@ -4989,19 +4600,22 @@ var PolicyProvider = class {
|
|
|
4989
4600
|
policy: result.policy,
|
|
4990
4601
|
expiresAtMs: this.now() + this.cacheTtlMs
|
|
4991
4602
|
};
|
|
4992
|
-
return {
|
|
4603
|
+
return {
|
|
4604
|
+
policy: result.policy,
|
|
4605
|
+
source: "settlement"
|
|
4606
|
+
};
|
|
4993
4607
|
}
|
|
4994
4608
|
const reason = "reason" in result && typeof result.reason === "string" ? result.reason : "unknown";
|
|
4995
4609
|
if (this.onFetchFailure === "permissive-default") {
|
|
4996
|
-
this.onWarning?.(
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
4610
|
+
this.onWarning?.("PolicyProvider: settlement-api unreachable, falling back to permissive default. Pre-flight redeem limit is DEGRADED until settlement-api recovers.", {
|
|
4611
|
+
event: "policy_provider_fallback",
|
|
4612
|
+
issuerId: this.issuerId,
|
|
4613
|
+
reason
|
|
4614
|
+
});
|
|
4615
|
+
return {
|
|
4616
|
+
policy: defaultPolicyFor(this.issuerId),
|
|
4617
|
+
source: "default"
|
|
4618
|
+
};
|
|
5005
4619
|
}
|
|
5006
4620
|
throw new PolicyProviderUnavailableError(this.issuerId, reason);
|
|
5007
4621
|
}
|
|
@@ -5009,6 +4623,9 @@ var PolicyProvider = class {
|
|
|
5009
4623
|
|
|
5010
4624
|
// src/redemption/service.ts
|
|
5011
4625
|
var RedemptionService = class {
|
|
4626
|
+
static {
|
|
4627
|
+
__name(this, "RedemptionService");
|
|
4628
|
+
}
|
|
5012
4629
|
policyProvider;
|
|
5013
4630
|
historyStore;
|
|
5014
4631
|
nowUnixSec;
|
|
@@ -5025,17 +4642,16 @@ var RedemptionService = class {
|
|
|
5025
4642
|
const { policy, source } = await this.policyProvider.getPolicy();
|
|
5026
4643
|
const now = this.nowUnixSec();
|
|
5027
4644
|
const [redeemedLast24hPt, lastRedeemedAtUnixSec] = await Promise.all([
|
|
5028
|
-
this.historyStore.sumRedeemedSince(
|
|
5029
|
-
user,
|
|
5030
|
-
now - REDEMPTION_HISTORY_WINDOW_SEC,
|
|
5031
|
-
pointTokenAddress
|
|
5032
|
-
),
|
|
4645
|
+
this.historyStore.sumRedeemedSince(user, now - REDEMPTION_HISTORY_WINDOW_SEC, pointTokenAddress),
|
|
5033
4646
|
this.historyStore.getLastRedeemedAtUnixSec(user, pointTokenAddress)
|
|
5034
4647
|
]);
|
|
5035
4648
|
return evaluateRedemption({
|
|
5036
4649
|
policy,
|
|
5037
4650
|
policySource: source,
|
|
5038
|
-
history: {
|
|
4651
|
+
history: {
|
|
4652
|
+
redeemedLast24hPt,
|
|
4653
|
+
lastRedeemedAtUnixSec
|
|
4654
|
+
},
|
|
5039
4655
|
amountPt,
|
|
5040
4656
|
nowUnixSec: now
|
|
5041
4657
|
});
|
|
@@ -5062,16 +4678,18 @@ async function createIssuerService(config) {
|
|
|
5062
4678
|
if (!config.auth?.domain) {
|
|
5063
4679
|
throw new Error("createIssuerService: auth.domain is required");
|
|
5064
4680
|
}
|
|
5065
|
-
const rawAddresses = config.pointTokenAddresses && config.pointTokenAddresses.length > 0 ? config.pointTokenAddresses : config.pointTokenAddress ? [
|
|
4681
|
+
const rawAddresses = config.pointTokenAddresses && config.pointTokenAddresses.length > 0 ? config.pointTokenAddresses : config.pointTokenAddress ? [
|
|
4682
|
+
config.pointTokenAddress
|
|
4683
|
+
] : [];
|
|
5066
4684
|
if (rawAddresses.length === 0) {
|
|
5067
|
-
throw new Error(
|
|
5068
|
-
"createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
|
|
5069
|
-
);
|
|
4685
|
+
throw new Error("createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required");
|
|
5070
4686
|
}
|
|
5071
|
-
const tokenAddresses = rawAddresses.map((a) => (0,
|
|
4687
|
+
const tokenAddresses = rawAddresses.map((a) => (0, import_viem14.getAddress)(a));
|
|
5072
4688
|
const ledger = config.ledger;
|
|
5073
4689
|
const sessionStore = config.sessionStore ?? new MemorySessionStore();
|
|
5074
|
-
const policy = config.policy ?? new DefaultPolicyEngine({
|
|
4690
|
+
const policy = config.policy ?? new DefaultPolicyEngine({
|
|
4691
|
+
ledger
|
|
4692
|
+
});
|
|
5075
4693
|
const authServiceConfig = {
|
|
5076
4694
|
sessionStore,
|
|
5077
4695
|
jwtSecret: config.auth.jwtSecret,
|
|
@@ -5093,15 +4711,13 @@ async function createIssuerService(config) {
|
|
|
5093
4711
|
provider: config.provider
|
|
5094
4712
|
});
|
|
5095
4713
|
}
|
|
5096
|
-
const sdkWrapperAddress = (0,
|
|
4714
|
+
const sdkWrapperAddress = (0, import_core18.getContractAddresses)(config.chainId).mintFeeWrapper;
|
|
5097
4715
|
const wrapperOverride = config.indexer?.mintFeeWrapperAddress;
|
|
5098
4716
|
const resolvedWrapperAddress = wrapperOverride !== void 0 ? wrapperOverride : sdkWrapperAddress;
|
|
5099
4717
|
const baseCursorStore = config.indexer?.cursorStore;
|
|
5100
4718
|
const sharedCursorWithMultipleTokens = baseCursorStore !== void 0 && typeof baseCursorStore.forKey !== "function" && tokenAddresses.length > 1;
|
|
5101
4719
|
if (sharedCursorWithMultipleTokens) {
|
|
5102
|
-
console.warn(
|
|
5103
|
-
`[@pafi-dev/issuer] cursorStore lacks forKey() and ${tokenAddresses.length} PointTokens are configured. All PointIndexers will share one cursor row, causing token-skipping. Implement IIndexerCursorStore.forKey to return per-token derived stores. This permissive path will be removed in a future major release.`
|
|
5104
|
-
);
|
|
4720
|
+
console.warn(`[@pafi-dev/issuer] cursorStore lacks forKey() and ${tokenAddresses.length} PointTokens are configured. All PointIndexers will share one cursor row, causing token-skipping. Implement IIndexerCursorStore.forKey to return per-token derived stores. This permissive path will be removed in a future major release.`);
|
|
5105
4721
|
}
|
|
5106
4722
|
const indexers = /* @__PURE__ */ new Map();
|
|
5107
4723
|
for (const tokenAddress of tokenAddresses) {
|
|
@@ -5117,9 +4733,7 @@ async function createIssuerService(config) {
|
|
|
5117
4733
|
indexerConfig.fromBlock = config.indexer.fromBlock;
|
|
5118
4734
|
}
|
|
5119
4735
|
if (baseCursorStore) {
|
|
5120
|
-
indexerConfig.cursorStore = typeof baseCursorStore.forKey === "function" ? baseCursorStore.forKey(
|
|
5121
|
-
`point-indexer:${tokenAddress.toLowerCase()}`
|
|
5122
|
-
) : baseCursorStore;
|
|
4736
|
+
indexerConfig.cursorStore = typeof baseCursorStore.forKey === "function" ? baseCursorStore.forKey(`point-indexer:${tokenAddress.toLowerCase()}`) : baseCursorStore;
|
|
5123
4737
|
}
|
|
5124
4738
|
if (config.indexer?.confirmations !== void 0) {
|
|
5125
4739
|
indexerConfig.confirmations = config.indexer.confirmations;
|
|
@@ -5132,9 +4746,9 @@ async function createIssuerService(config) {
|
|
|
5132
4746
|
}
|
|
5133
4747
|
indexers.set(tokenAddress, new PointIndexer(indexerConfig));
|
|
5134
4748
|
}
|
|
5135
|
-
const chainAddresses = (0,
|
|
4749
|
+
const chainAddresses = (0, import_core18.getContractAddresses)(config.chainId);
|
|
5136
4750
|
const resolvedContracts = {
|
|
5137
|
-
|
|
4751
|
+
kernel: chainAddresses.kernel,
|
|
5138
4752
|
usdt: chainAddresses.usdt,
|
|
5139
4753
|
issuerRegistry: chainAddresses.issuerRegistry,
|
|
5140
4754
|
mintingOracle: chainAddresses.mintingOracle,
|
|
@@ -5190,9 +4804,7 @@ async function createIssuerService(config) {
|
|
|
5190
4804
|
if (config.indexer?.autoStart) {
|
|
5191
4805
|
const lock = config.indexer.singletonLock;
|
|
5192
4806
|
if (!lock) {
|
|
5193
|
-
console.warn(
|
|
5194
|
-
"[@pafi-dev/issuer] indexer.autoStart=true without singletonLock \u2014 this is UNSAFE in multi-replica deployments. 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."
|
|
5195
|
-
);
|
|
4807
|
+
console.warn("[@pafi-dev/issuer] indexer.autoStart=true without singletonLock \u2014 this is UNSAFE in multi-replica deployments. 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.");
|
|
5196
4808
|
for (const idx of indexers.values()) {
|
|
5197
4809
|
idx.start();
|
|
5198
4810
|
}
|
|
@@ -5221,38 +4833,42 @@ async function createIssuerService(config) {
|
|
|
5221
4833
|
redemption
|
|
5222
4834
|
};
|
|
5223
4835
|
}
|
|
4836
|
+
__name(createIssuerService, "createIssuerService");
|
|
5224
4837
|
|
|
5225
4838
|
// src/issuer-state/validator.ts
|
|
5226
|
-
var
|
|
5227
|
-
var
|
|
4839
|
+
var import_viem15 = require("viem");
|
|
4840
|
+
var import_core19 = require("@pafi-dev/core");
|
|
5228
4841
|
var ISSUER_RECORD_TTL_MS = 1e4;
|
|
5229
4842
|
var IssuerStateValidator = class _IssuerStateValidator {
|
|
5230
|
-
|
|
5231
|
-
this
|
|
5232
|
-
this.registryAddress = registryAddress;
|
|
4843
|
+
static {
|
|
4844
|
+
__name(this, "IssuerStateValidator");
|
|
5233
4845
|
}
|
|
5234
4846
|
provider;
|
|
5235
4847
|
registryAddress;
|
|
5236
4848
|
pointTokenIssuerCache = /* @__PURE__ */ new Map();
|
|
5237
4849
|
stateCache = /* @__PURE__ */ new Map();
|
|
5238
4850
|
inflight = /* @__PURE__ */ new Map();
|
|
4851
|
+
constructor(provider, registryAddress) {
|
|
4852
|
+
this.provider = provider;
|
|
4853
|
+
this.registryAddress = registryAddress;
|
|
4854
|
+
}
|
|
5239
4855
|
/**
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
4856
|
+
* Convenience factory — reads `registryAddress` from the SDK
|
|
4857
|
+
* `CONTRACT_ADDRESSES` map for the given chain.
|
|
4858
|
+
*/
|
|
5243
4859
|
static forChain(provider, chainId) {
|
|
5244
|
-
const { issuerRegistry } = (0,
|
|
4860
|
+
const { issuerRegistry } = (0, import_core19.getContractAddresses)(chainId);
|
|
5245
4861
|
return new _IssuerStateValidator(provider, issuerRegistry);
|
|
5246
4862
|
}
|
|
5247
4863
|
/**
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
4864
|
+
* Invalidate cached state for one PointToken, or everything if omitted.
|
|
4865
|
+
* Call after admin txs that change registry or cap settings — closes
|
|
4866
|
+
* the split-brain window described
|
|
4867
|
+
* passive TTL. Idempotent: safe to call when no entry exists.
|
|
4868
|
+
*/
|
|
5253
4869
|
invalidate(pointToken) {
|
|
5254
4870
|
if (pointToken) {
|
|
5255
|
-
const key = (0,
|
|
4871
|
+
const key = (0, import_viem15.getAddress)(pointToken);
|
|
5256
4872
|
this.pointTokenIssuerCache.delete(key);
|
|
5257
4873
|
this.stateCache.delete(key);
|
|
5258
4874
|
this.inflight.delete(key);
|
|
@@ -5263,27 +4879,27 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
5263
4879
|
}
|
|
5264
4880
|
}
|
|
5265
4881
|
/**
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
4882
|
+
* Resolve `PointToken.issuer()` once per token and memoize.
|
|
4883
|
+
* The issuer field is set at `initialize()` and never changes.
|
|
4884
|
+
*/
|
|
5269
4885
|
async getIssuerAddressForPointToken(pointToken) {
|
|
5270
|
-
const key = (0,
|
|
4886
|
+
const key = (0, import_viem15.getAddress)(pointToken);
|
|
5271
4887
|
const cached = this.pointTokenIssuerCache.get(key);
|
|
5272
4888
|
if (cached) return cached;
|
|
5273
4889
|
const issuer = await this.provider.readContract({
|
|
5274
4890
|
address: key,
|
|
5275
|
-
abi:
|
|
4891
|
+
abi: import_core19.POINT_TOKEN_ABI,
|
|
5276
4892
|
functionName: "issuer"
|
|
5277
4893
|
});
|
|
5278
|
-
this.pointTokenIssuerCache.set(key, (0,
|
|
5279
|
-
return (0,
|
|
4894
|
+
this.pointTokenIssuerCache.set(key, (0, import_viem15.getAddress)(issuer));
|
|
4895
|
+
return (0, import_viem15.getAddress)(issuer);
|
|
5280
4896
|
}
|
|
5281
4897
|
/**
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
4898
|
+
* Read registry record + totalSupply, with 30s cache and in-flight
|
|
4899
|
+
* deduplication. Does NOT throw on inactive/missing — returns raw state.
|
|
4900
|
+
*/
|
|
5285
4901
|
async getIssuerState(pointToken) {
|
|
5286
|
-
const tokenAddr = (0,
|
|
4902
|
+
const tokenAddr = (0, import_viem15.getAddress)(pointToken);
|
|
5287
4903
|
const now = Date.now();
|
|
5288
4904
|
const cached = this.stateCache.get(tokenAddr);
|
|
5289
4905
|
if (cached && cached.expiresAt > now) return cached.value;
|
|
@@ -5302,49 +4918,41 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
5302
4918
|
return promise;
|
|
5303
4919
|
}
|
|
5304
4920
|
/**
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
4921
|
+
* Validate that `amount` PT can be minted on `pointToken` right now.
|
|
4922
|
+
*
|
|
4923
|
+
* Throws `IssuerStateError` with:
|
|
4924
|
+
* - `ISSUER_NOT_REGISTERED` — registry has no record for this issuer
|
|
4925
|
+
* - `ISSUER_INACTIVE` — issuer.active is false
|
|
4926
|
+
* - `MINT_CAP_EXCEEDED` — totalSupply + amount would exceed hardCap
|
|
4927
|
+
*
|
|
4928
|
+
* Returns the fetched state on success so callers can log without a
|
|
4929
|
+
* second RPC round-trip.
|
|
4930
|
+
*/
|
|
5315
4931
|
async preValidateMint(pointToken, amount) {
|
|
5316
4932
|
let state;
|
|
5317
4933
|
try {
|
|
5318
4934
|
state = await this.getIssuerState(pointToken);
|
|
5319
4935
|
} catch (err) {
|
|
5320
4936
|
if (err.message.includes("IssuerNotFound")) {
|
|
5321
|
-
throw new IssuerStateError(
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
{ pointToken }
|
|
5325
|
-
);
|
|
4937
|
+
throw new IssuerStateError("ISSUER_NOT_REGISTERED", `IssuerRegistry has no record for PointToken ${pointToken}`, {
|
|
4938
|
+
pointToken
|
|
4939
|
+
});
|
|
5326
4940
|
}
|
|
5327
4941
|
throw err;
|
|
5328
4942
|
}
|
|
5329
4943
|
const { issuer, equityCap, equitySupply, remaining } = state;
|
|
5330
4944
|
if (!issuer.active) {
|
|
5331
|
-
throw new IssuerStateError(
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
{ pointToken }
|
|
5335
|
-
);
|
|
4945
|
+
throw new IssuerStateError("ISSUER_INACTIVE", `Issuer "${issuer.name}" is deactivated on IssuerRegistry`, {
|
|
4946
|
+
pointToken
|
|
4947
|
+
});
|
|
5336
4948
|
}
|
|
5337
4949
|
if (equitySupply + amount > equityCap.hardCap) {
|
|
5338
|
-
throw new IssuerStateError(
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
equityMinted: equitySupply.toString(),
|
|
5345
|
-
remaining: remaining.toString()
|
|
5346
|
-
}
|
|
5347
|
-
);
|
|
4950
|
+
throw new IssuerStateError("MINT_CAP_EXCEEDED", `Requested ${amount} PT would exceed EQUITY mint cap. Cap=${equityCap.hardCap}, equityMinted=${equitySupply}, remaining=${remaining}`, {
|
|
4951
|
+
requested: amount.toString(),
|
|
4952
|
+
cap: equityCap.hardCap.toString(),
|
|
4953
|
+
equityMinted: equitySupply.toString(),
|
|
4954
|
+
remaining: remaining.toString()
|
|
4955
|
+
});
|
|
5348
4956
|
}
|
|
5349
4957
|
return state;
|
|
5350
4958
|
}
|
|
@@ -5352,9 +4960,11 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
5352
4960
|
const issuerAddr = await this.getIssuerAddressForPointToken(tokenAddr);
|
|
5353
4961
|
const issuerStruct = await this.provider.readContract({
|
|
5354
4962
|
address: this.registryAddress,
|
|
5355
|
-
abi:
|
|
4963
|
+
abi: import_core19.issuerRegistryAbi,
|
|
5356
4964
|
functionName: "getIssuer",
|
|
5357
|
-
args: [
|
|
4965
|
+
args: [
|
|
4966
|
+
issuerAddr
|
|
4967
|
+
]
|
|
5358
4968
|
});
|
|
5359
4969
|
const issuer = {
|
|
5360
4970
|
signerAddress: issuerStruct.signerAddress,
|
|
@@ -5365,7 +4975,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
5365
4975
|
};
|
|
5366
4976
|
const equitySupply = await this.provider.readContract({
|
|
5367
4977
|
address: tokenAddr,
|
|
5368
|
-
abi:
|
|
4978
|
+
abi: import_core19.POINT_TOKEN_ABI,
|
|
5369
4979
|
functionName: "equitySupply"
|
|
5370
4980
|
});
|
|
5371
4981
|
const hardCap = issuer.capitalBase * BigInt(issuer.basisPoints) / 10000n;
|
|
@@ -5386,6 +4996,9 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
5386
4996
|
|
|
5387
4997
|
// src/redemption/memoryHistoryStore.ts
|
|
5388
4998
|
var MemoryRedemptionHistoryStore = class {
|
|
4999
|
+
static {
|
|
5000
|
+
__name(this, "MemoryRedemptionHistoryStore");
|
|
5001
|
+
}
|
|
5389
5002
|
entries = [];
|
|
5390
5003
|
async sumRedeemedSince(user, sinceUnixSec, pointTokenAddress) {
|
|
5391
5004
|
const userKey = user.toLowerCase();
|
|
@@ -5421,7 +5034,7 @@ var MemoryRedemptionHistoryStore = class {
|
|
|
5421
5034
|
};
|
|
5422
5035
|
|
|
5423
5036
|
// src/index.ts
|
|
5424
|
-
var PAFI_ISSUER_SDK_VERSION = true ? "0.
|
|
5037
|
+
var PAFI_ISSUER_SDK_VERSION = true ? "0.40.0" : "dev";
|
|
5425
5038
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5426
5039
|
0 && (module.exports = {
|
|
5427
5040
|
AdapterMisconfiguredError,
|
|
@@ -5486,7 +5099,6 @@ var PAFI_ISSUER_SDK_VERSION = true ? "0.39.2" : "dev";
|
|
|
5486
5099
|
defaultPolicyFor,
|
|
5487
5100
|
evaluateRedemption,
|
|
5488
5101
|
handleClaimStatus,
|
|
5489
|
-
handleDelegateSubmit,
|
|
5490
5102
|
handleMobilePrepare,
|
|
5491
5103
|
handleMobileSubmit,
|
|
5492
5104
|
handleRedeemStatus,
|
|
@@ -5498,7 +5110,6 @@ var PAFI_ISSUER_SDK_VERSION = true ? "0.39.2" : "dev";
|
|
|
5498
5110
|
prepareMobileUserOp,
|
|
5499
5111
|
relayUserOp,
|
|
5500
5112
|
requestPaymaster,
|
|
5501
|
-
serializeEntryToJsonRpc
|
|
5502
|
-
serializeUserOpTypedData
|
|
5113
|
+
serializeEntryToJsonRpc
|
|
5503
5114
|
});
|
|
5504
5115
|
//# sourceMappingURL=index.cjs.map
|