@pafi-dev/issuer 0.24.1 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +155 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -7
- package/dist/index.d.ts +2 -7
- package/dist/index.js +159 -95
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2167,14 +2167,12 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
2167
2167
|
{ requested: pointToken }
|
|
2168
2168
|
);
|
|
2169
2169
|
}
|
|
2170
|
-
const [
|
|
2171
|
-
(0, import_core6.getMintRequestNonce)(this.provider, pointToken, normalizedAuthed),
|
|
2170
|
+
const [offChainBalance, onChainBalance, minter] = await Promise.all([
|
|
2172
2171
|
this.ledger.getBalance(normalizedAuthed, pointToken),
|
|
2173
2172
|
(0, import_core6.getPointTokenBalance)(this.provider, pointToken, normalizedAuthed),
|
|
2174
2173
|
(0, import_core6.isMinter)(this.provider, pointToken, normalizedAuthed)
|
|
2175
2174
|
]);
|
|
2176
2175
|
return {
|
|
2177
|
-
mintRequestNonce,
|
|
2178
2176
|
offChainBalance,
|
|
2179
2177
|
onChainBalance,
|
|
2180
2178
|
totalBalance: offChainBalance + onChainBalance,
|
|
@@ -2311,7 +2309,8 @@ var PointTokenDomainResolver = class {
|
|
|
2311
2309
|
var import_viem8 = require("viem");
|
|
2312
2310
|
var import_core7 = require("@pafi-dev/core");
|
|
2313
2311
|
var DEFAULT_REDEEM_LOCK_MS = 15 * 60 * 1e3;
|
|
2314
|
-
var
|
|
2312
|
+
var M11_SAFETY_MARGIN_MS = 30 * 1e3;
|
|
2313
|
+
var DEFAULT_SIG_DEADLINE_SEC = (DEFAULT_REDEEM_LOCK_MS - M11_SAFETY_MARGIN_MS) / 1e3;
|
|
2315
2314
|
var PTRedeemError = class extends import_core.PafiSdkError {
|
|
2316
2315
|
httpStatus = "unprocessable";
|
|
2317
2316
|
code;
|
|
@@ -2378,6 +2377,13 @@ var PTRedeemHandler = class {
|
|
|
2378
2377
|
this.redeemLockDurationMs = config.redeemLockDurationMs ?? DEFAULT_REDEEM_LOCK_MS;
|
|
2379
2378
|
this.signatureDeadlineSeconds = config.signatureDeadlineSeconds ?? DEFAULT_SIG_DEADLINE_SEC;
|
|
2380
2379
|
this.now = config.now ?? (() => Date.now());
|
|
2380
|
+
const maxAllowedSignatureMs = this.redeemLockDurationMs - M11_SAFETY_MARGIN_MS;
|
|
2381
|
+
if (this.signatureDeadlineSeconds * 1e3 > maxAllowedSignatureMs) {
|
|
2382
|
+
throw new PTRedeemError(
|
|
2383
|
+
"INVALID_AMOUNT",
|
|
2384
|
+
`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). See audit M-11.`
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2381
2387
|
if (config.redemptionService) {
|
|
2382
2388
|
this.redemptionService = config.redemptionService;
|
|
2383
2389
|
}
|
|
@@ -2443,8 +2449,14 @@ var PTRedeemHandler = class {
|
|
|
2443
2449
|
}
|
|
2444
2450
|
}
|
|
2445
2451
|
async _handleAfterNonceLock(request, burnNonce, pointTokenAddress) {
|
|
2452
|
+
const referenceMs = this.now();
|
|
2453
|
+
const projectedLockExpiresAtMs = referenceMs + this.redeemLockDurationMs;
|
|
2454
|
+
const requestedDeadlineSec = Math.floor(referenceMs / 1e3) + this.signatureDeadlineSeconds;
|
|
2455
|
+
const lockBoundedDeadlineSec = Math.floor(
|
|
2456
|
+
(projectedLockExpiresAtMs - M11_SAFETY_MARGIN_MS) / 1e3
|
|
2457
|
+
);
|
|
2446
2458
|
const previewDeadline = BigInt(
|
|
2447
|
-
Math.
|
|
2459
|
+
Math.min(requestedDeadlineSec, lockBoundedDeadlineSec)
|
|
2448
2460
|
);
|
|
2449
2461
|
let fee;
|
|
2450
2462
|
if (request.feeAmount !== void 0) {
|
|
@@ -2452,7 +2464,7 @@ var PTRedeemHandler = class {
|
|
|
2452
2464
|
} else if (this.feeService) {
|
|
2453
2465
|
const previewUserOp = this.relayService.previewBurnUserOp({
|
|
2454
2466
|
userAddress: request.userAddress,
|
|
2455
|
-
aaNonce:
|
|
2467
|
+
aaNonce: request.aaNonce,
|
|
2456
2468
|
pointTokenAddress,
|
|
2457
2469
|
amount: request.amount,
|
|
2458
2470
|
deadline: previewDeadline
|
|
@@ -3104,14 +3116,30 @@ function isNoWrapper2(address) {
|
|
|
3104
3116
|
return lower === "0x0000000000000000000000000000000000000000" || lower === "0x000000000000000000000000000000000000dead";
|
|
3105
3117
|
}
|
|
3106
3118
|
var DEFAULT_LOCK_MS = 15 * 60 * 1e3;
|
|
3107
|
-
var
|
|
3119
|
+
var M11_SAFETY_MARGIN_MS2 = 30 * 1e3;
|
|
3120
|
+
var DEFAULT_SIG_DEADLINE_SEC2 = (DEFAULT_LOCK_MS - M11_SAFETY_MARGIN_MS2) / 1e3;
|
|
3108
3121
|
var PTClaimHandler = class {
|
|
3109
3122
|
cfg;
|
|
3123
|
+
inFlightNonces = /* @__PURE__ */ new Map();
|
|
3110
3124
|
constructor(config) {
|
|
3125
|
+
const lockDurationMs = config.lockDurationMs ?? DEFAULT_LOCK_MS;
|
|
3126
|
+
const signatureDeadlineSeconds = config.signatureDeadlineSeconds ?? DEFAULT_SIG_DEADLINE_SEC2;
|
|
3127
|
+
const maxAllowedSignatureMs = lockDurationMs - M11_SAFETY_MARGIN_MS2;
|
|
3128
|
+
if (signatureDeadlineSeconds * 1e3 > maxAllowedSignatureMs) {
|
|
3129
|
+
throw new PTClaimError(
|
|
3130
|
+
"VALIDATION_FAILED",
|
|
3131
|
+
`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). See audit M-11.`,
|
|
3132
|
+
{
|
|
3133
|
+
lockDurationMs,
|
|
3134
|
+
signatureDeadlineSeconds,
|
|
3135
|
+
maxAllowedSignatureSec: maxAllowedSignatureMs / 1e3
|
|
3136
|
+
}
|
|
3137
|
+
);
|
|
3138
|
+
}
|
|
3111
3139
|
this.cfg = {
|
|
3112
3140
|
...config,
|
|
3113
|
-
lockDurationMs
|
|
3114
|
-
signatureDeadlineSeconds
|
|
3141
|
+
lockDurationMs,
|
|
3142
|
+
signatureDeadlineSeconds,
|
|
3115
3143
|
now: config.now ?? (() => Date.now())
|
|
3116
3144
|
};
|
|
3117
3145
|
}
|
|
@@ -3141,77 +3169,82 @@ var PTClaimHandler = class {
|
|
|
3141
3169
|
}
|
|
3142
3170
|
const chainAddresses = (0, import_core11.getContractAddresses)(request.chainId);
|
|
3143
3171
|
const { batchExecutor: batchExecutorAddress } = chainAddresses;
|
|
3172
|
+
let mintRequestNonce;
|
|
3173
|
+
try {
|
|
3174
|
+
mintRequestNonce = await this.cfg.provider.readContract({
|
|
3175
|
+
address: request.pointTokenAddress,
|
|
3176
|
+
abi: import_core11.POINT_TOKEN_ABI,
|
|
3177
|
+
functionName: "mintRequestNonces",
|
|
3178
|
+
args: [request.userAddress]
|
|
3179
|
+
});
|
|
3180
|
+
} catch (err) {
|
|
3181
|
+
throw new PTClaimError(
|
|
3182
|
+
"NONCE_READ_FAILED",
|
|
3183
|
+
`failed to read mintRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
|
|
3184
|
+
);
|
|
3185
|
+
}
|
|
3186
|
+
const nonceKey = `${(0, import_viem10.getAddress)(request.userAddress).toLowerCase()}:${request.pointTokenAddress.toLowerCase()}`;
|
|
3187
|
+
let userNonces = this.inFlightNonces.get(nonceKey);
|
|
3188
|
+
if (!userNonces) {
|
|
3189
|
+
userNonces = /* @__PURE__ */ new Set();
|
|
3190
|
+
this.inFlightNonces.set(nonceKey, userNonces);
|
|
3191
|
+
}
|
|
3192
|
+
if (userNonces.has(mintRequestNonce)) {
|
|
3193
|
+
throw new PTClaimError(
|
|
3194
|
+
"NONCE_IN_FLIGHT",
|
|
3195
|
+
`concurrent claim for nonce ${mintRequestNonce} in progress; retry after the prior request completes`,
|
|
3196
|
+
{ userAddress: request.userAddress, pointToken: request.pointTokenAddress, nonce: mintRequestNonce.toString() }
|
|
3197
|
+
);
|
|
3198
|
+
}
|
|
3199
|
+
userNonces.add(mintRequestNonce);
|
|
3144
3200
|
const wrapperOverride = this.cfg.mintFeeWrapperAddress;
|
|
3145
3201
|
const wrapperFromSdk = chainAddresses.mintFeeWrapper;
|
|
3146
3202
|
const resolvedWrapper = wrapperOverride !== void 0 ? isNoWrapper2(wrapperOverride) ? void 0 : wrapperOverride : isNoWrapper2(wrapperFromSdk) ? void 0 : wrapperFromSdk;
|
|
3147
|
-
const lockId = await this.cfg.ledger.lockForMinting(
|
|
3148
|
-
request.userAddress,
|
|
3149
|
-
request.amount,
|
|
3150
|
-
this.cfg.lockDurationMs,
|
|
3151
|
-
request.pointTokenAddress
|
|
3152
|
-
);
|
|
3153
3203
|
try {
|
|
3154
|
-
const
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
pointTokenAddress: request.pointTokenAddress,
|
|
3161
|
-
amount: request.amount,
|
|
3162
|
-
deadline: signatureDeadline,
|
|
3163
|
-
mintFeeWrapperAddress: resolvedWrapper
|
|
3164
|
-
});
|
|
3165
|
-
const feeAmount = this.cfg.feeService ? await this.cfg.feeService.estimateGasFee({
|
|
3166
|
-
scenario: resolvedWrapper ? "mint-wrapped" : "mint",
|
|
3167
|
-
contractAddress: request.pointTokenAddress,
|
|
3168
|
-
partialUserOp: {
|
|
3169
|
-
sender: previewUserOp.sender,
|
|
3170
|
-
nonce: previewUserOp.nonce,
|
|
3171
|
-
callData: previewUserOp.callData
|
|
3172
|
-
}
|
|
3173
|
-
}) : 0n;
|
|
3174
|
-
const domainName = await this.cfg.domainResolver.resolve(
|
|
3204
|
+
const lockCreatedAtMs = this.cfg.now();
|
|
3205
|
+
const lockExpiresAtMs = lockCreatedAtMs + this.cfg.lockDurationMs;
|
|
3206
|
+
const lockId = await this.cfg.ledger.lockForMinting(
|
|
3207
|
+
request.userAddress,
|
|
3208
|
+
request.amount,
|
|
3209
|
+
this.cfg.lockDurationMs,
|
|
3175
3210
|
request.pointTokenAddress
|
|
3176
3211
|
);
|
|
3177
|
-
const domain = {
|
|
3178
|
-
name: domainName,
|
|
3179
|
-
chainId: request.chainId,
|
|
3180
|
-
verifyingContract: request.pointTokenAddress
|
|
3181
|
-
};
|
|
3182
|
-
let userOp;
|
|
3183
3212
|
try {
|
|
3184
|
-
|
|
3213
|
+
const requestedDeadlineSec = Math.floor(lockCreatedAtMs / 1e3) + this.cfg.signatureDeadlineSeconds;
|
|
3214
|
+
const lockBoundedDeadlineSec = Math.floor(
|
|
3215
|
+
(lockExpiresAtMs - M11_SAFETY_MARGIN_MS2) / 1e3
|
|
3216
|
+
);
|
|
3217
|
+
const signatureDeadline = BigInt(
|
|
3218
|
+
Math.min(requestedDeadlineSec, lockBoundedDeadlineSec)
|
|
3219
|
+
);
|
|
3220
|
+
const previewUserOp = this.cfg.relayService.previewMintUserOp({
|
|
3185
3221
|
userAddress: request.userAddress,
|
|
3186
3222
|
aaNonce: request.aaNonce,
|
|
3187
|
-
batchExecutorAddress,
|
|
3188
3223
|
pointTokenAddress: request.pointTokenAddress,
|
|
3189
3224
|
amount: request.amount,
|
|
3190
|
-
issuerSignerWallet: this.cfg.issuerSignerWallet,
|
|
3191
|
-
domain,
|
|
3192
|
-
mintRequestNonce: request.mintRequestNonce,
|
|
3193
3225
|
deadline: signatureDeadline,
|
|
3194
|
-
mintFeeWrapperAddress: resolvedWrapper
|
|
3195
|
-
// Pass the bundler-estimated `feeAmount` explicitly so the
|
|
3196
|
-
// RelayService skips its legacy `quoteOperatorFeePt` path
|
|
3197
|
-
// (which uses the SDK's old 12_000 bps premium default).
|
|
3198
|
-
// Without this, the response's `feeAmount` (from FeeManager,
|
|
3199
|
-
// 100% premium on top of PAFI's 110% server-side estimate)
|
|
3200
|
-
// would diverge from the actual PT.transfer amount in the
|
|
3201
|
-
// UserOp batch (`quoteOperatorFeePt`'s 120%), and the user
|
|
3202
|
-
// would see one value while the wallet transferred another.
|
|
3203
|
-
feeAmount
|
|
3226
|
+
mintFeeWrapperAddress: resolvedWrapper
|
|
3204
3227
|
});
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3228
|
+
const feeAmount = this.cfg.feeService ? await this.cfg.feeService.estimateGasFee({
|
|
3229
|
+
scenario: resolvedWrapper ? "mint-wrapped" : "mint",
|
|
3230
|
+
contractAddress: request.pointTokenAddress,
|
|
3231
|
+
partialUserOp: {
|
|
3232
|
+
sender: previewUserOp.sender,
|
|
3233
|
+
nonce: previewUserOp.nonce,
|
|
3234
|
+
callData: previewUserOp.callData
|
|
3235
|
+
}
|
|
3236
|
+
}) : 0n;
|
|
3237
|
+
const domainName = await this.cfg.domainResolver.resolve(
|
|
3238
|
+
request.pointTokenAddress
|
|
3209
3239
|
);
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3240
|
+
const domain = {
|
|
3241
|
+
name: domainName,
|
|
3242
|
+
chainId: request.chainId,
|
|
3243
|
+
verifyingContract: request.pointTokenAddress
|
|
3244
|
+
};
|
|
3245
|
+
let userOp;
|
|
3213
3246
|
try {
|
|
3214
|
-
|
|
3247
|
+
userOp = await this.cfg.relayService.prepareMint({
|
|
3215
3248
|
userAddress: request.userAddress,
|
|
3216
3249
|
aaNonce: request.aaNonce,
|
|
3217
3250
|
batchExecutorAddress,
|
|
@@ -3219,34 +3252,68 @@ var PTClaimHandler = class {
|
|
|
3219
3252
|
amount: request.amount,
|
|
3220
3253
|
issuerSignerWallet: this.cfg.issuerSignerWallet,
|
|
3221
3254
|
domain,
|
|
3222
|
-
mintRequestNonce
|
|
3255
|
+
mintRequestNonce,
|
|
3223
3256
|
deadline: signatureDeadline,
|
|
3224
|
-
|
|
3225
|
-
|
|
3257
|
+
mintFeeWrapperAddress: resolvedWrapper,
|
|
3258
|
+
// Pass the bundler-estimated `feeAmount` explicitly so the
|
|
3259
|
+
// RelayService skips its legacy `quoteOperatorFeePt` path
|
|
3260
|
+
// (which uses the SDK's old 12_000 bps premium default).
|
|
3261
|
+
// Without this, the response's `feeAmount` (from FeeManager,
|
|
3262
|
+
// 100% premium on top of PAFI's 110% server-side estimate)
|
|
3263
|
+
// would diverge from the actual PT.transfer amount in the
|
|
3264
|
+
// UserOp batch (`quoteOperatorFeePt`'s 120%), and the user
|
|
3265
|
+
// would see one value while the wallet transferred another.
|
|
3266
|
+
feeAmount
|
|
3226
3267
|
});
|
|
3227
3268
|
} catch (err) {
|
|
3228
3269
|
throw new PTClaimError(
|
|
3229
3270
|
"BUILD_FAILED",
|
|
3230
|
-
`prepareMint
|
|
3271
|
+
`prepareMint failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3231
3272
|
);
|
|
3232
3273
|
}
|
|
3274
|
+
let fallback;
|
|
3275
|
+
if (feeAmount > 0n) {
|
|
3276
|
+
try {
|
|
3277
|
+
fallback = await this.cfg.relayService.prepareMint({
|
|
3278
|
+
userAddress: request.userAddress,
|
|
3279
|
+
aaNonce: request.aaNonce,
|
|
3280
|
+
batchExecutorAddress,
|
|
3281
|
+
pointTokenAddress: request.pointTokenAddress,
|
|
3282
|
+
amount: request.amount,
|
|
3283
|
+
issuerSignerWallet: this.cfg.issuerSignerWallet,
|
|
3284
|
+
domain,
|
|
3285
|
+
mintRequestNonce,
|
|
3286
|
+
deadline: signatureDeadline,
|
|
3287
|
+
feeAmount: 0n,
|
|
3288
|
+
mintFeeWrapperAddress: resolvedWrapper
|
|
3289
|
+
});
|
|
3290
|
+
} catch (err) {
|
|
3291
|
+
throw new PTClaimError(
|
|
3292
|
+
"BUILD_FAILED",
|
|
3293
|
+
`prepareMint (fallback) failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3294
|
+
);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
const calls = (0, import_core11.decodeBatchExecuteCalls)(userOp.callData);
|
|
3298
|
+
const callsFallback = fallback ? (0, import_core11.decodeBatchExecuteCalls)(fallback.callData) : void 0;
|
|
3299
|
+
return {
|
|
3300
|
+
userOp,
|
|
3301
|
+
fallback,
|
|
3302
|
+
lockId,
|
|
3303
|
+
feeAmount,
|
|
3304
|
+
signatureDeadline,
|
|
3305
|
+
expiresInSeconds: Math.floor(this.cfg.lockDurationMs / 1e3),
|
|
3306
|
+
calls,
|
|
3307
|
+
callsFallback
|
|
3308
|
+
};
|
|
3309
|
+
} catch (err) {
|
|
3310
|
+
await this.cfg.ledger.releaseLock(lockId).catch(() => {
|
|
3311
|
+
});
|
|
3312
|
+
throw err;
|
|
3233
3313
|
}
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
userOp,
|
|
3238
|
-
fallback,
|
|
3239
|
-
lockId,
|
|
3240
|
-
feeAmount,
|
|
3241
|
-
signatureDeadline,
|
|
3242
|
-
expiresInSeconds: Math.floor(this.cfg.lockDurationMs / 1e3),
|
|
3243
|
-
calls,
|
|
3244
|
-
callsFallback
|
|
3245
|
-
};
|
|
3246
|
-
} catch (err) {
|
|
3247
|
-
await this.cfg.ledger.releaseLock(lockId).catch(() => {
|
|
3248
|
-
});
|
|
3249
|
-
throw err;
|
|
3314
|
+
} finally {
|
|
3315
|
+
userNonces.delete(mintRequestNonce);
|
|
3316
|
+
if (userNonces.size === 0) this.inFlightNonces.delete(nonceKey);
|
|
3250
3317
|
}
|
|
3251
3318
|
}
|
|
3252
3319
|
};
|
|
@@ -3561,7 +3628,6 @@ var IssuerApiAdapter = class {
|
|
|
3561
3628
|
}
|
|
3562
3629
|
);
|
|
3563
3630
|
return {
|
|
3564
|
-
mintRequestNonce: result.mintRequestNonce.toString(),
|
|
3565
3631
|
offChainBalance: result.offChainBalance.toString(),
|
|
3566
3632
|
onChainBalance: result.onChainBalance.toString(),
|
|
3567
3633
|
totalBalance: result.totalBalance.toString(),
|
|
@@ -3585,8 +3651,7 @@ var IssuerApiAdapter = class {
|
|
|
3585
3651
|
amount: input.amount,
|
|
3586
3652
|
pointTokenAddress,
|
|
3587
3653
|
chainId: input.chainId,
|
|
3588
|
-
aaNonce: input.aaNonce
|
|
3589
|
-
mintRequestNonce: input.mintRequestNonce
|
|
3654
|
+
aaNonce: input.aaNonce
|
|
3590
3655
|
});
|
|
3591
3656
|
const sponsorAuth = await this.buildSponsorAuth(
|
|
3592
3657
|
input.authenticatedAddress,
|
|
@@ -3682,8 +3747,7 @@ var IssuerApiAdapter = class {
|
|
|
3682
3747
|
amount: input.amount,
|
|
3683
3748
|
pointTokenAddress,
|
|
3684
3749
|
chainId: input.chainId,
|
|
3685
|
-
aaNonce: input.aaNonce
|
|
3686
|
-
mintRequestNonce: input.mintRequestNonce
|
|
3750
|
+
aaNonce: input.aaNonce
|
|
3687
3751
|
});
|
|
3688
3752
|
const prepared = await this.runMobilePrepare(
|
|
3689
3753
|
input.authenticatedAddress,
|
|
@@ -5142,7 +5206,7 @@ var MemoryRedemptionHistoryStore = class {
|
|
|
5142
5206
|
};
|
|
5143
5207
|
|
|
5144
5208
|
// src/index.ts
|
|
5145
|
-
var PAFI_ISSUER_SDK_VERSION = true ? "0.
|
|
5209
|
+
var PAFI_ISSUER_SDK_VERSION = true ? "0.25.0" : "dev";
|
|
5146
5210
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5147
5211
|
0 && (module.exports = {
|
|
5148
5212
|
AdapterMisconfiguredError,
|