@pafi-dev/issuer 0.35.1 → 0.38.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 +210 -0
- package/dist/auth-client/index.cjs.map +1 -0
- package/dist/auth-client/index.d.cts +171 -0
- package/dist/auth-client/index.d.ts +171 -0
- package/dist/auth-client/index.js +183 -0
- package/dist/auth-client/index.js.map +1 -0
- package/dist/index.cjs +12 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.d.cts +1 -1
- package/dist/nestjs/index.d.ts +1 -1
- package/dist/{types-CxVXRHLy.d.cts → types-DPqLTJk-.d.cts} +36 -2
- package/dist/{types-CxVXRHLy.d.ts → types-DPqLTJk-.d.ts} +36 -2
- package/dist/wallet-auth/index.cjs +8 -1
- package/dist/wallet-auth/index.cjs.map +1 -1
- package/dist/wallet-auth/index.d.cts +2 -2
- package/dist/wallet-auth/index.d.ts +2 -2
- package/dist/wallet-auth/index.js +8 -1
- package/dist/wallet-auth/index.js.map +1 -1
- package/package.json +25 -12
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import "../chunk-BRKEJJFQ.js";
|
|
2
|
+
|
|
3
|
+
// src/auth-client/pafi-auth-client.ts
|
|
4
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
5
|
+
|
|
6
|
+
// src/auth-client/sign-client-assertion.ts
|
|
7
|
+
import { importJWK, SignJWT } from "jose";
|
|
8
|
+
import { randomUUID } from "crypto";
|
|
9
|
+
async function signClientAssertion(args) {
|
|
10
|
+
const alg = args.alg ?? args.privateJwk.alg ?? "ES256";
|
|
11
|
+
const key = await importJWK(args.privateJwk, alg);
|
|
12
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
13
|
+
return new SignJWT({}).setProtectedHeader({ alg, typ: "JWT", kid: args.privateJwk.kid }).setIssuer(args.clientId).setSubject(args.clientId).setAudience(`${args.gatewayUrl}/v1/token-exchange`).setIssuedAt(now).setExpirationTime(now + 60).setJti(randomUUID()).sign(key);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/auth-client/types.ts
|
|
17
|
+
var PafiAuthError = class extends Error {
|
|
18
|
+
constructor(message, status, code, correlationId) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.status = status;
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.correlationId = correlationId;
|
|
23
|
+
this.name = "PafiAuthError";
|
|
24
|
+
}
|
|
25
|
+
status;
|
|
26
|
+
code;
|
|
27
|
+
correlationId;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/auth-client/pafi-auth-client.ts
|
|
31
|
+
var PafiAuthClient = class {
|
|
32
|
+
constructor(opts) {
|
|
33
|
+
this.opts = opts;
|
|
34
|
+
if (!opts.clientPrivateJwk.kid) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"PafiAuthClient: clientPrivateJwk.kid is required (gateway uses kid to look up the verification key)"
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
40
|
+
this.tokenExchangeAud = `${opts.gatewayUrl}/v1/token-exchange`;
|
|
41
|
+
}
|
|
42
|
+
opts;
|
|
43
|
+
fetchImpl;
|
|
44
|
+
tokenExchangeAud;
|
|
45
|
+
// ───────────────────────────────────────────────────────────────
|
|
46
|
+
// EMAIL OTP — 2-step
|
|
47
|
+
// ───────────────────────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Step 1: ask the gateway to send the user an OTP. Returns the
|
|
50
|
+
* `challengeId` to echo back on {@link verifyEmail}.
|
|
51
|
+
*/
|
|
52
|
+
async startEmail(args) {
|
|
53
|
+
const res = await this.post(
|
|
54
|
+
"/v1/auth/email/start",
|
|
55
|
+
{
|
|
56
|
+
issuer_id: this.opts.issuerId,
|
|
57
|
+
email: args.email
|
|
58
|
+
},
|
|
59
|
+
args.correlationId
|
|
60
|
+
);
|
|
61
|
+
return {
|
|
62
|
+
challengeId: res.challenge_id,
|
|
63
|
+
expiresInSec: res.expires_in
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Step 2: submit the OTP the user received. On success returns
|
|
68
|
+
* {@link AuthSuccess} containing BOTH the long-lived
|
|
69
|
+
* pafi_session_token (issuer verifies via gateway JWKS) AND the
|
|
70
|
+
* short-lived pafi_jwt (issuer FE feeds to Privy).
|
|
71
|
+
*/
|
|
72
|
+
async verifyEmail(args) {
|
|
73
|
+
const res = await this.post(
|
|
74
|
+
"/v1/auth/email/verify",
|
|
75
|
+
{
|
|
76
|
+
challenge_id: args.challengeId,
|
|
77
|
+
otp_code: args.otpCode
|
|
78
|
+
},
|
|
79
|
+
args.correlationId
|
|
80
|
+
);
|
|
81
|
+
return mapAuthSuccess(res);
|
|
82
|
+
}
|
|
83
|
+
// ───────────────────────────────────────────────────────────────
|
|
84
|
+
// GOOGLE — 1-step exchange
|
|
85
|
+
// ───────────────────────────────────────────────────────────────
|
|
86
|
+
/**
|
|
87
|
+
* Hand the gateway an id_token the issuer FE obtained from Google
|
|
88
|
+
* Identity Services (using PAFI's shared client_id). Gateway verifies
|
|
89
|
+
* signature + audience + `email_verified` before resolving identity.
|
|
90
|
+
*/
|
|
91
|
+
async exchangeGoogle(args) {
|
|
92
|
+
const res = await this.post(
|
|
93
|
+
"/v1/auth/google/exchange",
|
|
94
|
+
{
|
|
95
|
+
issuer_id: this.opts.issuerId,
|
|
96
|
+
id_token: args.idToken
|
|
97
|
+
},
|
|
98
|
+
args.correlationId
|
|
99
|
+
);
|
|
100
|
+
return mapAuthSuccess(res);
|
|
101
|
+
}
|
|
102
|
+
// ───────────────────────────────────────────────────────────────
|
|
103
|
+
// KAKAO — 1-step exchange (authorization code)
|
|
104
|
+
// ───────────────────────────────────────────────────────────────
|
|
105
|
+
/**
|
|
106
|
+
* Hand the gateway the authorization code returned by Kakao's
|
|
107
|
+
* redirect. Gateway exchanges with Kakao (server-to-server using
|
|
108
|
+
* PAFI's client_secret), verifies id_token, resolves identity.
|
|
109
|
+
*
|
|
110
|
+
* `redirectUri` must match the URL the FE used when starting the
|
|
111
|
+
* Kakao flow. Falls back to the gateway's KAKAO_REDIRECT_URI when
|
|
112
|
+
* omitted — pass an explicit value for multi-environment FEs.
|
|
113
|
+
*/
|
|
114
|
+
async exchangeKakao(args) {
|
|
115
|
+
const res = await this.post(
|
|
116
|
+
"/v1/auth/kakao/exchange",
|
|
117
|
+
{
|
|
118
|
+
issuer_id: this.opts.issuerId,
|
|
119
|
+
code: args.code,
|
|
120
|
+
...args.redirectUri ? { redirect_uri: args.redirectUri } : {}
|
|
121
|
+
},
|
|
122
|
+
args.correlationId
|
|
123
|
+
);
|
|
124
|
+
return mapAuthSuccess(res);
|
|
125
|
+
}
|
|
126
|
+
// ───────────────────────────────────────────────────────────────
|
|
127
|
+
async post(path, body, correlationId) {
|
|
128
|
+
const assertion = await signClientAssertion({
|
|
129
|
+
gatewayUrl: this.opts.gatewayUrl,
|
|
130
|
+
clientId: this.opts.clientId,
|
|
131
|
+
privateJwk: this.opts.clientPrivateJwk,
|
|
132
|
+
alg: this.opts.alg
|
|
133
|
+
});
|
|
134
|
+
const finalCorrelationId = correlationId ?? `iss-${randomUUID2()}`;
|
|
135
|
+
const res = await this.fetchImpl(`${this.opts.gatewayUrl}${path}`, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: {
|
|
138
|
+
Authorization: `Bearer ${assertion}`,
|
|
139
|
+
"Content-Type": "application/json",
|
|
140
|
+
"X-Correlation-Id": finalCorrelationId
|
|
141
|
+
},
|
|
142
|
+
body: JSON.stringify(body)
|
|
143
|
+
});
|
|
144
|
+
const text = await res.text();
|
|
145
|
+
let parsed;
|
|
146
|
+
try {
|
|
147
|
+
parsed = text ? JSON.parse(text) : {};
|
|
148
|
+
} catch {
|
|
149
|
+
throw new PafiAuthError(
|
|
150
|
+
`Non-JSON response from gateway (${path}): ${text.slice(0, 120)}`,
|
|
151
|
+
res.status,
|
|
152
|
+
"non_json_response",
|
|
153
|
+
finalCorrelationId
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
if (!res.ok) {
|
|
157
|
+
const err = parsed;
|
|
158
|
+
throw new PafiAuthError(
|
|
159
|
+
err.error_description ?? err.error ?? `Gateway returned HTTP ${res.status}`,
|
|
160
|
+
res.status,
|
|
161
|
+
err.error ?? "unknown_error",
|
|
162
|
+
err.correlation_id ?? finalCorrelationId
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return parsed;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
function mapAuthSuccess(res) {
|
|
169
|
+
return {
|
|
170
|
+
pafiSessionToken: res.pafi_session_token,
|
|
171
|
+
pafiJwt: res.pafi_jwt,
|
|
172
|
+
canonicalId: res.canonical_id,
|
|
173
|
+
expiresAt: res.expires_at,
|
|
174
|
+
isFirstLogin: res.is_first_login,
|
|
175
|
+
...res.verified_email ? { verifiedEmail: res.verified_email } : {}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
export {
|
|
179
|
+
PafiAuthClient,
|
|
180
|
+
PafiAuthError,
|
|
181
|
+
signClientAssertion
|
|
182
|
+
};
|
|
183
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/auth-client/pafi-auth-client.ts","../../src/auth-client/sign-client-assertion.ts","../../src/auth-client/types.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { signClientAssertion } from \"./sign-client-assertion\";\nimport {\n PafiAuthError,\n type AuthSuccess,\n type EmailChallenge,\n type PafiAuthClientOptions,\n} from \"./types\";\n\ninterface GatewayErrorResponse {\n error?: string;\n error_description?: string;\n correlation_id?: string;\n}\n\ninterface GatewayAuthSuccessResponse {\n pafi_session_token: string;\n pafi_jwt: string;\n canonical_id: string;\n expires_at: number;\n is_first_login: boolean;\n verified_email?: string;\n}\n\ninterface GatewayChallengeResponse {\n challenge_id: string;\n expires_in: number;\n}\n\n/**\n * Issuer-side client for the PAFI gateway's direct-auth endpoints. Use\n * one instance per issuer backend — the constructor binds gateway URL +\n * issuer id + signing credentials, methods just route to specific\n * endpoints.\n *\n * Each method signs a fresh client_assertion (RFC 7523, 60s TTL) so\n * a leaked one is useless beyond the window + replay-protected by jti.\n *\n * The gateway endpoints invoked here are owned by PAFI — the issuer\n * never sees OTP codes, never holds OAuth client_secrets, never\n * verifies signatures itself. Gateway is the sole authority; this\n * client is purely a transport.\n *\n * Usage in a NestJS issuer backend:\n *\n * @Injectable()\n * export class PafiAuthClientProvider {\n * readonly client: PafiAuthClient;\n * constructor(config: ConfigService) {\n * this.client = new PafiAuthClient({\n * gatewayUrl: config.getOrThrow('PAFI_GATEWAY_URL'),\n * issuerId: config.getOrThrow('PAFI_GATEWAY_ISSUER_ID'),\n * clientId: config.getOrThrow('PAFI_GATEWAY_CLIENT_ID'),\n * clientPrivateJwk: JSON.parse(\n * config.getOrThrow('PAFI_GATEWAY_CLIENT_PRIVATE_JWK_JSON'),\n * ),\n * });\n * }\n * }\n */\nexport class PafiAuthClient {\n private readonly fetchImpl: typeof fetch;\n private readonly tokenExchangeAud: string;\n\n constructor(private readonly opts: PafiAuthClientOptions) {\n if (!opts.clientPrivateJwk.kid) {\n throw new Error(\n \"PafiAuthClient: clientPrivateJwk.kid is required (gateway uses kid to look up the verification key)\",\n );\n }\n this.fetchImpl = opts.fetchImpl ?? fetch;\n this.tokenExchangeAud = `${opts.gatewayUrl}/v1/token-exchange`;\n }\n\n // ───────────────────────────────────────────────────────────────\n // EMAIL OTP — 2-step\n // ───────────────────────────────────────────────────────────────\n\n /**\n * Step 1: ask the gateway to send the user an OTP. Returns the\n * `challengeId` to echo back on {@link verifyEmail}.\n */\n async startEmail(args: {\n email: string;\n correlationId?: string;\n }): Promise<EmailChallenge> {\n const res = await this.post<GatewayChallengeResponse>(\n \"/v1/auth/email/start\",\n {\n issuer_id: this.opts.issuerId,\n email: args.email,\n },\n args.correlationId,\n );\n return {\n challengeId: res.challenge_id,\n expiresInSec: res.expires_in,\n };\n }\n\n /**\n * Step 2: submit the OTP the user received. On success returns\n * {@link AuthSuccess} containing BOTH the long-lived\n * pafi_session_token (issuer verifies via gateway JWKS) AND the\n * short-lived pafi_jwt (issuer FE feeds to Privy).\n */\n async verifyEmail(args: {\n challengeId: string;\n otpCode: string;\n correlationId?: string;\n }): Promise<AuthSuccess> {\n const res = await this.post<GatewayAuthSuccessResponse>(\n \"/v1/auth/email/verify\",\n {\n challenge_id: args.challengeId,\n otp_code: args.otpCode,\n },\n args.correlationId,\n );\n return mapAuthSuccess(res);\n }\n\n // ───────────────────────────────────────────────────────────────\n // GOOGLE — 1-step exchange\n // ───────────────────────────────────────────────────────────────\n\n /**\n * Hand the gateway an id_token the issuer FE obtained from Google\n * Identity Services (using PAFI's shared client_id). Gateway verifies\n * signature + audience + `email_verified` before resolving identity.\n */\n async exchangeGoogle(args: {\n idToken: string;\n correlationId?: string;\n }): Promise<AuthSuccess> {\n const res = await this.post<GatewayAuthSuccessResponse>(\n \"/v1/auth/google/exchange\",\n {\n issuer_id: this.opts.issuerId,\n id_token: args.idToken,\n },\n args.correlationId,\n );\n return mapAuthSuccess(res);\n }\n\n // ───────────────────────────────────────────────────────────────\n // KAKAO — 1-step exchange (authorization code)\n // ───────────────────────────────────────────────────────────────\n\n /**\n * Hand the gateway the authorization code returned by Kakao's\n * redirect. Gateway exchanges with Kakao (server-to-server using\n * PAFI's client_secret), verifies id_token, resolves identity.\n *\n * `redirectUri` must match the URL the FE used when starting the\n * Kakao flow. Falls back to the gateway's KAKAO_REDIRECT_URI when\n * omitted — pass an explicit value for multi-environment FEs.\n */\n async exchangeKakao(args: {\n code: string;\n redirectUri?: string;\n correlationId?: string;\n }): Promise<AuthSuccess> {\n const res = await this.post<GatewayAuthSuccessResponse>(\n \"/v1/auth/kakao/exchange\",\n {\n issuer_id: this.opts.issuerId,\n code: args.code,\n ...(args.redirectUri ? { redirect_uri: args.redirectUri } : {}),\n },\n args.correlationId,\n );\n return mapAuthSuccess(res);\n }\n\n // ───────────────────────────────────────────────────────────────\n\n private async post<T>(\n path: string,\n body: unknown,\n correlationId: string | undefined,\n ): Promise<T> {\n const assertion = await signClientAssertion({\n gatewayUrl: this.opts.gatewayUrl,\n clientId: this.opts.clientId,\n privateJwk: this.opts.clientPrivateJwk,\n alg: this.opts.alg,\n });\n const finalCorrelationId = correlationId ?? `iss-${randomUUID()}`;\n const res = await this.fetchImpl(`${this.opts.gatewayUrl}${path}`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${assertion}`,\n \"Content-Type\": \"application/json\",\n \"X-Correlation-Id\": finalCorrelationId,\n },\n body: JSON.stringify(body),\n });\n const text = await res.text();\n let parsed: unknown;\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n throw new PafiAuthError(\n `Non-JSON response from gateway (${path}): ${text.slice(0, 120)}`,\n res.status,\n \"non_json_response\",\n finalCorrelationId,\n );\n }\n if (!res.ok) {\n const err = parsed as GatewayErrorResponse;\n throw new PafiAuthError(\n err.error_description ??\n err.error ??\n `Gateway returned HTTP ${res.status}`,\n res.status,\n err.error ?? \"unknown_error\",\n err.correlation_id ?? finalCorrelationId,\n );\n }\n return parsed as T;\n }\n}\n\nfunction mapAuthSuccess(res: GatewayAuthSuccessResponse): AuthSuccess {\n return {\n pafiSessionToken: res.pafi_session_token,\n pafiJwt: res.pafi_jwt,\n canonicalId: res.canonical_id,\n expiresAt: res.expires_at,\n isFirstLogin: res.is_first_login,\n ...(res.verified_email ? { verifiedEmail: res.verified_email } : {}),\n };\n}\n","import { importJWK, SignJWT, type JWK } from \"jose\";\nimport { randomUUID } from \"node:crypto\";\n\n/**\n * Mint the RFC 7523 client_assertion JWT the gateway expects in the\n * `Authorization: Bearer …` header of every direct-auth call.\n *\n * Claims:\n * - iss / sub = clientId (RFC 7523 §3: same value for client auth)\n * - aud = `${gatewayUrl}/v1/token-exchange` (exact endpoint URL)\n * — NOTE the gateway also accepts this same audience\n * for the direct-auth endpoints because they live on\n * the same client-auth boundary. Single audience keeps\n * one client_assertion reusable across all gateway\n * endpoints for the duration of its short lifetime.\n * - iat / exp = 60-second window (replay-protected by jti)\n * - jti = random UUID\n *\n * 60-second lifetime is a deliberate trade-off: long enough to absorb\n * clock skew + slow networks, short enough that a stolen assertion is\n * usable only briefly. The gateway's per-jti replay cache means even\n * within that window an assertion is single-use.\n */\nexport async function signClientAssertion(args: {\n gatewayUrl: string;\n clientId: string;\n privateJwk: JWK & { kid: string };\n alg?: string;\n}): Promise<string> {\n const alg = args.alg ?? args.privateJwk.alg ?? \"ES256\";\n const key = await importJWK(args.privateJwk, alg);\n const now = Math.floor(Date.now() / 1000);\n return new SignJWT({})\n .setProtectedHeader({ alg, typ: \"JWT\", kid: args.privateJwk.kid })\n .setIssuer(args.clientId)\n .setSubject(args.clientId)\n .setAudience(`${args.gatewayUrl}/v1/token-exchange`)\n .setIssuedAt(now)\n .setExpirationTime(now + 60)\n .setJti(randomUUID())\n .sign(key);\n}\n","import type { JWK } from \"jose\";\n\n/**\n * Constructor params for {@link PafiAuthClient}. One instance per\n * issuer backend — wraps the issuer's gateway credentials (client_id +\n * private JWK) plus the static config (gateway URL, audience).\n */\nexport interface PafiAuthClientOptions {\n /** Base URL of the PAFI gateway (e.g. `https://id-dev.pacificfinance.org`). */\n gatewayUrl: string;\n /** Issuer identifier registered with the gateway (e.g. `gg56`). */\n issuerId: string;\n /**\n * Gateway client_id assigned at issuer onboarding. Also acts as the\n * `iss`/`sub` of the client_assertion JWT (RFC 7523 §3).\n */\n clientId: string;\n /**\n * Private JWK the issuer uses to sign client_assertion. MUST include\n * `kid` — gateway looks up the matching public JWK by kid.\n */\n clientPrivateJwk: JWK & { kid: string };\n /**\n * Optional fetch override — useful for tests / Node env without\n * global fetch (Node ≥ 18 has it built-in).\n */\n fetchImpl?: typeof fetch;\n /**\n * Optional algorithm override for the client assertion JWT. Default\n * `ES256`. Must match what the gateway expects for this client.\n */\n alg?: \"ES256\" | \"ES384\" | \"RS256\" | \"RS384\" | \"RS512\" | \"EdDSA\";\n}\n\nexport interface AuthSuccess {\n pafiSessionToken: string;\n pafiJwt: string;\n canonicalId: string;\n expiresAt: number;\n isFirstLogin: boolean;\n verifiedEmail?: string;\n}\n\nexport interface EmailChallenge {\n challengeId: string;\n expiresInSec: number;\n}\n\n/**\n * Thrown when the gateway rejects the call. `code` is the gateway's\n * structured `error` field (e.g. `invalid_otp`, `too_many_attempts`,\n * `expired`, `email_not_verified`) — issuers can branch on it to drive\n * UX (e.g. show \"Resend code\" button on `expired`).\n */\nexport class PafiAuthError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly code: string,\n public readonly correlationId?: string,\n ) {\n super(message);\n this.name = \"PafiAuthError\";\n }\n}\n"],"mappings":"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","names":["randomUUID","randomUUID"]}
|
package/dist/index.cjs
CHANGED
|
@@ -4437,6 +4437,7 @@ function createNativePtQuoter(config) {
|
|
|
4437
4437
|
cacheTtlMs = 3e4,
|
|
4438
4438
|
fallbackEthPriceUsd = 3e3,
|
|
4439
4439
|
fallbackPtPriceUsdt = 0.1,
|
|
4440
|
+
failClosed = false,
|
|
4440
4441
|
fetchImpl = globalThis.fetch,
|
|
4441
4442
|
now = () => Date.now()
|
|
4442
4443
|
} = config;
|
|
@@ -4461,6 +4462,11 @@ function createNativePtQuoter(config) {
|
|
|
4461
4462
|
ethPriceCache = { value: answer, expiresAt: ts + cacheTtlMs };
|
|
4462
4463
|
return answer;
|
|
4463
4464
|
} catch (err) {
|
|
4465
|
+
if (failClosed) {
|
|
4466
|
+
throw new Error(
|
|
4467
|
+
`[nativePtQuoter] Chainlink unavailable in fail-closed mode: ${err.message}`
|
|
4468
|
+
);
|
|
4469
|
+
}
|
|
4464
4470
|
console.warn("[nativePtQuoter] Chainlink unavailable, using fallback:", err.message);
|
|
4465
4471
|
return BigInt(Math.round(fallbackEthPriceUsd * 1e8));
|
|
4466
4472
|
}
|
|
@@ -4493,6 +4499,11 @@ function createNativePtQuoter(config) {
|
|
|
4493
4499
|
ptPriceCache = { value, expiresAt: ts + cacheTtlMs };
|
|
4494
4500
|
return value;
|
|
4495
4501
|
} catch (err) {
|
|
4502
|
+
if (failClosed) {
|
|
4503
|
+
throw new Error(
|
|
4504
|
+
`[nativePtQuoter] subgraph miss for ${pointTokenAddress} in fail-closed mode: ${err.message}`
|
|
4505
|
+
);
|
|
4506
|
+
}
|
|
4496
4507
|
console.warn("[nativePtQuoter] subgraph unavailable, using fallback:", err.message);
|
|
4497
4508
|
const ptPerUsdtHuman = 1 / fallbackPtPriceUsdt;
|
|
4498
4509
|
return parseBigDecimalTo18(ptPerUsdtHuman.toFixed(18));
|
|
@@ -5410,7 +5421,7 @@ var MemoryRedemptionHistoryStore = class {
|
|
|
5410
5421
|
};
|
|
5411
5422
|
|
|
5412
5423
|
// src/index.ts
|
|
5413
|
-
var PAFI_ISSUER_SDK_VERSION = true ? "0.
|
|
5424
|
+
var PAFI_ISSUER_SDK_VERSION = true ? "0.38.0" : "dev";
|
|
5414
5425
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5415
5426
|
0 && (module.exports = {
|
|
5416
5427
|
AdapterMisconfiguredError,
|