@pafi-dev/issuer 0.31.0 → 0.33.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 +150 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -10
- package/dist/index.d.ts +62 -10
- package/dist/index.js +142 -21
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1551,9 +1551,9 @@ interface SettlementClientConfig {
|
|
|
1551
1551
|
/**
|
|
1552
1552
|
* chainId — used to derive the issuer-api base URL via
|
|
1553
1553
|
* `getPafiServiceUrls(chainId).issuerApi`. SDK ships with the URL
|
|
1554
|
-
* per chainId; bump SDK version to retarget.
|
|
1555
1554
|
*/
|
|
1556
1555
|
chainId: number;
|
|
1556
|
+
baseUrl?: string;
|
|
1557
1557
|
/** PAFI-assigned issuer id used in `X-Issuer-Id` header. */
|
|
1558
1558
|
issuerId: string;
|
|
1559
1559
|
/** Raw API key sent as `Authorization: Bearer <apiKey>`. */
|
|
@@ -1566,6 +1566,8 @@ interface SettlementClientConfig {
|
|
|
1566
1566
|
interface PolicyProviderConfig extends SettlementClientConfig {
|
|
1567
1567
|
/** Cache TTL in milliseconds. Default 5 * 60 * 1000 (5min). */
|
|
1568
1568
|
cacheTtlMs?: number;
|
|
1569
|
+
onFetchFailure?: "fail-closed" | "permissive-default";
|
|
1570
|
+
onWarning?: (msg: string, ctx: Record<string, unknown>) => void;
|
|
1569
1571
|
/**
|
|
1570
1572
|
* Optional clock for testability. Returns unix milliseconds.
|
|
1571
1573
|
* Defaults to () => Date.now().
|
|
@@ -1596,6 +1598,8 @@ declare class PolicyProvider {
|
|
|
1596
1598
|
private readonly client;
|
|
1597
1599
|
private readonly issuerId;
|
|
1598
1600
|
private readonly cacheTtlMs;
|
|
1601
|
+
private readonly onFetchFailure;
|
|
1602
|
+
private readonly onWarning?;
|
|
1599
1603
|
private readonly now;
|
|
1600
1604
|
private cache;
|
|
1601
1605
|
private inflight;
|
|
@@ -1987,7 +1991,7 @@ interface PTRedeemResponse {
|
|
|
1987
1991
|
/** The BurnRequest deadline (unix seconds) — FE uses this to surface a countdown. */
|
|
1988
1992
|
signatureDeadline: bigint;
|
|
1989
1993
|
}
|
|
1990
|
-
type PTRedeemErrorCode = "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "NONCE_IN_FLIGHT" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED" | "REDEMPTION_POLICY_DENIED" | "UNSUPPORTED_POINT_TOKEN";
|
|
1994
|
+
type PTRedeemErrorCode = "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "NONCE_IN_FLIGHT" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED" | "REDEMPTION_POLICY_DENIED" | "REDEMPTION_POLICY_UNAVAILABLE" | "UNSUPPORTED_POINT_TOKEN";
|
|
1991
1995
|
declare class PTRedeemError extends PafiSdkError {
|
|
1992
1996
|
readonly httpStatus: "unprocessable";
|
|
1993
1997
|
readonly code: PTRedeemErrorCode;
|
|
@@ -2039,9 +2043,9 @@ interface PafiBackendConfig {
|
|
|
2039
2043
|
/**
|
|
2040
2044
|
* chainId — used to derive the sponsor-relayer base URL via
|
|
2041
2045
|
* `getPafiServiceUrls(chainId).sponsorRelayer`. SDK ships with the
|
|
2042
|
-
* URL per chainId; bump SDK version to retarget.
|
|
2043
2046
|
*/
|
|
2044
2047
|
chainId: number;
|
|
2048
|
+
baseUrl?: string;
|
|
2045
2049
|
issuerId: string;
|
|
2046
2050
|
apiKey: string;
|
|
2047
2051
|
fetchImpl?: typeof fetch;
|
|
@@ -2198,12 +2202,32 @@ interface MintStatusParams {
|
|
|
2198
2202
|
* - lock.status === "PENDING"
|
|
2199
2203
|
* - lock.userOpHash is bound (set by `/claim/submit`)
|
|
2200
2204
|
*
|
|
2201
|
-
* If the bundler reports the UserOp confirmed
|
|
2205
|
+
* If the bundler reports the UserOp confirmed AND the receipt block
|
|
2206
|
+
* is past the configured `confirmations` depth, the handler updates
|
|
2202
2207
|
* the ledger lock + returns `MINTED` immediately, bypassing
|
|
2203
2208
|
* `PointIndexer`'s amount-based race (multiple PENDING locks with
|
|
2204
2209
|
* the same amount can be matched to the wrong tx_hash).
|
|
2205
2210
|
*/
|
|
2206
2211
|
pafiBackendClient?: PafiBackendClient | null;
|
|
2212
|
+
/**
|
|
2213
|
+
* Audit PACI5-13 — required for the confirmation-depth check on the
|
|
2214
|
+
* bundler-receipt fallback. The bundler returns success at zero
|
|
2215
|
+
* confs, but `PointIndexer` enforces a 3-block reorg window;
|
|
2216
|
+
* crediting / debiting off-chain at 0 confs while the indexer waits
|
|
2217
|
+
* for finality leaves an unbacked durable mutation if the tx is
|
|
2218
|
+
* reorged out. Pass the SAME PublicClient that the indexer uses so
|
|
2219
|
+
* both paths see consistent chain head. Optional only for legacy
|
|
2220
|
+
* callers that don't supply `pafiBackendClient`; required whenever
|
|
2221
|
+
* the receipt-fallback path can run.
|
|
2222
|
+
*/
|
|
2223
|
+
provider?: PublicClient;
|
|
2224
|
+
/**
|
|
2225
|
+
* Audit PACI5-13 — confirmation depth required before the receipt
|
|
2226
|
+
* fallback applies the credit / debit. MUST match
|
|
2227
|
+
* `PointIndexer.confirmations` (default 3). Operators who reduce
|
|
2228
|
+
* the indexer depth must set this to the same value.
|
|
2229
|
+
*/
|
|
2230
|
+
confirmations?: number;
|
|
2207
2231
|
/** Optional logger for "ledger update failed" warnings. */
|
|
2208
2232
|
onWarning?: (msg: string) => void;
|
|
2209
2233
|
}
|
|
@@ -2212,6 +2236,20 @@ interface BurnStatusParams {
|
|
|
2212
2236
|
userAddress: Address;
|
|
2213
2237
|
ledger: IPointLedger;
|
|
2214
2238
|
pafiBackendClient?: PafiBackendClient | null;
|
|
2239
|
+
/**
|
|
2240
|
+
* Audit PACI5-13 — see `MintStatusParams.provider` for full
|
|
2241
|
+
* rationale. The receipt-fallback path applies a spendable
|
|
2242
|
+
* off-chain credit; without a confirmation-depth gate a reorg
|
|
2243
|
+
* before `BurnIndexer.confirmations` leaves durable unbacked
|
|
2244
|
+
* credit with no reversal mechanism.
|
|
2245
|
+
*/
|
|
2246
|
+
provider?: PublicClient;
|
|
2247
|
+
/**
|
|
2248
|
+
* Audit PACI5-13 — confirmation depth required before the receipt
|
|
2249
|
+
* fallback applies the credit. MUST match
|
|
2250
|
+
* `BurnIndexer.confirmations` (default 3).
|
|
2251
|
+
*/
|
|
2252
|
+
confirmations?: number;
|
|
2215
2253
|
onWarning?: (msg: string) => void;
|
|
2216
2254
|
}
|
|
2217
2255
|
declare class LockNotFoundError extends PafiSdkError {
|
|
@@ -3185,6 +3223,26 @@ interface IssuerServiceConfig {
|
|
|
3185
3223
|
issuerId: string;
|
|
3186
3224
|
apiKey: string;
|
|
3187
3225
|
historyStore: IRedemptionHistoryStore;
|
|
3226
|
+
/**
|
|
3227
|
+
* Optional override for the PAFI issuer-api base URL. Audit
|
|
3228
|
+
* env var (e.g. `PAFI_ISSUER_API_URL`) so policy fetch hits the
|
|
3229
|
+
* canonical environment for the deploy. Undefined → SDK
|
|
3230
|
+
* ship-default per chainId.
|
|
3231
|
+
*/
|
|
3232
|
+
baseUrl?: string;
|
|
3233
|
+
/**
|
|
3234
|
+
* Behavior khi settlement-api fetch fail. Default `'fail-closed'`
|
|
3235
|
+
* Opt in to `'permissive-default'` ONLY when paired with an alert
|
|
3236
|
+
* on the `policy_provider_fallback` event surfaced via
|
|
3237
|
+
* `onPolicyWarning`.
|
|
3238
|
+
*/
|
|
3239
|
+
onFetchFailure?: "fail-closed" | "permissive-default";
|
|
3240
|
+
/**
|
|
3241
|
+
* Observability hook for `policy_provider_fallback` events. Wire
|
|
3242
|
+
* to your logger / Sentry / Datadog so the on-call dashboard sees
|
|
3243
|
+
* settlement-api degradation.
|
|
3244
|
+
*/
|
|
3245
|
+
onPolicyWarning?: (msg: string, ctx: Record<string, unknown>) => void;
|
|
3188
3246
|
/** Override fetch (testing). */
|
|
3189
3247
|
fetchImpl?: typeof fetch;
|
|
3190
3248
|
/** Per-fetch timeout in ms. Default 1000. */
|
|
@@ -3354,12 +3412,6 @@ interface RedeemPrepareDto extends MobilePrepareDto {
|
|
|
3354
3412
|
* Lock id reserved for the FALLBACK redeem path (= full `amount`).
|
|
3355
3413
|
* Mobile FE polls `/redeem/status/:lockIdFallback` when it submits
|
|
3356
3414
|
* the fallback variant (`variant: 'fallback'` on `/redeem/submit`).
|
|
3357
|
-
*
|
|
3358
|
-
* Audit PACI5-21 — pre-fix the adapter exposed only the sponsored
|
|
3359
|
-
* `lockId` (= `amount - fee`), so the bundler-receipt fallback in
|
|
3360
|
-
* `handleRedeemStatus` resolved the smaller credit against the
|
|
3361
|
-
* on-chain burn of the full amount → user under-credited by exactly
|
|
3362
|
-
* the fee on every fallback redeem.
|
|
3363
3415
|
*/
|
|
3364
3416
|
lockIdFallback?: string;
|
|
3365
3417
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1551,9 +1551,9 @@ interface SettlementClientConfig {
|
|
|
1551
1551
|
/**
|
|
1552
1552
|
* chainId — used to derive the issuer-api base URL via
|
|
1553
1553
|
* `getPafiServiceUrls(chainId).issuerApi`. SDK ships with the URL
|
|
1554
|
-
* per chainId; bump SDK version to retarget.
|
|
1555
1554
|
*/
|
|
1556
1555
|
chainId: number;
|
|
1556
|
+
baseUrl?: string;
|
|
1557
1557
|
/** PAFI-assigned issuer id used in `X-Issuer-Id` header. */
|
|
1558
1558
|
issuerId: string;
|
|
1559
1559
|
/** Raw API key sent as `Authorization: Bearer <apiKey>`. */
|
|
@@ -1566,6 +1566,8 @@ interface SettlementClientConfig {
|
|
|
1566
1566
|
interface PolicyProviderConfig extends SettlementClientConfig {
|
|
1567
1567
|
/** Cache TTL in milliseconds. Default 5 * 60 * 1000 (5min). */
|
|
1568
1568
|
cacheTtlMs?: number;
|
|
1569
|
+
onFetchFailure?: "fail-closed" | "permissive-default";
|
|
1570
|
+
onWarning?: (msg: string, ctx: Record<string, unknown>) => void;
|
|
1569
1571
|
/**
|
|
1570
1572
|
* Optional clock for testability. Returns unix milliseconds.
|
|
1571
1573
|
* Defaults to () => Date.now().
|
|
@@ -1596,6 +1598,8 @@ declare class PolicyProvider {
|
|
|
1596
1598
|
private readonly client;
|
|
1597
1599
|
private readonly issuerId;
|
|
1598
1600
|
private readonly cacheTtlMs;
|
|
1601
|
+
private readonly onFetchFailure;
|
|
1602
|
+
private readonly onWarning?;
|
|
1599
1603
|
private readonly now;
|
|
1600
1604
|
private cache;
|
|
1601
1605
|
private inflight;
|
|
@@ -1987,7 +1991,7 @@ interface PTRedeemResponse {
|
|
|
1987
1991
|
/** The BurnRequest deadline (unix seconds) — FE uses this to surface a countdown. */
|
|
1988
1992
|
signatureDeadline: bigint;
|
|
1989
1993
|
}
|
|
1990
|
-
type PTRedeemErrorCode = "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "NONCE_IN_FLIGHT" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED" | "REDEMPTION_POLICY_DENIED" | "UNSUPPORTED_POINT_TOKEN";
|
|
1994
|
+
type PTRedeemErrorCode = "UNAUTHORIZED" | "INVALID_AMOUNT" | "NONCE_READ_FAILED" | "NONCE_IN_FLIGHT" | "LEDGER_NOT_SUPPORTED" | "SIGNING_FAILED" | "REDEMPTION_POLICY_DENIED" | "REDEMPTION_POLICY_UNAVAILABLE" | "UNSUPPORTED_POINT_TOKEN";
|
|
1991
1995
|
declare class PTRedeemError extends PafiSdkError {
|
|
1992
1996
|
readonly httpStatus: "unprocessable";
|
|
1993
1997
|
readonly code: PTRedeemErrorCode;
|
|
@@ -2039,9 +2043,9 @@ interface PafiBackendConfig {
|
|
|
2039
2043
|
/**
|
|
2040
2044
|
* chainId — used to derive the sponsor-relayer base URL via
|
|
2041
2045
|
* `getPafiServiceUrls(chainId).sponsorRelayer`. SDK ships with the
|
|
2042
|
-
* URL per chainId; bump SDK version to retarget.
|
|
2043
2046
|
*/
|
|
2044
2047
|
chainId: number;
|
|
2048
|
+
baseUrl?: string;
|
|
2045
2049
|
issuerId: string;
|
|
2046
2050
|
apiKey: string;
|
|
2047
2051
|
fetchImpl?: typeof fetch;
|
|
@@ -2198,12 +2202,32 @@ interface MintStatusParams {
|
|
|
2198
2202
|
* - lock.status === "PENDING"
|
|
2199
2203
|
* - lock.userOpHash is bound (set by `/claim/submit`)
|
|
2200
2204
|
*
|
|
2201
|
-
* If the bundler reports the UserOp confirmed
|
|
2205
|
+
* If the bundler reports the UserOp confirmed AND the receipt block
|
|
2206
|
+
* is past the configured `confirmations` depth, the handler updates
|
|
2202
2207
|
* the ledger lock + returns `MINTED` immediately, bypassing
|
|
2203
2208
|
* `PointIndexer`'s amount-based race (multiple PENDING locks with
|
|
2204
2209
|
* the same amount can be matched to the wrong tx_hash).
|
|
2205
2210
|
*/
|
|
2206
2211
|
pafiBackendClient?: PafiBackendClient | null;
|
|
2212
|
+
/**
|
|
2213
|
+
* Audit PACI5-13 — required for the confirmation-depth check on the
|
|
2214
|
+
* bundler-receipt fallback. The bundler returns success at zero
|
|
2215
|
+
* confs, but `PointIndexer` enforces a 3-block reorg window;
|
|
2216
|
+
* crediting / debiting off-chain at 0 confs while the indexer waits
|
|
2217
|
+
* for finality leaves an unbacked durable mutation if the tx is
|
|
2218
|
+
* reorged out. Pass the SAME PublicClient that the indexer uses so
|
|
2219
|
+
* both paths see consistent chain head. Optional only for legacy
|
|
2220
|
+
* callers that don't supply `pafiBackendClient`; required whenever
|
|
2221
|
+
* the receipt-fallback path can run.
|
|
2222
|
+
*/
|
|
2223
|
+
provider?: PublicClient;
|
|
2224
|
+
/**
|
|
2225
|
+
* Audit PACI5-13 — confirmation depth required before the receipt
|
|
2226
|
+
* fallback applies the credit / debit. MUST match
|
|
2227
|
+
* `PointIndexer.confirmations` (default 3). Operators who reduce
|
|
2228
|
+
* the indexer depth must set this to the same value.
|
|
2229
|
+
*/
|
|
2230
|
+
confirmations?: number;
|
|
2207
2231
|
/** Optional logger for "ledger update failed" warnings. */
|
|
2208
2232
|
onWarning?: (msg: string) => void;
|
|
2209
2233
|
}
|
|
@@ -2212,6 +2236,20 @@ interface BurnStatusParams {
|
|
|
2212
2236
|
userAddress: Address;
|
|
2213
2237
|
ledger: IPointLedger;
|
|
2214
2238
|
pafiBackendClient?: PafiBackendClient | null;
|
|
2239
|
+
/**
|
|
2240
|
+
* Audit PACI5-13 — see `MintStatusParams.provider` for full
|
|
2241
|
+
* rationale. The receipt-fallback path applies a spendable
|
|
2242
|
+
* off-chain credit; without a confirmation-depth gate a reorg
|
|
2243
|
+
* before `BurnIndexer.confirmations` leaves durable unbacked
|
|
2244
|
+
* credit with no reversal mechanism.
|
|
2245
|
+
*/
|
|
2246
|
+
provider?: PublicClient;
|
|
2247
|
+
/**
|
|
2248
|
+
* Audit PACI5-13 — confirmation depth required before the receipt
|
|
2249
|
+
* fallback applies the credit. MUST match
|
|
2250
|
+
* `BurnIndexer.confirmations` (default 3).
|
|
2251
|
+
*/
|
|
2252
|
+
confirmations?: number;
|
|
2215
2253
|
onWarning?: (msg: string) => void;
|
|
2216
2254
|
}
|
|
2217
2255
|
declare class LockNotFoundError extends PafiSdkError {
|
|
@@ -3185,6 +3223,26 @@ interface IssuerServiceConfig {
|
|
|
3185
3223
|
issuerId: string;
|
|
3186
3224
|
apiKey: string;
|
|
3187
3225
|
historyStore: IRedemptionHistoryStore;
|
|
3226
|
+
/**
|
|
3227
|
+
* Optional override for the PAFI issuer-api base URL. Audit
|
|
3228
|
+
* env var (e.g. `PAFI_ISSUER_API_URL`) so policy fetch hits the
|
|
3229
|
+
* canonical environment for the deploy. Undefined → SDK
|
|
3230
|
+
* ship-default per chainId.
|
|
3231
|
+
*/
|
|
3232
|
+
baseUrl?: string;
|
|
3233
|
+
/**
|
|
3234
|
+
* Behavior khi settlement-api fetch fail. Default `'fail-closed'`
|
|
3235
|
+
* Opt in to `'permissive-default'` ONLY when paired with an alert
|
|
3236
|
+
* on the `policy_provider_fallback` event surfaced via
|
|
3237
|
+
* `onPolicyWarning`.
|
|
3238
|
+
*/
|
|
3239
|
+
onFetchFailure?: "fail-closed" | "permissive-default";
|
|
3240
|
+
/**
|
|
3241
|
+
* Observability hook for `policy_provider_fallback` events. Wire
|
|
3242
|
+
* to your logger / Sentry / Datadog so the on-call dashboard sees
|
|
3243
|
+
* settlement-api degradation.
|
|
3244
|
+
*/
|
|
3245
|
+
onPolicyWarning?: (msg: string, ctx: Record<string, unknown>) => void;
|
|
3188
3246
|
/** Override fetch (testing). */
|
|
3189
3247
|
fetchImpl?: typeof fetch;
|
|
3190
3248
|
/** Per-fetch timeout in ms. Default 1000. */
|
|
@@ -3354,12 +3412,6 @@ interface RedeemPrepareDto extends MobilePrepareDto {
|
|
|
3354
3412
|
* Lock id reserved for the FALLBACK redeem path (= full `amount`).
|
|
3355
3413
|
* Mobile FE polls `/redeem/status/:lockIdFallback` when it submits
|
|
3356
3414
|
* the fallback variant (`variant: 'fallback'` on `/redeem/submit`).
|
|
3357
|
-
*
|
|
3358
|
-
* Audit PACI5-21 — pre-fix the adapter exposed only the sponsored
|
|
3359
|
-
* `lockId` (= `amount - fee`), so the bundler-receipt fallback in
|
|
3360
|
-
* `handleRedeemStatus` resolved the smaller credit against the
|
|
3361
|
-
* on-chain burn of the full amount → user under-credited by exactly
|
|
3362
|
-
* the fee on every fallback redeem.
|
|
3363
3415
|
*/
|
|
3364
3416
|
lockIdFallback?: string;
|
|
3365
3417
|
}
|
package/dist/index.js
CHANGED
|
@@ -2190,11 +2190,23 @@ var PTRedeemHandler = class {
|
|
|
2190
2190
|
);
|
|
2191
2191
|
}
|
|
2192
2192
|
if (this.redemptionService) {
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2193
|
+
let decision;
|
|
2194
|
+
try {
|
|
2195
|
+
decision = await this.redemptionService.evaluate(
|
|
2196
|
+
request.userAddress,
|
|
2197
|
+
request.amount,
|
|
2198
|
+
pointTokenAddress
|
|
2199
|
+
);
|
|
2200
|
+
} catch (err) {
|
|
2201
|
+
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
2202
|
+
if (code === "POLICY_PROVIDER_UNAVAILABLE") {
|
|
2203
|
+
throw new PTRedeemError(
|
|
2204
|
+
"REDEMPTION_POLICY_UNAVAILABLE",
|
|
2205
|
+
"Redemption policy temporarily unavailable \u2014 please try again shortly."
|
|
2206
|
+
);
|
|
2207
|
+
}
|
|
2208
|
+
throw err;
|
|
2209
|
+
}
|
|
2198
2210
|
if (!decision.allowed) {
|
|
2199
2211
|
const denial = decision.denial;
|
|
2200
2212
|
throw new PTRedeemError(
|
|
@@ -2410,6 +2422,45 @@ var PTRedeemHandler = class {
|
|
|
2410
2422
|
};
|
|
2411
2423
|
|
|
2412
2424
|
// src/api/statusHandlers.ts
|
|
2425
|
+
var DEFAULT_STATUS_CONFIRMATIONS = 3;
|
|
2426
|
+
async function isReceiptPastConfirmations(receipt, provider, confirmations, onWarning, handlerName) {
|
|
2427
|
+
if (!provider) {
|
|
2428
|
+
onWarning?.(
|
|
2429
|
+
`${handlerName}: provider missing \u2014 cannot enforce confirmation depth; deferring receipt fallback to on-chain indexer (audit PACI5-13).`
|
|
2430
|
+
);
|
|
2431
|
+
return false;
|
|
2432
|
+
}
|
|
2433
|
+
if (!receipt.blockNumber) {
|
|
2434
|
+
onWarning?.(
|
|
2435
|
+
`${handlerName}: receipt has no blockNumber \u2014 cannot enforce confirmation depth; deferring to indexer (audit PACI5-13).`
|
|
2436
|
+
);
|
|
2437
|
+
return false;
|
|
2438
|
+
}
|
|
2439
|
+
const requiredConfs = BigInt(confirmations ?? DEFAULT_STATUS_CONFIRMATIONS);
|
|
2440
|
+
let receiptBlock;
|
|
2441
|
+
try {
|
|
2442
|
+
receiptBlock = BigInt(receipt.blockNumber);
|
|
2443
|
+
} catch {
|
|
2444
|
+
onWarning?.(
|
|
2445
|
+
`${handlerName}: malformed receipt blockNumber (${receipt.blockNumber}) \u2014 deferring to indexer (audit PACI5-13).`
|
|
2446
|
+
);
|
|
2447
|
+
return false;
|
|
2448
|
+
}
|
|
2449
|
+
let head;
|
|
2450
|
+
try {
|
|
2451
|
+
head = await provider.getBlockNumber();
|
|
2452
|
+
} catch (err) {
|
|
2453
|
+
onWarning?.(
|
|
2454
|
+
`${handlerName}: getBlockNumber failed (${err instanceof Error ? err.message : String(err)}) \u2014 deferring to indexer (audit PACI5-13).`
|
|
2455
|
+
);
|
|
2456
|
+
return false;
|
|
2457
|
+
}
|
|
2458
|
+
const depth = head - receiptBlock;
|
|
2459
|
+
if (depth < requiredConfs) {
|
|
2460
|
+
return false;
|
|
2461
|
+
}
|
|
2462
|
+
return true;
|
|
2463
|
+
}
|
|
2413
2464
|
var LockNotFoundError = class extends PafiSdkError {
|
|
2414
2465
|
code = "LOCK_NOT_FOUND";
|
|
2415
2466
|
httpStatus = "not_found";
|
|
@@ -2438,6 +2489,23 @@ async function handleClaimStatus(params) {
|
|
|
2438
2489
|
lock.userOpHash
|
|
2439
2490
|
);
|
|
2440
2491
|
if (receipt) {
|
|
2492
|
+
const passesConfirmationDepth = await isReceiptPastConfirmations(
|
|
2493
|
+
receipt,
|
|
2494
|
+
params.provider,
|
|
2495
|
+
params.confirmations,
|
|
2496
|
+
params.onWarning,
|
|
2497
|
+
"handleClaimStatus"
|
|
2498
|
+
);
|
|
2499
|
+
if (!passesConfirmationDepth) {
|
|
2500
|
+
return {
|
|
2501
|
+
lockId: lock.lockId,
|
|
2502
|
+
status: "PENDING",
|
|
2503
|
+
txHash: lock.txHash ?? null,
|
|
2504
|
+
amount: lock.amount.toString(),
|
|
2505
|
+
createdAt: new Date(lock.createdAt).toISOString(),
|
|
2506
|
+
expiresAt: new Date(lock.expiresAt).toISOString()
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2441
2509
|
if (receipt.success && receipt.txHash) {
|
|
2442
2510
|
if (!lock.tokenAddress) {
|
|
2443
2511
|
params.onWarning?.(
|
|
@@ -2512,14 +2580,23 @@ async function handleRedeemStatus(params) {
|
|
|
2512
2580
|
credit.userOpHash
|
|
2513
2581
|
);
|
|
2514
2582
|
if (receipt && receipt.success) {
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2583
|
+
const passesConfirmationDepth = await isReceiptPastConfirmations(
|
|
2584
|
+
receipt,
|
|
2585
|
+
params.provider,
|
|
2586
|
+
params.confirmations,
|
|
2587
|
+
params.onWarning,
|
|
2588
|
+
"handleRedeemStatus"
|
|
2589
|
+
);
|
|
2590
|
+
if (passesConfirmationDepth) {
|
|
2591
|
+
status = "RESOLVED";
|
|
2592
|
+
txHash = receipt.txHash;
|
|
2593
|
+
if (params.ledger.resolveCreditByBurnTx) {
|
|
2594
|
+
await params.ledger.resolveCreditByBurnTx(credit.lockId, receipt.txHash).catch((err) => {
|
|
2595
|
+
params.onWarning?.(
|
|
2596
|
+
`handleRedeemStatus: resolveCreditByBurnTx failed for lock ${credit.lockId}: ${err}`
|
|
2597
|
+
);
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2523
2600
|
}
|
|
2524
2601
|
}
|
|
2525
2602
|
} catch (err) {
|
|
@@ -3682,10 +3759,6 @@ var IssuerApiAdapter = class {
|
|
|
3682
3759
|
pointTokenAddress,
|
|
3683
3760
|
redeemResponse.expiresInSeconds,
|
|
3684
3761
|
input.eip7702Auth,
|
|
3685
|
-
// Audit PACI5-21 — fallback path reserves a separate
|
|
3686
|
-
// PendingCredit row for the full `amount`. Surface its lockId so
|
|
3687
|
-
// mobile FE can poll the correct row + `handleMobileSubmit`
|
|
3688
|
-
// routes the userOpHash bind to it on fallback submit.
|
|
3689
3762
|
redeemResponse.fallback?.lockId
|
|
3690
3763
|
);
|
|
3691
3764
|
return {
|
|
@@ -3721,6 +3794,9 @@ var IssuerApiAdapter = class {
|
|
|
3721
3794
|
userAddress: authenticatedAddress,
|
|
3722
3795
|
ledger: this.cfg.ledger,
|
|
3723
3796
|
pafiBackendClient: this.cfg.pafiBackendClient,
|
|
3797
|
+
// Audit PACI5-13 — pass the same provider the indexers use so
|
|
3798
|
+
// the receipt fallback gates on the same reorg depth.
|
|
3799
|
+
provider: this.cfg.provider,
|
|
3724
3800
|
onWarning: this.cfg.onWarning
|
|
3725
3801
|
});
|
|
3726
3802
|
}
|
|
@@ -3730,6 +3806,8 @@ var IssuerApiAdapter = class {
|
|
|
3730
3806
|
userAddress: authenticatedAddress,
|
|
3731
3807
|
ledger: this.cfg.ledger,
|
|
3732
3808
|
pafiBackendClient: this.cfg.pafiBackendClient,
|
|
3809
|
+
// Audit PACI5-13 — see claimStatus comment.
|
|
3810
|
+
provider: this.cfg.provider,
|
|
3733
3811
|
onWarning: this.cfg.onWarning
|
|
3734
3812
|
});
|
|
3735
3813
|
}
|
|
@@ -4264,7 +4342,9 @@ var PafiBackendClient = class {
|
|
|
4264
4342
|
if (!config.issuerId) throw new Error("PafiBackendClient: issuerId is required");
|
|
4265
4343
|
if (!config.apiKey) throw new Error("PafiBackendClient: apiKey is required");
|
|
4266
4344
|
this.config = config;
|
|
4267
|
-
this.baseUrl = getPafiServiceUrls(config.chainId
|
|
4345
|
+
this.baseUrl = getPafiServiceUrls(config.chainId, {
|
|
4346
|
+
sponsorRelayer: config.baseUrl
|
|
4347
|
+
}).sponsorRelayer;
|
|
4268
4348
|
}
|
|
4269
4349
|
async requestSponsorship(request) {
|
|
4270
4350
|
const maxAttempts = this.config.retry?.maxAttempts ?? 1;
|
|
@@ -4529,6 +4609,9 @@ function nextBlackoutEndAfter(windows, nowUnixSec) {
|
|
|
4529
4609
|
}
|
|
4530
4610
|
var REDEMPTION_HISTORY_WINDOW_SEC = SECONDS_PER_DAY;
|
|
4531
4611
|
|
|
4612
|
+
// src/redemption/policyProvider.ts
|
|
4613
|
+
import { PafiSdkError as PafiSdkError2 } from "@pafi-dev/core";
|
|
4614
|
+
|
|
4532
4615
|
// src/redemption/settlementClient.ts
|
|
4533
4616
|
import {
|
|
4534
4617
|
getPafiServiceUrls as getPafiServiceUrls2
|
|
@@ -4541,7 +4624,9 @@ var SettlementClient = class {
|
|
|
4541
4624
|
if (!config.issuerId) throw new Error("SettlementClient: issuerId is required");
|
|
4542
4625
|
if (!config.apiKey) throw new Error("SettlementClient: apiKey is required");
|
|
4543
4626
|
this.config = {
|
|
4544
|
-
baseUrl: getPafiServiceUrls2(config.chainId
|
|
4627
|
+
baseUrl: getPafiServiceUrls2(config.chainId, {
|
|
4628
|
+
issuerApi: config.baseUrl
|
|
4629
|
+
}).issuerApi.replace(/\/+$/, ""),
|
|
4545
4630
|
issuerId: config.issuerId,
|
|
4546
4631
|
apiKey: config.apiKey,
|
|
4547
4632
|
fetchTimeoutMs: config.fetchTimeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
@@ -4645,10 +4730,23 @@ function defaultPolicyFor(issuerId) {
|
|
|
4645
4730
|
|
|
4646
4731
|
// src/redemption/policyProvider.ts
|
|
4647
4732
|
var DEFAULT_CACHE_TTL_MS3 = 5 * 60 * 1e3;
|
|
4733
|
+
var PolicyProviderUnavailableError = class extends PafiSdkError2 {
|
|
4734
|
+
code = "POLICY_PROVIDER_UNAVAILABLE";
|
|
4735
|
+
httpStatus = "service_unavailable";
|
|
4736
|
+
details;
|
|
4737
|
+
constructor(issuerId, reason) {
|
|
4738
|
+
super(
|
|
4739
|
+
`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.`
|
|
4740
|
+
);
|
|
4741
|
+
this.details = { issuerId, reason };
|
|
4742
|
+
}
|
|
4743
|
+
};
|
|
4648
4744
|
var PolicyProvider = class {
|
|
4649
4745
|
client;
|
|
4650
4746
|
issuerId;
|
|
4651
4747
|
cacheTtlMs;
|
|
4748
|
+
onFetchFailure;
|
|
4749
|
+
onWarning;
|
|
4652
4750
|
now;
|
|
4653
4751
|
cache = null;
|
|
4654
4752
|
inflight = null;
|
|
@@ -4656,6 +4754,8 @@ var PolicyProvider = class {
|
|
|
4656
4754
|
this.client = new SettlementClient(config);
|
|
4657
4755
|
this.issuerId = config.issuerId;
|
|
4658
4756
|
this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS3;
|
|
4757
|
+
this.onFetchFailure = config.onFetchFailure ?? "fail-closed";
|
|
4758
|
+
this.onWarning = config.onWarning;
|
|
4659
4759
|
this.now = config.now ?? (() => Date.now());
|
|
4660
4760
|
}
|
|
4661
4761
|
async getPolicy() {
|
|
@@ -4688,7 +4788,19 @@ var PolicyProvider = class {
|
|
|
4688
4788
|
};
|
|
4689
4789
|
return { policy: result.policy, source: "settlement" };
|
|
4690
4790
|
}
|
|
4691
|
-
|
|
4791
|
+
const reason = "reason" in result && typeof result.reason === "string" ? result.reason : "unknown";
|
|
4792
|
+
if (this.onFetchFailure === "permissive-default") {
|
|
4793
|
+
this.onWarning?.(
|
|
4794
|
+
"PolicyProvider: settlement-api unreachable, falling back to permissive default. Pre-flight redeem limit is DEGRADED until settlement-api recovers.",
|
|
4795
|
+
{
|
|
4796
|
+
event: "policy_provider_fallback",
|
|
4797
|
+
issuerId: this.issuerId,
|
|
4798
|
+
reason
|
|
4799
|
+
}
|
|
4800
|
+
);
|
|
4801
|
+
return { policy: defaultPolicyFor(this.issuerId), source: "default" };
|
|
4802
|
+
}
|
|
4803
|
+
throw new PolicyProviderUnavailableError(this.issuerId, reason);
|
|
4692
4804
|
}
|
|
4693
4805
|
};
|
|
4694
4806
|
|
|
@@ -4835,6 +4947,15 @@ async function createIssuerService(config) {
|
|
|
4835
4947
|
issuerId: config.redemption.issuerId,
|
|
4836
4948
|
apiKey: config.redemption.apiKey
|
|
4837
4949
|
};
|
|
4950
|
+
if (config.redemption.baseUrl) {
|
|
4951
|
+
policyConfig.baseUrl = config.redemption.baseUrl;
|
|
4952
|
+
}
|
|
4953
|
+
if (config.redemption.onFetchFailure) {
|
|
4954
|
+
policyConfig.onFetchFailure = config.redemption.onFetchFailure;
|
|
4955
|
+
}
|
|
4956
|
+
if (config.redemption.onPolicyWarning) {
|
|
4957
|
+
policyConfig.onWarning = config.redemption.onPolicyWarning;
|
|
4958
|
+
}
|
|
4838
4959
|
if (config.redemption.fetchImpl) policyConfig.fetchImpl = config.redemption.fetchImpl;
|
|
4839
4960
|
if (config.redemption.fetchTimeoutMs !== void 0) {
|
|
4840
4961
|
policyConfig.fetchTimeoutMs = config.redemption.fetchTimeoutMs;
|
|
@@ -5099,7 +5220,7 @@ var MemoryRedemptionHistoryStore = class {
|
|
|
5099
5220
|
};
|
|
5100
5221
|
|
|
5101
5222
|
// src/index.ts
|
|
5102
|
-
var PAFI_ISSUER_SDK_VERSION = true ? "0.
|
|
5223
|
+
var PAFI_ISSUER_SDK_VERSION = true ? "0.33.0" : "dev";
|
|
5103
5224
|
export {
|
|
5104
5225
|
AdapterMisconfiguredError,
|
|
5105
5226
|
AuthError,
|