@xfuel/verify 0.1.0 → 0.2.1

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.js CHANGED
@@ -12,23 +12,51 @@
12
12
  * See docs/specs/RECEIPT_V2_SEMANTICS.md for the verification algorithm.
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.canonicalSignedPayload = exports.BASE_SEPOLIA_RPC_URL = exports.BASE_RPC_URL = exports.ZK_VERIFIER_ADDRESS = exports.computeInferenceBinding = exports.computePaymentCommitment = void 0;
15
+ exports.jwksHostAllowed = exports.isPinnedTrustedJwk = exports.jwkThumbprint = exports.KEY_UNTRUSTED = exports.DEFAULT_TRUSTED_JWKS_HOSTS = exports.DEFAULT_TRUSTED_ISSUER_KIDS = exports.verifyIssuerJws = exports.resolvePinnedIssuerJwk = exports.canonicalSignedPayload = exports.BASE_SEPOLIA_RPC_URL = exports.ZK_VERIFIER_ADDRESS = exports.decodeJwsPayload = exports.receiptPayerClaimsFromEnvelope = exports.verifyPayerBinding = exports.SOLANA_USDC_MINT_DEVNET = exports.SOLANA_USDC_MINT_MAINNET = exports.SOLANA_RPC_URL = exports.fetchSolanaTransaction = exports.inferUsdcOutflowFromBalances = exports.extractUsdcTransfersFromTx = exports.isSolanaBase58Pubkey = exports.parseSolanaPaymentRef = exports.verifySolanaPayer = exports.BASE_RPC_URL = exports.ERC20_TRANSFER_TOPIC = exports.USDC_ADDRESSES = exports.fetchBaseTransactionReceipt = exports.sumUsdcTransfersFromPayer = exports.isEvmAddress = exports.parseBasePaymentRef = exports.verifyBasePayer = exports.computeInferenceBinding = exports.computePaymentCommitment = void 0;
16
+ exports.canonicalPayloadVersion = canonicalPayloadVersion;
16
17
  exports.canonicalIssuerPayload = canonicalIssuerPayload;
18
+ exports.sumUsdcTransfersToPayee = sumUsdcTransfersToPayee;
19
+ exports.reconcileSettledTransfer = reconcileSettledTransfer;
17
20
  exports.verifyIssuerSignature = verifyIssuerSignature;
18
21
  exports.verifyIssuerSignatureWithJwks = verifyIssuerSignatureWithJwks;
19
22
  exports.verifyBinding = verifyBinding;
20
23
  exports.verifyNullifier = verifyNullifier;
21
24
  exports.hashCanonicalPayload = hashCanonicalPayload;
25
+ exports.diffOuterClaims = diffOuterClaims;
26
+ exports.fetchIssuerJwks = fetchIssuerJwks;
27
+ exports.receiptPayerClaims = receiptPayerClaims;
22
28
  exports.verifyReceipt = verifyReceipt;
23
29
  const ethers_1 = require("ethers");
24
30
  const node_crypto_1 = require("node:crypto");
25
31
  const binding_js_1 = require("./binding.js");
26
32
  Object.defineProperty(exports, "computePaymentCommitment", { enumerable: true, get: function () { return binding_js_1.computePaymentCommitment; } });
27
33
  Object.defineProperty(exports, "computeInferenceBinding", { enumerable: true, get: function () { return binding_js_1.computeInferenceBinding; } });
34
+ const base_payer_js_1 = require("./base-payer.js");
35
+ Object.defineProperty(exports, "verifyBasePayer", { enumerable: true, get: function () { return base_payer_js_1.verifyBasePayer; } });
36
+ Object.defineProperty(exports, "parseBasePaymentRef", { enumerable: true, get: function () { return base_payer_js_1.parseBasePaymentRef; } });
37
+ Object.defineProperty(exports, "isEvmAddress", { enumerable: true, get: function () { return base_payer_js_1.isEvmAddress; } });
38
+ Object.defineProperty(exports, "sumUsdcTransfersFromPayer", { enumerable: true, get: function () { return base_payer_js_1.sumUsdcTransfersFromPayer; } });
39
+ Object.defineProperty(exports, "fetchBaseTransactionReceipt", { enumerable: true, get: function () { return base_payer_js_1.fetchBaseTransactionReceipt; } });
40
+ Object.defineProperty(exports, "USDC_ADDRESSES", { enumerable: true, get: function () { return base_payer_js_1.USDC_ADDRESSES; } });
41
+ Object.defineProperty(exports, "ERC20_TRANSFER_TOPIC", { enumerable: true, get: function () { return base_payer_js_1.ERC20_TRANSFER_TOPIC; } });
42
+ Object.defineProperty(exports, "BASE_RPC_URL", { enumerable: true, get: function () { return base_payer_js_1.BASE_RPC_URL; } });
43
+ const solana_payer_js_1 = require("./solana-payer.js");
44
+ Object.defineProperty(exports, "verifySolanaPayer", { enumerable: true, get: function () { return solana_payer_js_1.verifySolanaPayer; } });
45
+ Object.defineProperty(exports, "parseSolanaPaymentRef", { enumerable: true, get: function () { return solana_payer_js_1.parseSolanaPaymentRef; } });
46
+ Object.defineProperty(exports, "isSolanaBase58Pubkey", { enumerable: true, get: function () { return solana_payer_js_1.isSolanaBase58Pubkey; } });
47
+ Object.defineProperty(exports, "extractUsdcTransfersFromTx", { enumerable: true, get: function () { return solana_payer_js_1.extractUsdcTransfersFromTx; } });
48
+ Object.defineProperty(exports, "inferUsdcOutflowFromBalances", { enumerable: true, get: function () { return solana_payer_js_1.inferUsdcOutflowFromBalances; } });
49
+ Object.defineProperty(exports, "fetchSolanaTransaction", { enumerable: true, get: function () { return solana_payer_js_1.fetchSolanaTransaction; } });
50
+ Object.defineProperty(exports, "SOLANA_RPC_URL", { enumerable: true, get: function () { return solana_payer_js_1.SOLANA_RPC_URL; } });
51
+ Object.defineProperty(exports, "SOLANA_USDC_MINT_MAINNET", { enumerable: true, get: function () { return solana_payer_js_1.SOLANA_USDC_MINT_MAINNET; } });
52
+ Object.defineProperty(exports, "SOLANA_USDC_MINT_DEVNET", { enumerable: true, get: function () { return solana_payer_js_1.SOLANA_USDC_MINT_DEVNET; } });
53
+ const payer_js_1 = require("./payer.js");
54
+ Object.defineProperty(exports, "verifyPayerBinding", { enumerable: true, get: function () { return payer_js_1.verifyPayerBinding; } });
55
+ Object.defineProperty(exports, "receiptPayerClaimsFromEnvelope", { enumerable: true, get: function () { return payer_js_1.receiptPayerClaimsFromEnvelope; } });
56
+ Object.defineProperty(exports, "decodeJwsPayload", { enumerable: true, get: function () { return payer_js_1.decodeJwsPayload; } });
57
+ const jws_js_1 = require("./jws.js");
28
58
  /** ZKVerifierSP1 contract on Base mainnet. */
29
59
  exports.ZK_VERIFIER_ADDRESS = '0x9373499645292715a2275A78eD65B14215C41c06';
30
- /** Default Base mainnet RPC. */
31
- exports.BASE_RPC_URL = 'https://mainnet.base.org';
32
60
  /** Base Sepolia testnet RPC. */
33
61
  exports.BASE_SEPOLIA_RPC_URL = 'https://sepolia.base.org';
34
62
  /** Minimal ABI for nullifier check. */
@@ -36,28 +64,28 @@ const ZK_VERIFIER_ABI = [
36
64
  'function usedNullifiers(bytes32) view returns (bool)',
37
65
  ];
38
66
  /**
39
- * Canonical, order-stable payload an issuer signature covers.
40
- * MUST match `canonicalSignedPayload` in services/gateway/src/receipt.js exactly.
41
- *
42
- * Signed fields (15 total):
43
- * 1. task_id
44
- * 2. payment.rail
45
- * 3. payment.ref
46
- * 4. payment.gross_amount
47
- * 5. payment.net_amount
48
- * 6. payment.fee_amount
49
- * 7. payment.protocol_fee_bps ?? payment.fee_bps
50
- * 8. payment.platform_fee
51
- * 9. payment.platform_fee_bps
52
- * 10. provider_cogs.actual
53
- * 11. route.model
54
- * 12. route.model_commitment.commitment
55
- * 13. route.provider
56
- * 14. output.hash
57
- * 15. binding.expected_commitment
67
+ * HMAC payload version.
68
+ * Versions <= 7 keep the historical net/fee field list.
69
+ * Version 8 signs settled_amount plus the internal accounting breakdown.
70
+ * Lockstep with `canonicalPayloadVersion` in services/gateway/src/receipt.js
71
+ * for the v8 list (the v7 list here is the historical 15-field public formula).
58
72
  */
59
- function canonicalIssuerPayload(receipt) {
60
- return JSON.stringify([
73
+ function canonicalPayloadVersion(receipt) {
74
+ const stamped = receipt.hmac_attestation?.payload_version
75
+ ?? receipt.signature?.payload_version;
76
+ if (stamped != null)
77
+ return Number(stamped);
78
+ const issuerV = receipt.issuer_signature?.payload_version;
79
+ if (issuerV != null)
80
+ return Number(issuerV);
81
+ const payment = receipt.payment;
82
+ if (payment?.accounting || (payment && Object.prototype.hasOwnProperty.call(payment, 'settled_amount'))) {
83
+ return 8;
84
+ }
85
+ return 7;
86
+ }
87
+ function canonicalFieldsV7(receipt) {
88
+ return [
61
89
  receipt.task_id ?? null,
62
90
  receipt.payment?.rail ?? null,
63
91
  receipt.payment?.ref ?? null,
@@ -73,7 +101,204 @@ function canonicalIssuerPayload(receipt) {
73
101
  receipt.route?.provider ?? null,
74
102
  receipt.output?.hash ?? null,
75
103
  receipt.binding?.expected_commitment ?? null,
76
- ]);
104
+ ];
105
+ }
106
+ function canonicalFieldsV8(receipt) {
107
+ const breakdown = receipt.payment?.accounting?.internal_breakdown;
108
+ const caller = receipt.caller_binding;
109
+ return [
110
+ receipt.task_id ?? null,
111
+ receipt.payment?.rail ?? null,
112
+ receipt.payment?.ref ?? null,
113
+ receipt.payment?.gross_amount ?? null,
114
+ receipt.payment?.settled_amount ?? null,
115
+ breakdown?.route_margin_bps ?? null,
116
+ breakdown?.route_margin_amount ?? null,
117
+ breakdown?.receipt_floor_amount ?? null,
118
+ breakdown?.provider_cogs_amount ?? null,
119
+ breakdown?.tier2_proof_amount ?? null,
120
+ receipt.provider_cogs?.actual ?? null,
121
+ receipt.route?.model ?? null,
122
+ receipt.route?.model_commitment?.commitment ?? null,
123
+ receipt.route?.provider ?? null,
124
+ receipt.output?.hash ?? null,
125
+ receipt.binding?.expected_commitment ?? null,
126
+ caller?.payer_wallet ?? null,
127
+ caller?.agent_pubkey ?? null,
128
+ caller?.api_key_hash ?? null,
129
+ ];
130
+ }
131
+ /**
132
+ * Canonical, order-stable payload an HMAC issuer signature covers.
133
+ * v8 matches `canonicalSignedPayload` in services/gateway/src/receipt.js.
134
+ * v7 and earlier stay on the historical 15-field list so old signatures verify.
135
+ */
136
+ function canonicalIssuerPayload(receipt) {
137
+ const fields = canonicalPayloadVersion(receipt) >= 8
138
+ ? canonicalFieldsV8(receipt)
139
+ : canonicalFieldsV7(receipt);
140
+ return JSON.stringify(fields);
141
+ }
142
+ function topicAddress(topic) {
143
+ const hex = topic.toLowerCase().replace(/^0x/, '');
144
+ return `0x${hex.slice(-40)}`;
145
+ }
146
+ /**
147
+ * Sum USDC Transfer values to `payee` in a transaction's logs.
148
+ * Non-Transfer logs are ignored. When `usdcAddress` is set, other contracts are ignored.
149
+ */
150
+ function sumUsdcTransfersToPayee(logs, payee, usdcAddress) {
151
+ if (!payee || !(0, base_payer_js_1.isEvmAddress)(payee))
152
+ return null;
153
+ const payeeLower = payee.toLowerCase();
154
+ const usdcLower = usdcAddress?.toLowerCase();
155
+ let total = 0n;
156
+ let found = false;
157
+ for (const log of logs || []) {
158
+ if (usdcLower && log.address?.toLowerCase() !== usdcLower)
159
+ continue;
160
+ const topics = log.topics || [];
161
+ if (topics[0]?.toLowerCase() !== base_payer_js_1.ERC20_TRANSFER_TOPIC)
162
+ continue;
163
+ if (topics.length < 3 || !topics[2])
164
+ continue;
165
+ if (topicAddress(topics[2]) !== payeeLower)
166
+ continue;
167
+ found = true;
168
+ total += BigInt(log.data || '0x0');
169
+ }
170
+ return found ? total : null;
171
+ }
172
+ /**
173
+ * Payment facts for on-chain reconciliation.
174
+ * A JWS is used only after `verifyIssuerSignatureWithJwks` accepts the key.
175
+ * An untrusted or invalid JWS is not decoded into the comparison, and the
176
+ * unsigned outer `payment` copy is not a fallback for those claims.
177
+ * Receipts with no JWS still use the outer payment (legacy envelopes).
178
+ */
179
+ function trustedPaymentForReconcile(receipt, options) {
180
+ const outerVersion = Number(receipt.issuer_signature?.payload_version ?? canonicalPayloadVersion(receipt));
181
+ const jws = receipt.issuer_signature?.jws;
182
+ if (!jws) {
183
+ return { ok: true, version: outerVersion, payment: receipt.payment || {} };
184
+ }
185
+ const verification = verifyIssuerSignatureWithJwks(receipt, options.jwks, {
186
+ trustedKids: options.trustedKids,
187
+ });
188
+ if (!verification.valid || verification.key_trusted === false) {
189
+ return {
190
+ ok: false,
191
+ version: outerVersion,
192
+ reason: verification.reason || jws_js_1.KEY_UNTRUSTED,
193
+ };
194
+ }
195
+ const claims = verification.payload;
196
+ const claimPayment = (claims?.payment && typeof claims.payment === 'object')
197
+ ? claims.payment
198
+ : null;
199
+ if (!claimPayment) {
200
+ return { ok: false, version: outerVersion, reason: 'no_signed_amount' };
201
+ }
202
+ const version = typeof claims?.payload_version === 'number'
203
+ ? claims.payload_version
204
+ : outerVersion;
205
+ return { ok: true, version: Number(version), payment: claimPayment };
206
+ }
207
+ /**
208
+ * Optional on-chain reconciliation. Given a tx receipt's logs, the signed
209
+ * amount the payee was supposed to receive must equal the USDC Transfer to payee.
210
+ *
211
+ * The amount and payee come from JWS claims only after a key-trust check
212
+ * (JWKS by kid, or an embedded key whose thumbprint is a pinned kid). An
213
+ * untrusted signature does not contribute those facts.
214
+ *
215
+ * v8 compares `settled_amount` (falling back to `gross_amount`): that is the
216
+ * on-chain transfer. v7 and earlier compare `net_amount`, which those receipts
217
+ * defined as "amount after fees". A single direct transfer of gross will not
218
+ * match a net that subtracts a fee that never moved on chain.
219
+ */
220
+ function reconcileSettledTransfer(receipt, logs, options = {}) {
221
+ const trusted = trustedPaymentForReconcile(receipt, options);
222
+ if (!trusted.ok) {
223
+ return {
224
+ checked: false,
225
+ matches: false,
226
+ payload_version: trusted.version,
227
+ signed_field: null,
228
+ signed_amount: null,
229
+ transfer_amount: null,
230
+ payee: null,
231
+ reason: trusted.reason,
232
+ };
233
+ }
234
+ const { version, payment } = trusted;
235
+ const payee = payment.payee ?? null;
236
+ let signed_field = null;
237
+ let signed_amount = null;
238
+ if (version >= 8) {
239
+ if (payment.settled_amount != null && payment.settled_amount !== '') {
240
+ signed_field = 'settled_amount';
241
+ signed_amount = String(payment.settled_amount);
242
+ }
243
+ else if (payment.gross_amount != null && payment.gross_amount !== '') {
244
+ signed_field = 'gross_amount';
245
+ signed_amount = String(payment.gross_amount);
246
+ }
247
+ }
248
+ else {
249
+ signed_field = 'net_amount';
250
+ signed_amount = payment.net_amount != null ? String(payment.net_amount) : null;
251
+ }
252
+ if (!payee || !(0, base_payer_js_1.isEvmAddress)(payee)) {
253
+ return {
254
+ checked: false,
255
+ matches: false,
256
+ payload_version: version,
257
+ signed_field,
258
+ signed_amount,
259
+ transfer_amount: null,
260
+ payee,
261
+ reason: 'no_payee',
262
+ };
263
+ }
264
+ if (signed_amount == null) {
265
+ return {
266
+ checked: false,
267
+ matches: false,
268
+ payload_version: version,
269
+ signed_field,
270
+ signed_amount,
271
+ transfer_amount: null,
272
+ payee,
273
+ reason: 'no_signed_amount',
274
+ };
275
+ }
276
+ const transfer = sumUsdcTransfersToPayee(logs, payee, options.usdcAddress);
277
+ if (transfer == null) {
278
+ return {
279
+ checked: true,
280
+ matches: false,
281
+ payload_version: version,
282
+ signed_field,
283
+ signed_amount,
284
+ transfer_amount: null,
285
+ payee,
286
+ reason: 'no_transfer_to_payee',
287
+ };
288
+ }
289
+ const matches = BigInt(signed_amount) === transfer;
290
+ return {
291
+ checked: true,
292
+ matches,
293
+ payload_version: version,
294
+ signed_field,
295
+ signed_amount,
296
+ transfer_amount: transfer.toString(),
297
+ payee,
298
+ reason: matches
299
+ ? undefined
300
+ : `amount_mismatch: signed ${signed_field} ${signed_amount} !== transfer ${transfer.toString()}`,
301
+ };
77
302
  }
78
303
  /**
79
304
  * Verify a receipt's ES256 issuer signature against a JWK (public key).
@@ -123,28 +348,122 @@ function verifyIssuerSignature(receipt, jwk) {
123
348
  * @param receipt - Receipt JSON with issuer_signature
124
349
  * @param jwks - JWKS with keys array
125
350
  */
126
- function verifyIssuerSignatureWithJwks(receipt, jwks) {
351
+ function jwksCandidates(jwks, kid) {
352
+ const keys = jwks?.keys || [];
353
+ const es256 = keys.filter((k) => (0, jws_js_1.isEs256PublicJwk)(k) && (k.alg == null || k.alg === 'ES256'));
354
+ if (!kid)
355
+ return es256;
356
+ return es256.filter((k) => k.kid === kid);
357
+ }
358
+ /**
359
+ * Verify an issuer signature against trusted keys only.
360
+ *
361
+ * Trust, in order:
362
+ * 1. A JWKS entry matched by `kid` (supplied file or fetched issuer JWKS).
363
+ * 2. An embedded `issuer_jwk` whose RFC 7638 thumbprint equals a pinned trusted kid.
364
+ *
365
+ * An embedded key that merely verifies the bytes is `key untrusted`.
366
+ */
367
+ function verifyIssuerSignatureWithJwks(receipt, jwks, options = {}) {
127
368
  const sig = receipt.issuer_signature;
128
- if (!sig || !sig.value) {
129
- return { checked: false, valid: false, reason: 'no_issuer_signature' };
369
+ if (!sig) {
370
+ return { checked: false, valid: false, key_trusted: false, reason: 'no_issuer_signature' };
130
371
  }
131
- if (!jwks || !Array.isArray(jwks.keys) || jwks.keys.length === 0) {
132
- return { checked: false, valid: false, reason: 'empty_jwks' };
372
+ const trustedKids = options.trustedKids ?? jws_js_1.DEFAULT_TRUSTED_ISSUER_KIDS;
373
+ const header = sig.jws ? (0, jws_js_1.readJwsHeader)(sig.jws) : null;
374
+ const kid = header?.kid || sig.kid;
375
+ const embedded = (0, jws_js_1.resolvePinnedIssuerJwk)(receipt);
376
+ const pinned = !!(embedded && (0, jws_js_1.isPinnedTrustedJwk)(embedded, trustedKids));
377
+ if (sig.jws) {
378
+ const fromJwks = jwksCandidates(jwks, kid);
379
+ for (const jwk of fromJwks) {
380
+ const jwsResult = (0, jws_js_1.verifyIssuerJws)(sig.jws, jwk);
381
+ if (jwsResult.valid) {
382
+ return {
383
+ checked: true,
384
+ valid: true,
385
+ key_trusted: true,
386
+ trust: 'jwks',
387
+ kid: jwk.kid || kid,
388
+ payload: jwsResult.payload,
389
+ };
390
+ }
391
+ }
392
+ if (pinned && embedded) {
393
+ const pinResult = (0, jws_js_1.verifyIssuerJws)(sig.jws, embedded);
394
+ if (pinResult.valid) {
395
+ return {
396
+ checked: true,
397
+ valid: true,
398
+ key_trusted: true,
399
+ trust: 'pinned_kid',
400
+ kid: embedded.kid || (0, jws_js_1.jwkThumbprint)(embedded),
401
+ payload: pinResult.payload,
402
+ };
403
+ }
404
+ return {
405
+ checked: true,
406
+ valid: false,
407
+ key_trusted: true,
408
+ kid: kid || embedded.kid,
409
+ reason: pinResult.reason || 'signature_invalid',
410
+ };
411
+ }
412
+ if (embedded && (0, jws_js_1.verifyIssuerJws)(sig.jws, embedded).valid) {
413
+ return {
414
+ checked: true,
415
+ valid: false,
416
+ key_trusted: false,
417
+ kid: embedded.kid || kid,
418
+ reason: jws_js_1.KEY_UNTRUSTED,
419
+ };
420
+ }
421
+ if (fromJwks.length === 0 && !pinned) {
422
+ return {
423
+ checked: true,
424
+ valid: false,
425
+ key_trusted: false,
426
+ kid,
427
+ reason: jws_js_1.KEY_UNTRUSTED,
428
+ };
429
+ }
430
+ return {
431
+ checked: true,
432
+ valid: false,
433
+ key_trusted: fromJwks.length > 0 || pinned,
434
+ kid,
435
+ reason: 'signature_invalid',
436
+ };
437
+ }
438
+ if (!sig.value) {
439
+ return { checked: false, valid: false, key_trusted: false, reason: 'no_issuer_signature' };
440
+ }
441
+ if (!jwks || !Array.isArray(jwks.keys)) {
442
+ return {
443
+ checked: false,
444
+ valid: false,
445
+ key_trusted: false,
446
+ kid: sig.kid,
447
+ reason: 'JWKS not provided — pass jwks option to verify issuer signature',
448
+ };
449
+ }
450
+ if (jwks.keys.length === 0) {
451
+ return { checked: false, valid: false, key_trusted: false, kid: sig.kid, reason: 'empty_jwks' };
133
452
  }
134
453
  // Find matching key by kid, or try all ES256 keys if no kid on signature
135
454
  const candidates = sig.kid
136
455
  ? jwks.keys.filter(k => k.kid === sig.kid && k.alg === 'ES256')
137
456
  : jwks.keys.filter(k => k.alg === 'ES256');
138
457
  if (candidates.length === 0) {
139
- return { checked: false, valid: false, reason: 'no_matching_key' };
458
+ return { checked: false, valid: false, key_trusted: false, kid: sig.kid, reason: 'no_matching_key' };
140
459
  }
141
460
  for (const jwk of candidates) {
142
461
  const result = verifyIssuerSignature(receipt, jwk);
143
462
  if (result.valid) {
144
- return result;
463
+ return { ...result, key_trusted: true, trust: 'jwks', kid: jwk.kid || sig.kid };
145
464
  }
146
465
  }
147
- return { checked: true, valid: false, reason: 'signature_invalid' };
466
+ return { checked: true, valid: false, key_trusted: true, kid: sig.kid, reason: 'signature_invalid' };
148
467
  }
149
468
  /**
150
469
  * Verify a receipt's payment binding locally (no network required).
@@ -152,8 +471,18 @@ function verifyIssuerSignatureWithJwks(receipt, jwks) {
152
471
  * This recomputes the commitment from the receipt's fields and compares
153
472
  * it to the stored `binding.expected_commitment`.
154
473
  */
474
+ function noCommitmentReason(rail) {
475
+ if (rail === 'unmetered' || rail === 'tfuel') {
476
+ return 'No binding present on receipt (unmetered or TFUEL rail)';
477
+ }
478
+ if (rail === 'usdc' || rail === 'reported') {
479
+ return 'No payment-binding commitment (expected_commitment is null)';
480
+ }
481
+ return 'No binding present on receipt (may be unmetered or TFUEL rail)';
482
+ }
155
483
  function verifyBinding(receipt) {
156
484
  const binding = receipt.binding;
485
+ const railHint = binding?.rail || receipt.payment?.rail || null;
157
486
  if (!binding) {
158
487
  return {
159
488
  verified: false,
@@ -161,7 +490,17 @@ function verifyBinding(receipt) {
161
490
  recomputed: null,
162
491
  matches: false,
163
492
  covers: [],
164
- reason: 'No binding present on receipt (may be unmetered or TFUEL rail)',
493
+ reason: noCommitmentReason(railHint),
494
+ };
495
+ }
496
+ if (binding.expected_commitment == null || binding.expected_commitment === '') {
497
+ return {
498
+ verified: false,
499
+ expected: null,
500
+ recomputed: null,
501
+ matches: false,
502
+ covers: binding.covers || [],
503
+ reason: noCommitmentReason(railHint),
165
504
  };
166
505
  }
167
506
  const paymentRef = receipt.payment?.ref ?? null;
@@ -241,7 +580,7 @@ async function verifyNullifier(receipt, options = {}) {
241
580
  reason: 'Invalid nullifier format',
242
581
  };
243
582
  }
244
- const rpcUrl = options.rpcUrl || exports.BASE_RPC_URL;
583
+ const rpcUrl = options.rpcUrl || base_payer_js_1.BASE_RPC_URL;
245
584
  const verifierAddress = options.verifierAddress || exports.ZK_VERIFIER_ADDRESS;
246
585
  try {
247
586
  const provider = new ethers_1.JsonRpcProvider(rpcUrl);
@@ -274,33 +613,240 @@ exports.canonicalSignedPayload = canonicalIssuerPayload;
274
613
  function hashCanonicalPayload(receipt) {
275
614
  return (0, ethers_1.keccak256)((0, ethers_1.toUtf8Bytes)((0, exports.canonicalSignedPayload)(receipt)));
276
615
  }
616
+ function claimString(value) {
617
+ if (value == null || value === '')
618
+ return null;
619
+ return String(value);
620
+ }
621
+ function asRecord(value) {
622
+ if (!value || typeof value !== 'object' || Array.isArray(value))
623
+ return null;
624
+ return value;
625
+ }
626
+ function normFact(value, address = false) {
627
+ const text = claimString(value);
628
+ if (text == null)
629
+ return null;
630
+ return address ? text.toLowerCase() : text;
631
+ }
632
+ /**
633
+ * Fields a verifier may quote. Compared only when both the unsigned outer
634
+ * copy and the JWS payload carry a value.
635
+ */
636
+ const CLAIM_COMPARE = [
637
+ { field: 'task_id', outer: (r) => r.task_id, signed: (c) => c.task_id },
638
+ { field: 'payment.rail', outer: (r) => r.payment?.rail, signed: (c) => asRecord(c.payment)?.rail },
639
+ { field: 'payment.ref', outer: (r) => r.payment?.ref, signed: (c) => asRecord(c.payment)?.ref },
640
+ { field: 'payment.gross_amount', outer: (r) => r.payment?.gross_amount, signed: (c) => asRecord(c.payment)?.gross_amount },
641
+ { field: 'payment.settled_amount', outer: (r) => r.payment?.settled_amount, signed: (c) => asRecord(c.payment)?.settled_amount },
642
+ { field: 'payment.net_amount', outer: (r) => r.payment?.net_amount, signed: (c) => asRecord(c.payment)?.net_amount },
643
+ { field: 'payment.asset', outer: (r) => r.payment?.asset, signed: (c) => asRecord(c.payment)?.asset, address: true },
644
+ { field: 'payment.payee', outer: (r) => r.payment?.payee, signed: (c) => asRecord(c.payment)?.payee, address: true },
645
+ { field: 'caller_binding.payer_wallet', outer: (r) => r.caller_binding?.payer_wallet, signed: (c) => asRecord(c.caller_binding)?.payer_wallet, address: true },
646
+ { field: 'caller_binding.agent_pubkey', outer: (r) => r.caller_binding?.agent_pubkey, signed: (c) => asRecord(c.caller_binding)?.agent_pubkey },
647
+ { field: 'caller_binding.api_key_hash', outer: (r) => r.caller_binding?.api_key_hash, signed: (c) => asRecord(c.caller_binding)?.api_key_hash },
648
+ { field: 'route.model', outer: (r) => r.route?.model, signed: (c) => asRecord(c.route)?.model },
649
+ { field: 'route.provider', outer: (r) => r.route?.provider, signed: (c) => asRecord(c.route)?.provider },
650
+ { field: 'output.hash', outer: (r) => r.output?.hash, signed: (c) => asRecord(c.output)?.hash },
651
+ { field: 'binding.expected_commitment', outer: (r) => r.binding?.expected_commitment, signed: (c) => asRecord(c.binding)?.expected_commitment },
652
+ { field: 'provider_cogs.actual', outer: (r) => r.provider_cogs?.actual, signed: (c) => asRecord(c.provider_cogs)?.actual },
653
+ ];
654
+ /** Flag unsigned outer copies that disagree with the JWS payload. */
655
+ function diffOuterClaims(receipt, claims) {
656
+ if (!claims)
657
+ return [];
658
+ const mismatches = [];
659
+ for (const spec of CLAIM_COMPARE) {
660
+ const outer = normFact(spec.outer(receipt), spec.address);
661
+ const signed = normFact(spec.signed(claims), spec.address);
662
+ if (outer == null || signed == null || outer === signed)
663
+ continue;
664
+ mismatches.push({
665
+ field: spec.field,
666
+ outer: claimString(spec.outer(receipt)),
667
+ signed: claimString(spec.signed(claims)),
668
+ });
669
+ }
670
+ return mismatches;
671
+ }
672
+ function receiptViewFromClaims(receipt, claims) {
673
+ const payment = asRecord(claims.payment);
674
+ const binding = asRecord(claims.binding);
675
+ const route = asRecord(claims.route);
676
+ const output = asRecord(claims.output);
677
+ const caller = asRecord(claims.caller_binding);
678
+ const modelCommitment = claimString(route?.model_commitment);
679
+ return {
680
+ ...receipt,
681
+ task_id: claimString(claims.task_id) || receipt.task_id,
682
+ payment: {
683
+ rail: claimString(payment?.rail) ?? undefined,
684
+ ref: claimString(payment?.ref),
685
+ gross_amount: claimString(payment?.gross_amount),
686
+ settled_amount: claimString(payment?.settled_amount),
687
+ net_amount: claimString(payment?.net_amount),
688
+ asset: claimString(payment?.asset),
689
+ payee: claimString(payment?.payee),
690
+ fee_amount: claimString(payment?.fee_amount),
691
+ protocol_fee_bps: typeof payment?.protocol_fee_bps === 'number' ? payment.protocol_fee_bps : null,
692
+ platform_fee: claimString(payment?.platform_fee),
693
+ platform_fee_bps: typeof payment?.platform_fee_bps === 'number' ? payment.platform_fee_bps : null,
694
+ },
695
+ route: {
696
+ model: claimString(route?.model) ?? undefined,
697
+ provider: claimString(route?.provider) ?? undefined,
698
+ model_commitment: modelCommitment ? { commitment: modelCommitment } : null,
699
+ },
700
+ output: output?.hash ? { hash: claimString(output.hash) ?? undefined } : null,
701
+ caller_binding: caller
702
+ ? {
703
+ payer_wallet: claimString(caller.payer_wallet),
704
+ agent_pubkey: claimString(caller.agent_pubkey),
705
+ api_key_hash: claimString(caller.api_key_hash),
706
+ }
707
+ : null,
708
+ provider_cogs: asRecord(claims.provider_cogs)?.actual != null
709
+ ? { actual: claimString(asRecord(claims.provider_cogs)?.actual) ?? undefined }
710
+ : receipt.provider_cogs,
711
+ binding: {
712
+ expected_commitment: binding && 'expected_commitment' in binding
713
+ ? (claimString(binding.expected_commitment) ?? undefined)
714
+ : undefined,
715
+ amount: claimString(binding?.amount)
716
+ || claimString(payment?.net_amount)
717
+ || claimString(payment?.gross_amount)
718
+ || '0',
719
+ rail: claimString(binding?.rail) || claimString(payment?.rail) || undefined,
720
+ covers: Array.isArray(binding?.covers) ? binding.covers : ['payment', 'settlement'],
721
+ model_commitment: claimString(binding?.model_commitment) || modelCommitment,
722
+ output_hash: claimString(binding?.output_hash) || claimString(output?.hash),
723
+ },
724
+ };
725
+ }
726
+ function payerClaimsFromPayload(payload) {
727
+ const payment = asRecord(payload.payment);
728
+ const caller = asRecord(payload.caller_binding);
729
+ return {
730
+ payment: {
731
+ ref: claimString(payment?.ref),
732
+ gross_amount: claimString(payment?.gross_amount),
733
+ payee: claimString(payment?.payee),
734
+ asset: claimString(payment?.asset),
735
+ rail: claimString(payment?.rail),
736
+ },
737
+ caller_binding: caller ? { payer_wallet: claimString(caller.payer_wallet) } : null,
738
+ };
739
+ }
740
+ function factsFromClaims(claims) {
741
+ if (!claims) {
742
+ return { hub: null, model: null, amount_usdc: null, tx: null, output_hash: null };
743
+ }
744
+ const payment = asRecord(claims.payment);
745
+ const route = asRecord(claims.route);
746
+ const output = asRecord(claims.output);
747
+ return {
748
+ hub: claimString(route?.provider),
749
+ model: claimString(route?.model),
750
+ amount_usdc: claimString(payment?.gross_amount),
751
+ tx: claimString(payment?.ref),
752
+ output_hash: claimString(output?.hash),
753
+ };
754
+ }
755
+ function issuerJwksUri(receipt) {
756
+ const fromVerification = receipt.verification?.jwks_uri;
757
+ if (typeof fromVerification === 'string' && fromVerification.startsWith('https://'))
758
+ return fromVerification;
759
+ const jws = receipt.issuer_signature?.jws;
760
+ const header = jws ? (0, jws_js_1.readJwsHeader)(jws) : null;
761
+ if (header?.jku && header.jku.startsWith('https://'))
762
+ return header.jku;
763
+ return null;
764
+ }
765
+ function httpsUrl(uri) {
766
+ try {
767
+ return new URL(uri).protocol === 'https:';
768
+ }
769
+ catch {
770
+ return false;
771
+ }
772
+ }
773
+ function mergeJwks(primary, extra) {
774
+ const keys = [...(primary?.keys || []), ...(extra?.keys || [])];
775
+ if (keys.length === 0)
776
+ return undefined;
777
+ const seen = new Set();
778
+ const deduped = [];
779
+ for (const key of keys) {
780
+ const id = `${key.kid || ''}:${key.x || ''}:${key.y || ''}`;
781
+ if (seen.has(id))
782
+ continue;
783
+ seen.add(id);
784
+ deduped.push(key);
785
+ }
786
+ return { keys: deduped };
787
+ }
788
+ /** Fetch a JWKS document. Caller decides whether the URL is a trust root. */
789
+ async function fetchIssuerJwks(uri, fetchImpl = globalThis.fetch) {
790
+ if (!httpsUrl(uri))
791
+ throw new Error('jwks_uri must be https');
792
+ const res = await fetchImpl(uri);
793
+ if (!res.ok)
794
+ throw new Error(`jwks_fetch_failed: ${res.status}`);
795
+ const body = await res.json();
796
+ if (!body || !Array.isArray(body.keys))
797
+ throw new Error('jwks_malformed');
798
+ return body;
799
+ }
277
800
  /**
278
- * Full receipt verification — binding + issuer signature + optional nullifier check.
801
+ * Full receipt verification — trusted issuer signature, signed-claim facts,
802
+ * optional on-chain payer and nullifier checks.
279
803
  *
280
- * @param receipt The Chit402 receipt JSON
281
- * @param options.jwks JWKS for issuer signature verification (no network fetch)
282
- * @param options.checkNullifier Whether to verify nullifier on-chain (requires network)
283
- * @param options.rpcUrl RPC URL for on-chain checks
804
+ * Facts (amount, payer, payee, asset, tx, model) are taken only from a JWS
805
+ * that verified under a trusted key. The embedded `issuer_jwk` is not trusted
806
+ * unless its RFC 7638 thumbprint is a pinned kid, or the same key is in a JWKS.
284
807
  */
808
+ /** Extract JWS claims for payer binding. Prefers the JWS payload over the outer copy. */
809
+ function receiptPayerClaims(receipt) {
810
+ return (0, payer_js_1.receiptPayerClaimsFromEnvelope)(receipt);
811
+ }
285
812
  async function verifyReceipt(receipt, options = {}) {
286
813
  const errors = [];
287
- // Verify binding locally
288
- const binding = verifyBinding(receipt);
289
- if (!binding.matches && binding.expected) {
290
- errors.push('Payment binding mismatch');
814
+ const trustedKids = options.trustedKids ?? jws_js_1.DEFAULT_TRUSTED_ISSUER_KIDS;
815
+ const trustedHosts = options.trustedJwksHosts ?? jws_js_1.DEFAULT_TRUSTED_JWKS_HOSTS;
816
+ let jwks = options.jwks;
817
+ if (options.jwksUri || options.fetchJwks) {
818
+ const uri = options.jwksUri || issuerJwksUri(receipt);
819
+ const allowed = !!uri && (options.jwksUri ? httpsUrl(uri) : (0, jws_js_1.jwksHostAllowed)(uri, trustedHosts));
820
+ if (uri && allowed) {
821
+ try {
822
+ const fetched = await fetchIssuerJwks(uri, options.fetchImpl);
823
+ jwks = mergeJwks(jwks, fetched);
824
+ }
825
+ catch (err) {
826
+ errors.push(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
827
+ }
828
+ }
829
+ else if (options.jwksUri) {
830
+ errors.push('JWKS URI rejected — only https URLs are fetched');
831
+ }
832
+ else if (options.fetchJwks && uri) {
833
+ errors.push(`JWKS host untrusted: ${uri}`);
834
+ }
291
835
  }
292
- // Verify issuer signature if JWKS provided
293
836
  let issuer_signature;
294
- if (options.jwks) {
295
- issuer_signature = verifyIssuerSignatureWithJwks(receipt, options.jwks);
837
+ if (receipt.issuer_signature?.jws || receipt.issuer_signature?.value) {
838
+ issuer_signature = verifyIssuerSignatureWithJwks(receipt, jwks, { trustedKids });
296
839
  if (issuer_signature.checked && !issuer_signature.valid) {
297
- errors.push(`Issuer signature invalid: ${issuer_signature.reason}`);
840
+ errors.push(issuer_signature.reason === jws_js_1.KEY_UNTRUSTED
841
+ ? jws_js_1.KEY_UNTRUSTED
842
+ : `Issuer signature invalid: ${issuer_signature.reason}`);
298
843
  }
299
844
  }
300
845
  else if (receipt.issuer_signature) {
301
846
  issuer_signature = {
302
847
  checked: false,
303
848
  valid: false,
849
+ key_trusted: false,
304
850
  kid: receipt.issuer_signature.kid,
305
851
  reason: 'JWKS not provided — pass jwks option to verify issuer signature',
306
852
  };
@@ -309,10 +855,106 @@ async function verifyReceipt(receipt, options = {}) {
309
855
  issuer_signature = {
310
856
  checked: false,
311
857
  valid: false,
858
+ key_trusted: false,
312
859
  reason: 'No issuer signature present on receipt',
313
860
  };
314
861
  }
315
- // Verify nullifier on-chain if requested
862
+ const decoded = receipt.issuer_signature?.jws
863
+ ? (0, payer_js_1.decodeJwsPayload)(receipt.issuer_signature.jws)
864
+ : null;
865
+ const claim_mismatches = diffOuterClaims(receipt, decoded);
866
+ for (const mismatch of claim_mismatches) {
867
+ errors.push(`outer/signed mismatch: ${mismatch.field} (outer ${mismatch.outer}, signed ${mismatch.signed})`);
868
+ }
869
+ const verifiedClaims = issuer_signature.valid ? issuer_signature.payload : undefined;
870
+ let binding;
871
+ if (receipt.issuer_signature?.jws) {
872
+ if (verifiedClaims) {
873
+ binding = verifyBinding(receiptViewFromClaims(receipt, verifiedClaims));
874
+ }
875
+ else {
876
+ binding = {
877
+ verified: false,
878
+ expected: null,
879
+ recomputed: null,
880
+ matches: false,
881
+ covers: [],
882
+ reason: issuer_signature.reason === jws_js_1.KEY_UNTRUSTED
883
+ ? 'Binding not checked — issuer key untrusted'
884
+ : 'Binding not checked — issuer signature not verified',
885
+ };
886
+ }
887
+ }
888
+ else {
889
+ binding = verifyBinding(receipt);
890
+ }
891
+ if (!binding.matches && binding.expected) {
892
+ errors.push('Payment binding mismatch');
893
+ }
894
+ const signedPayerClaims = verifiedClaims ? payerClaimsFromPayload(verifiedClaims) : null;
895
+ let payer;
896
+ if (options.checkPayer) {
897
+ if (!signedPayerClaims) {
898
+ payer = {
899
+ checked: false,
900
+ valid: false,
901
+ reason: 'Payer facts come from verified signed claims — issuer signature is not trusted',
902
+ };
903
+ }
904
+ else {
905
+ const payerResult = await (0, payer_js_1.verifyPayerBinding)(signedPayerClaims, {
906
+ rpcUrl: options.rpcUrl,
907
+ solanaRpcUrl: options.solanaRpcUrl,
908
+ fetchSolanaTransaction: options.fetchSolanaTransaction,
909
+ fetchBaseReceipt: options.fetchBaseReceipt,
910
+ });
911
+ payer = {
912
+ checked: payerResult.checked,
913
+ valid: payerResult.valid,
914
+ rail: payerResult.rail,
915
+ reason: payerResult.reason,
916
+ payer_wallet: payerResult.payerWallet ?? signedPayerClaims.caller_binding?.payer_wallet ?? null,
917
+ payee: signedPayerClaims.payment?.payee ?? null,
918
+ asset: signedPayerClaims.payment?.asset ?? null,
919
+ amount: payerResult.expectedAmount ?? signedPayerClaims.payment?.gross_amount ?? null,
920
+ };
921
+ if (payerResult.checked && !payerResult.valid) {
922
+ errors.push(`Payer binding mismatch: ${payerResult.reason}`);
923
+ }
924
+ }
925
+ }
926
+ else if (signedPayerClaims?.caller_binding?.payer_wallet && signedPayerClaims.payment?.ref) {
927
+ payer = {
928
+ checked: false,
929
+ valid: false,
930
+ payer_wallet: signedPayerClaims.caller_binding.payer_wallet,
931
+ payee: signedPayerClaims.payment.payee ?? null,
932
+ asset: signedPayerClaims.payment.asset ?? null,
933
+ amount: signedPayerClaims.payment.gross_amount ?? null,
934
+ reason: 'On-chain payer check not requested — pass checkPayer: true',
935
+ };
936
+ }
937
+ else if (!receipt.issuer_signature?.jws && receipt.caller_binding?.payer_wallet && receipt.payment?.ref) {
938
+ payer = {
939
+ checked: false,
940
+ valid: false,
941
+ reason: 'On-chain payer check not requested — pass checkPayer: true',
942
+ };
943
+ }
944
+ else if (receipt.issuer_signature?.jws && !issuer_signature.valid) {
945
+ payer = {
946
+ checked: false,
947
+ valid: false,
948
+ reason: 'Payer facts come from verified signed claims — issuer signature is not trusted',
949
+ };
950
+ }
951
+ else {
952
+ payer = {
953
+ checked: false,
954
+ valid: false,
955
+ reason: 'No payer_wallet or payment.ref to verify',
956
+ };
957
+ }
316
958
  let nullifier;
317
959
  if (options.checkNullifier && receipt.proof?.nullifier) {
318
960
  nullifier = await verifyNullifier(receipt, {
@@ -333,73 +975,90 @@ async function verifyReceipt(receipt, options = {}) {
333
975
  : 'On-chain check not requested',
334
976
  };
335
977
  }
336
- // Extract frozen fields
337
- const hub = receipt.route?.provider ?? null;
338
- const model = receipt.route?.model ?? null;
339
- const amount_usdc = receipt.payment?.gross_amount ?? null;
340
- const tx = receipt.payment?.ref ?? null;
341
- const output_hash = receipt.output?.hash ?? null;
342
- // Determine overall status with strict signature semantics:
343
- // - If JWKS provided and signature invalid/kid mismatch/no matching key → 'failed'
344
- // - If receipt has issuer_signature but no JWKS provided → 'partial' (cannot verify)
345
- // - Unsigned receipts can be 'partial' or 'verified' based on binding
346
- let overall;
347
- // Check for signature verification failure (JWKS provided but signature invalid)
348
- const jwksProvided = !!options.jwks;
978
+ const facts = factsFromClaims(verifiedClaims);
349
979
  const hasIssuerSig = !!receipt.issuer_signature;
350
- const issuerSigInvalid = jwksProvided && hasIssuerSig && !issuer_signature.valid;
351
- // Binding failure
352
- const bindingFailed = binding.expected && !binding.matches;
353
- if (issuerSigInvalid || bindingFailed) {
354
- // JWKS provided but signature invalid, OR binding mismatch → failed
980
+ const jwksWasSupplied = !!(options.jwks || options.jwksUri || (options.fetchJwks && jwks));
981
+ const signatureFailed = hasIssuerSig && !issuer_signature.valid && (!!issuer_signature.checked
982
+ || jwksWasSupplied
983
+ || issuer_signature.reason === jws_js_1.KEY_UNTRUSTED
984
+ || issuer_signature.reason === 'no_matching_key'
985
+ || issuer_signature.reason === 'empty_jwks');
986
+ const mismatchFailed = claim_mismatches.length > 0;
987
+ const bindingFailed = !!(binding.expected && !binding.matches);
988
+ const payerFailed = !!(options.checkPayer && payer.checked && !payer.valid);
989
+ const nullifierFailed = !!(options.checkNullifier
990
+ && receipt.proof?.nullifier
991
+ && nullifier.anchored === false);
992
+ const signatureUnchecked = hasIssuerSig && !issuer_signature.checked;
993
+ let overall;
994
+ if (signatureFailed || mismatchFailed || bindingFailed || payerFailed || nullifierFailed) {
355
995
  overall = 'failed';
356
996
  }
357
- else if (hasIssuerSig && !jwksProvided) {
358
- // Receipt has signature but no JWKS provided → partial (cannot verify signature)
997
+ else if (signatureUnchecked) {
359
998
  overall = 'partial';
360
999
  }
361
- else if (binding.matches && (!options.checkNullifier || nullifier.anchored)) {
362
- // Binding matches, signature OK (or no signature), nullifier OK → verified
363
- overall = 'verified';
364
- }
365
- else if (binding.matches || issuer_signature.valid || (nullifier.verified && nullifier.anchored)) {
366
- // At least one check passed → partial
1000
+ else if (!binding.expected && !hasIssuerSig && !options.checkPayer) {
367
1001
  overall = 'partial';
368
1002
  }
369
- else if (!binding.expected && !receipt.proof?.nullifier && !hasIssuerSig) {
370
- // No binding/sig/nullifier to verify (unmetered/demo) → partial
371
- overall = 'partial';
1003
+ else if ((binding.matches || !binding.expected)
1004
+ && (!options.checkPayer || payer.valid)
1005
+ && (!options.checkNullifier || !receipt.proof?.nullifier || nullifier.anchored === true)
1006
+ && (issuer_signature.valid || !hasIssuerSig)) {
1007
+ overall = 'verified';
372
1008
  }
373
1009
  else {
374
- overall = 'failed';
1010
+ overall = 'partial';
375
1011
  }
376
1012
  return {
377
1013
  receipt_id: receipt.task_id,
378
1014
  binding,
379
1015
  issuer_signature,
1016
+ payer,
380
1017
  nullifier,
381
- output_hash,
382
- hub,
383
- model,
384
- amount_usdc,
385
- tx,
1018
+ output_hash: facts.output_hash,
1019
+ hub: facts.hub,
1020
+ model: facts.model,
1021
+ amount_usdc: facts.amount_usdc,
1022
+ tx: facts.tx,
1023
+ claim_mismatches,
386
1024
  overall,
387
1025
  errors,
388
1026
  };
389
1027
  }
1028
+ var jws_js_2 = require("./jws.js");
1029
+ Object.defineProperty(exports, "resolvePinnedIssuerJwk", { enumerable: true, get: function () { return jws_js_2.resolvePinnedIssuerJwk; } });
1030
+ Object.defineProperty(exports, "verifyIssuerJws", { enumerable: true, get: function () { return jws_js_2.verifyIssuerJws; } });
1031
+ Object.defineProperty(exports, "DEFAULT_TRUSTED_ISSUER_KIDS", { enumerable: true, get: function () { return jws_js_2.DEFAULT_TRUSTED_ISSUER_KIDS; } });
1032
+ Object.defineProperty(exports, "DEFAULT_TRUSTED_JWKS_HOSTS", { enumerable: true, get: function () { return jws_js_2.DEFAULT_TRUSTED_JWKS_HOSTS; } });
1033
+ Object.defineProperty(exports, "KEY_UNTRUSTED", { enumerable: true, get: function () { return jws_js_2.KEY_UNTRUSTED; } });
1034
+ Object.defineProperty(exports, "jwkThumbprint", { enumerable: true, get: function () { return jws_js_2.jwkThumbprint; } });
1035
+ Object.defineProperty(exports, "isPinnedTrustedJwk", { enumerable: true, get: function () { return jws_js_2.isPinnedTrustedJwk; } });
1036
+ Object.defineProperty(exports, "jwksHostAllowed", { enumerable: true, get: function () { return jws_js_2.jwksHostAllowed; } });
390
1037
  exports.default = {
391
1038
  verifyBinding,
392
1039
  verifyIssuerSignature,
393
1040
  verifyIssuerSignatureWithJwks,
1041
+ verifyIssuerJws: jws_js_1.verifyIssuerJws,
1042
+ resolvePinnedIssuerJwk: jws_js_1.resolvePinnedIssuerJwk,
394
1043
  verifyNullifier,
1044
+ verifyPayerBinding: payer_js_1.verifyPayerBinding,
1045
+ verifySolanaPayer: solana_payer_js_1.verifySolanaPayer,
1046
+ verifyBasePayer: base_payer_js_1.verifyBasePayer,
395
1047
  verifyReceipt,
1048
+ receiptPayerClaims,
1049
+ receiptPayerClaimsFromEnvelope: payer_js_1.receiptPayerClaimsFromEnvelope,
1050
+ decodeJwsPayload: payer_js_1.decodeJwsPayload,
396
1051
  computePaymentCommitment: binding_js_1.computePaymentCommitment,
397
1052
  computeInferenceBinding: binding_js_1.computeInferenceBinding,
398
1053
  canonicalIssuerPayload,
1054
+ canonicalPayloadVersion,
399
1055
  canonicalSignedPayload: exports.canonicalSignedPayload,
1056
+ reconcileSettledTransfer,
1057
+ sumUsdcTransfersToPayee,
400
1058
  hashCanonicalPayload,
401
1059
  ZK_VERIFIER_ADDRESS: exports.ZK_VERIFIER_ADDRESS,
402
- BASE_RPC_URL: exports.BASE_RPC_URL,
1060
+ BASE_RPC_URL: base_payer_js_1.BASE_RPC_URL,
403
1061
  BASE_SEPOLIA_RPC_URL: exports.BASE_SEPOLIA_RPC_URL,
1062
+ SOLANA_RPC_URL: solana_payer_js_1.SOLANA_RPC_URL,
404
1063
  };
405
1064
  //# sourceMappingURL=index.js.map