@molpha/sdk 0.2.0-dev-20260911113923 → 0.2.0-dev-20260912150104
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/README.md +67 -0
- package/dist/index.d.ts +111 -3
- package/dist/index.js +399 -5
- package/dist/index.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/{wallet-udLZ5Iy0.d.ts → wallet-B3mYmC2b.d.ts} +77 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -77,13 +77,39 @@ function assertUint(value, max, label) {
|
|
|
77
77
|
throw new RangeError(`${label} out of range: ${value}`);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
function bytesToBase64(bytes) {
|
|
81
|
+
let binary = "";
|
|
82
|
+
for (let i = 0; i < bytes.length; i += 32768) {
|
|
83
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
84
|
+
}
|
|
85
|
+
return btoa(binary);
|
|
86
|
+
}
|
|
87
|
+
function base64ToBytes(value) {
|
|
88
|
+
const padded = value.length % 4 === 0 ? value : value + "=".repeat(4 - value.length % 4);
|
|
89
|
+
const binary = atob(padded);
|
|
90
|
+
const out = new Uint8Array(binary.length);
|
|
91
|
+
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
80
94
|
|
|
81
95
|
// src/core/apiconfig.ts
|
|
96
|
+
function compareCodeUnits(a, b) {
|
|
97
|
+
const len = Math.min(a.length, b.length);
|
|
98
|
+
for (let i = 0; i < len; i++) {
|
|
99
|
+
const diff = a.charCodeAt(i) - b.charCodeAt(i);
|
|
100
|
+
if (diff !== 0) return diff;
|
|
101
|
+
}
|
|
102
|
+
return a.length - b.length;
|
|
103
|
+
}
|
|
104
|
+
function sortHeaders(headers) {
|
|
105
|
+
return Object.fromEntries(Object.entries(headers).sort(([a], [b]) => compareCodeUnits(a, b)));
|
|
106
|
+
}
|
|
82
107
|
function canonicalizeAPIConfig(apiConfig) {
|
|
108
|
+
const headers = apiConfig.headers ?? {};
|
|
83
109
|
return {
|
|
84
110
|
url: apiConfig.url,
|
|
85
111
|
method: apiConfig.method ?? "GET",
|
|
86
|
-
headers:
|
|
112
|
+
headers: Object.keys(headers).length === 0 ? headers : sortHeaders(headers),
|
|
87
113
|
responseParser: apiConfig.responseParser,
|
|
88
114
|
valueTransform: apiConfig.valueTransform ?? ""
|
|
89
115
|
};
|
|
@@ -296,6 +322,268 @@ function validateEncryptionRecipients(selectedNodes) {
|
|
|
296
322
|
return { index: node.index, signingKey };
|
|
297
323
|
});
|
|
298
324
|
}
|
|
325
|
+
var typeHash = (signature) => keccak_256(utf8(signature));
|
|
326
|
+
var EIP712_DOMAIN_TYPEHASH = typeHash(
|
|
327
|
+
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
|
|
328
|
+
);
|
|
329
|
+
var TRANSFER_WITH_AUTHORIZATION_TYPEHASH = typeHash(
|
|
330
|
+
"TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)"
|
|
331
|
+
);
|
|
332
|
+
function word(bytes) {
|
|
333
|
+
if (bytes.length > 32) throw new RangeError("ABI word overflow");
|
|
334
|
+
const out = new Uint8Array(32);
|
|
335
|
+
out.set(bytes, 32 - bytes.length);
|
|
336
|
+
return out;
|
|
337
|
+
}
|
|
338
|
+
function addressWord(address3) {
|
|
339
|
+
const bytes = hexToBytes(address3);
|
|
340
|
+
if (bytes.length !== 20) throw new RangeError(`Not a 20-byte address: ${address3}`);
|
|
341
|
+
return word(bytes);
|
|
342
|
+
}
|
|
343
|
+
function uintWord(value) {
|
|
344
|
+
const n = typeof value === "bigint" ? value : BigInt(value);
|
|
345
|
+
if (n < 0n) throw new RangeError(`Not an unsigned integer: ${value}`);
|
|
346
|
+
const hex = n.toString(16);
|
|
347
|
+
return word(hexToBytes(hex.length % 2 === 0 ? hex : "0" + hex));
|
|
348
|
+
}
|
|
349
|
+
function bytes32Word(value) {
|
|
350
|
+
const bytes = hexToBytes(value);
|
|
351
|
+
if (bytes.length !== 32) throw new RangeError(`Not a 32-byte value: ${value}`);
|
|
352
|
+
return bytes;
|
|
353
|
+
}
|
|
354
|
+
function domainSeparator(domain) {
|
|
355
|
+
return keccak_256(
|
|
356
|
+
concatBytes(
|
|
357
|
+
EIP712_DOMAIN_TYPEHASH,
|
|
358
|
+
keccak_256(utf8(domain.name)),
|
|
359
|
+
keccak_256(utf8(domain.version)),
|
|
360
|
+
uintWord(BigInt(domain.chainId)),
|
|
361
|
+
addressWord(domain.verifyingContract)
|
|
362
|
+
)
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
function transferWithAuthorizationHash(auth) {
|
|
366
|
+
return keccak_256(
|
|
367
|
+
concatBytes(
|
|
368
|
+
TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
|
|
369
|
+
addressWord(auth.from),
|
|
370
|
+
addressWord(auth.to),
|
|
371
|
+
uintWord(auth.value),
|
|
372
|
+
uintWord(auth.validAfter),
|
|
373
|
+
uintWord(auth.validBefore),
|
|
374
|
+
bytes32Word(auth.nonce)
|
|
375
|
+
)
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
function transferWithAuthorizationDigest(domain, auth) {
|
|
379
|
+
return keccak_256(
|
|
380
|
+
concatBytes(
|
|
381
|
+
Uint8Array.from([25, 1]),
|
|
382
|
+
domainSeparator(domain),
|
|
383
|
+
transferWithAuthorizationHash(auth)
|
|
384
|
+
)
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
function toChecksumAddress(address3) {
|
|
388
|
+
const lower = bytesToHex(hexToBytes(address3));
|
|
389
|
+
if (lower.length !== 40) throw new RangeError(`Not a 20-byte address: ${address3}`);
|
|
390
|
+
const digest = bytesToHex(keccak_256(utf8(lower)));
|
|
391
|
+
let out = "0x";
|
|
392
|
+
for (let i = 0; i < lower.length; i++) {
|
|
393
|
+
out += parseInt(digest[i], 16) >= 8 ? lower[i].toUpperCase() : lower[i];
|
|
394
|
+
}
|
|
395
|
+
return out;
|
|
396
|
+
}
|
|
397
|
+
function evmAddressFromPrivateKey(privateKey) {
|
|
398
|
+
const key = typeof privateKey === "string" ? hexToBytes(privateKey) : privateKey;
|
|
399
|
+
if (key.length !== 32) throw new RangeError("EVM private key must be 32 bytes");
|
|
400
|
+
const uncompressed = secp256k1.getPublicKey(key, false);
|
|
401
|
+
return toChecksumAddress(bytesToHex0x(keccak_256(uncompressed.subarray(1)).subarray(12)));
|
|
402
|
+
}
|
|
403
|
+
function createEvmSignerFromPrivateKey(privateKey) {
|
|
404
|
+
const key = typeof privateKey === "string" ? hexToBytes(privateKey) : privateKey;
|
|
405
|
+
const address3 = evmAddressFromPrivateKey(key);
|
|
406
|
+
return {
|
|
407
|
+
address: address3,
|
|
408
|
+
async signDigest(digest) {
|
|
409
|
+
if (digest.length !== 32) throw new RangeError("Digest must be 32 bytes");
|
|
410
|
+
const signed = secp256k1.sign(digest, key, { prehash: false, format: "recovered" });
|
|
411
|
+
const out = new Uint8Array(65);
|
|
412
|
+
out.set(signed.subarray(1), 0);
|
|
413
|
+
out[64] = 27 + signed[0];
|
|
414
|
+
return out;
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/gateway/x402.ts
|
|
420
|
+
var NETWORKS = {
|
|
421
|
+
"eip155:8453": { chainId: 8453, usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
|
|
422
|
+
"eip155:84532": { chainId: 84532, usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e" },
|
|
423
|
+
// x402 v1 network names.
|
|
424
|
+
base: { chainId: 8453, usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
|
|
425
|
+
"base-sepolia": { chainId: 84532, usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e" }
|
|
426
|
+
};
|
|
427
|
+
var UpstreamPaymentRequiredError = class extends Error {
|
|
428
|
+
constructor(quote, message) {
|
|
429
|
+
super(
|
|
430
|
+
message ?? `API source requires payment for up to ${quote.eligibleSetSize} fetches: ${quote.resource}`
|
|
431
|
+
);
|
|
432
|
+
this.quote = quote;
|
|
433
|
+
this.name = "UpstreamPaymentRequiredError";
|
|
434
|
+
}
|
|
435
|
+
quote;
|
|
436
|
+
};
|
|
437
|
+
function eligibleSetSize(signaturesRequired, registry) {
|
|
438
|
+
return effectiveSelectionSize(
|
|
439
|
+
signaturesRequired,
|
|
440
|
+
registry.redundancyBuffer,
|
|
441
|
+
registry.nodeCount
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
function parseUpstreamQuote(envelope) {
|
|
445
|
+
if (!envelope || typeof envelope !== "object") return null;
|
|
446
|
+
const extensions = envelope.extensions;
|
|
447
|
+
if (!extensions || typeof extensions !== "object") return null;
|
|
448
|
+
const quote = extensions.upstream;
|
|
449
|
+
if (!quote || typeof quote !== "object") return null;
|
|
450
|
+
return typeof quote.eligibleSetSize === "number" ? quote : null;
|
|
451
|
+
}
|
|
452
|
+
function selectTerms(envelope, resource, assetDomain) {
|
|
453
|
+
const version = envelope.x402Version === 1 ? 1 : 2;
|
|
454
|
+
const offers = Array.isArray(envelope.accepts) ? envelope.accepts : [];
|
|
455
|
+
if (offers.length === 0) {
|
|
456
|
+
throw new Error(`Source 402 for ${resource} advertised no payment terms`);
|
|
457
|
+
}
|
|
458
|
+
const supported = offers.find(
|
|
459
|
+
(o) => o.scheme === "exact" && NETWORKS[String(o.network)] !== void 0
|
|
460
|
+
);
|
|
461
|
+
if (!supported) {
|
|
462
|
+
const seen = offers.map((o) => `${o.scheme}/${o.network}`).join(", ");
|
|
463
|
+
throw new Error(
|
|
464
|
+
`Source ${resource} requires an unsupported payment scheme or network (${seen}); this SDK signs exact payments on Base USDC only`
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
const network = NETWORKS[String(supported.network)];
|
|
468
|
+
const asset = String(supported.asset ?? "");
|
|
469
|
+
if (asset.toLowerCase() !== network.usdc.toLowerCase()) {
|
|
470
|
+
throw new Error(
|
|
471
|
+
`Source ${resource} asks to be paid in ${asset} on ${supported.network}; this SDK signs USDC (${network.usdc}) only`
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
const domain = assetDomain ?? (supported.extra?.name && supported.extra?.version ? { name: String(supported.extra.name), version: String(supported.extra.version) } : void 0);
|
|
475
|
+
if (!domain) {
|
|
476
|
+
throw new Error(
|
|
477
|
+
`Source ${resource} omitted extra.name/extra.version for ${asset}; supply sourcePayment.assetDomain to sign against it`
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
const amount = String(supported.amount ?? supported.maxAmountRequired ?? "");
|
|
481
|
+
if (!/^\d+$/.test(amount)) {
|
|
482
|
+
throw new Error(`Source ${resource} quoted a non-integer price: ${amount}`);
|
|
483
|
+
}
|
|
484
|
+
const payTo = String(supported.payTo ?? "");
|
|
485
|
+
if (!payTo) throw new Error(`Source ${resource} omitted payTo`);
|
|
486
|
+
return {
|
|
487
|
+
x402Version: version,
|
|
488
|
+
// Echoed verbatim in the signed payload, so it must stay the source's own object.
|
|
489
|
+
requirements: supported,
|
|
490
|
+
network: String(supported.network),
|
|
491
|
+
chainId: network.chainId,
|
|
492
|
+
asset,
|
|
493
|
+
payTo,
|
|
494
|
+
amount,
|
|
495
|
+
maxTimeoutSeconds: Number(supported.maxTimeoutSeconds ?? 60),
|
|
496
|
+
domain,
|
|
497
|
+
resource
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
function decodeEnvelope(headerValue, body) {
|
|
501
|
+
if (headerValue) {
|
|
502
|
+
try {
|
|
503
|
+
return JSON.parse(
|
|
504
|
+
new TextDecoder().decode(base64ToBytes(headerValue))
|
|
505
|
+
);
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return JSON.parse(body);
|
|
510
|
+
}
|
|
511
|
+
function validateSuppliedTerms(terms, resource, assetDomain) {
|
|
512
|
+
return selectTerms(
|
|
513
|
+
{
|
|
514
|
+
x402Version: terms.x402Version,
|
|
515
|
+
accepts: [terms.requirements]
|
|
516
|
+
},
|
|
517
|
+
resource,
|
|
518
|
+
assetDomain
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
async function probeSource(apiConfig, options = {}) {
|
|
522
|
+
const resolved = options.secrets ? resolveAPIConfig(apiConfig, options.secrets) : apiConfig;
|
|
523
|
+
const init = {
|
|
524
|
+
method: resolved.method ?? "GET",
|
|
525
|
+
headers: resolved.headers ?? {}
|
|
526
|
+
};
|
|
527
|
+
let res;
|
|
528
|
+
if (options.timeoutMs === void 0) {
|
|
529
|
+
res = await fetch(resolved.url, init);
|
|
530
|
+
} else {
|
|
531
|
+
const controller = new AbortController();
|
|
532
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs);
|
|
533
|
+
try {
|
|
534
|
+
res = await fetch(resolved.url, { ...init, signal: controller.signal });
|
|
535
|
+
} finally {
|
|
536
|
+
clearTimeout(timer);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (res.status !== 402) return null;
|
|
540
|
+
return selectTerms(
|
|
541
|
+
decodeEnvelope(res.headers.get("PAYMENT-REQUIRED"), await res.text()),
|
|
542
|
+
apiConfig.url,
|
|
543
|
+
options.assetDomain
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
async function signSourcePayments(terms, signer, count) {
|
|
547
|
+
if (!Number.isInteger(count) || count < 1) {
|
|
548
|
+
throw new RangeError(`Cannot sign ${count} source payments`);
|
|
549
|
+
}
|
|
550
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
551
|
+
const validAfter = String(now - 600);
|
|
552
|
+
const validBefore = String(now + Math.max(terms.maxTimeoutSeconds, 60));
|
|
553
|
+
const payments = [];
|
|
554
|
+
for (let i = 0; i < count; i++) {
|
|
555
|
+
const authorization = {
|
|
556
|
+
from: toChecksumAddress(signer.address),
|
|
557
|
+
to: toChecksumAddress(terms.payTo),
|
|
558
|
+
value: terms.amount,
|
|
559
|
+
validAfter,
|
|
560
|
+
validBefore,
|
|
561
|
+
nonce: bytesToHex0x(randomBytes(32))
|
|
562
|
+
};
|
|
563
|
+
const digest = transferWithAuthorizationDigest(
|
|
564
|
+
{
|
|
565
|
+
name: terms.domain.name,
|
|
566
|
+
version: terms.domain.version,
|
|
567
|
+
chainId: terms.chainId,
|
|
568
|
+
verifyingContract: terms.asset
|
|
569
|
+
},
|
|
570
|
+
authorization
|
|
571
|
+
);
|
|
572
|
+
const signature = bytesToHex0x(await signer.signDigest(digest));
|
|
573
|
+
const payload = terms.x402Version === 1 ? {
|
|
574
|
+
x402Version: 1,
|
|
575
|
+
scheme: "exact",
|
|
576
|
+
network: terms.network,
|
|
577
|
+
payload: { signature, authorization }
|
|
578
|
+
} : {
|
|
579
|
+
x402Version: 2,
|
|
580
|
+
accepted: terms.requirements,
|
|
581
|
+
payload: { signature, authorization }
|
|
582
|
+
};
|
|
583
|
+
payments.push(bytesToBase64(utf8(JSON.stringify(payload))));
|
|
584
|
+
}
|
|
585
|
+
return payments;
|
|
586
|
+
}
|
|
299
587
|
var SEED_GATEWAY = utf8("molpha_gateway");
|
|
300
588
|
var addressEncoder = getAddressEncoder();
|
|
301
589
|
function normalizeEndpoint(input) {
|
|
@@ -380,8 +668,24 @@ var MolphaGateway = class {
|
|
|
380
668
|
}
|
|
381
669
|
/** Tries endpoints in order; returns the first node list it can fetch. */
|
|
382
670
|
async getNodes() {
|
|
671
|
+
return (await this.getNodesInfo()).nodes;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* `GET /v1/nodes` — the peer set plus the gateway's view of the registry
|
|
675
|
+
* selection policy, which sizes the eligible set a paid source must fund.
|
|
676
|
+
*
|
|
677
|
+
* The `registry` block is advisory: it is whatever the gateway read from the
|
|
678
|
+
* chain, and is absent on older gateways or when that read failed. Prefer the
|
|
679
|
+
* on-chain read (`MolphaSolanaClient.getRegistrySelectionConfig`) whenever a
|
|
680
|
+
* Solana connection is available — `requestSignedData` already does.
|
|
681
|
+
*/
|
|
682
|
+
async getNodesInfo() {
|
|
383
683
|
const data = await this.firstReachableData("/v1/nodes");
|
|
384
|
-
|
|
684
|
+
if (Array.isArray(data)) return { nodes: data };
|
|
685
|
+
return {
|
|
686
|
+
nodes: data.nodes,
|
|
687
|
+
...data.registry ? { registry: data.registry } : {}
|
|
688
|
+
};
|
|
385
689
|
}
|
|
386
690
|
async isHealthy() {
|
|
387
691
|
for (const endpoint of this.endpoints) {
|
|
@@ -442,12 +746,17 @@ var MolphaGateway = class {
|
|
|
442
746
|
* read does not report `nodeCount`). Supply `opts.context` (e.g. from
|
|
443
747
|
* {@link prepareContext}) to reuse cached inputs and skip those fetches — a
|
|
444
748
|
* fully-populated context collapses the call to a single POST round.
|
|
749
|
+
*
|
|
750
|
+
* When the API source is itself x402-paywalled, pass `opts.sourcePayment` to
|
|
751
|
+
* fund it; without that, such a source throws
|
|
752
|
+
* {@link UpstreamPaymentRequiredError} carrying the gateway's quote.
|
|
445
753
|
*/
|
|
446
754
|
async requestSignedData(opts) {
|
|
447
755
|
const {
|
|
448
756
|
apiConfig,
|
|
449
757
|
signer,
|
|
450
758
|
encrypt,
|
|
759
|
+
sourcePayment,
|
|
451
760
|
maxAge = 60,
|
|
452
761
|
maxRetries = 15,
|
|
453
762
|
timeoutMs = 5e3
|
|
@@ -482,9 +791,33 @@ var MolphaGateway = class {
|
|
|
482
791
|
if (nodeCount === void 0) {
|
|
483
792
|
throw new Error("Round context resolved neither nodeCount nor nodes");
|
|
484
793
|
}
|
|
794
|
+
let terms = null;
|
|
795
|
+
let authorizations = 0;
|
|
796
|
+
let requoted = false;
|
|
797
|
+
if (sourcePayment) {
|
|
798
|
+
terms = sourcePayment.terms ? validateSuppliedTerms(
|
|
799
|
+
sourcePayment.terms,
|
|
800
|
+
requestApiConfig.url,
|
|
801
|
+
sourcePayment.assetDomain
|
|
802
|
+
) : await probeSource(apiConfig, {
|
|
803
|
+
...encrypt ? { secrets: encrypt.secrets } : {},
|
|
804
|
+
...sourcePayment.assetDomain ? { assetDomain: sourcePayment.assetDomain } : {},
|
|
805
|
+
timeoutMs
|
|
806
|
+
});
|
|
807
|
+
if (terms) {
|
|
808
|
+
authorizations = effectiveSelectionSize(
|
|
809
|
+
signaturesRequired,
|
|
810
|
+
redundancyBuffer,
|
|
811
|
+
nodeCount
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
485
815
|
let lastError;
|
|
816
|
+
let lastTimestamp = 0;
|
|
486
817
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
487
|
-
|
|
818
|
+
let timestamp = Math.floor(Date.now() / 1e3);
|
|
819
|
+
if (timestamp <= lastTimestamp) timestamp = lastTimestamp + 1;
|
|
820
|
+
lastTimestamp = timestamp;
|
|
488
821
|
const seed = deriveSelectionSeed(sourceIdBytes, registryVersion, timestamp);
|
|
489
822
|
const groupSize = effectiveSelectionSize(signaturesRequired, redundancyBuffer, nodeCount);
|
|
490
823
|
const bitmap = deriveGroupBitmap(seed, nodeCount, groupSize);
|
|
@@ -503,6 +836,14 @@ var MolphaGateway = class {
|
|
|
503
836
|
}
|
|
504
837
|
encKeyBundle = encryptForNodes(requestApiConfig, encrypt.secrets, selected);
|
|
505
838
|
}
|
|
839
|
+
let sourcePayments;
|
|
840
|
+
if (terms && sourcePayment && authorizations > 0) {
|
|
841
|
+
sourcePayments = await signSourcePayments(
|
|
842
|
+
terms,
|
|
843
|
+
sourcePayment.signer,
|
|
844
|
+
authorizations
|
|
845
|
+
);
|
|
846
|
+
}
|
|
506
847
|
const baseBody = {
|
|
507
848
|
sourceId,
|
|
508
849
|
registryVersion,
|
|
@@ -514,6 +855,8 @@ var MolphaGateway = class {
|
|
|
514
855
|
apiConfig: requestApiConfig
|
|
515
856
|
};
|
|
516
857
|
if (encKeyBundle) baseBody.encKeyBundle = encKeyBundle;
|
|
858
|
+
if (sourcePayments) baseBody.sourcePayments = sourcePayments;
|
|
859
|
+
let requote = false;
|
|
517
860
|
for (const endpoint of this.endpoints) {
|
|
518
861
|
try {
|
|
519
862
|
let authSig = ZERO_AUTH_SIG;
|
|
@@ -535,6 +878,55 @@ var MolphaGateway = class {
|
|
|
535
878
|
body,
|
|
536
879
|
timeoutMs
|
|
537
880
|
);
|
|
881
|
+
if (res.status === 402) {
|
|
882
|
+
const quote = parseUpstreamQuote(
|
|
883
|
+
await res.json().catch(() => null)
|
|
884
|
+
);
|
|
885
|
+
if (!quote) {
|
|
886
|
+
throw new GatewayError(
|
|
887
|
+
"Gateway requires payment but returned no upstream source quote",
|
|
888
|
+
402
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
if (!sourcePayment) throw new UpstreamPaymentRequiredError(quote);
|
|
892
|
+
if (terms && requoted && authorizations >= quote.eligibleSetSize) {
|
|
893
|
+
throw new UpstreamPaymentRequiredError(
|
|
894
|
+
quote,
|
|
895
|
+
`Source payment was not accepted for ${quote.resource}: ${quote.error ?? "no detail"}`
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
requoted = true;
|
|
899
|
+
if (quote.eligibleSetSize !== groupSize) {
|
|
900
|
+
throw new GatewayError(
|
|
901
|
+
`Gateway upstream quote eligibleSetSize ${quote.eligibleSetSize} does not match this round's selection size ${groupSize} (min(signaturesRequired + redundancyBuffer, nodeCount))`,
|
|
902
|
+
402
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
authorizations = groupSize;
|
|
906
|
+
if (!terms) {
|
|
907
|
+
try {
|
|
908
|
+
terms = await probeSource(apiConfig, {
|
|
909
|
+
...encrypt ? { secrets: encrypt.secrets } : {},
|
|
910
|
+
...sourcePayment.assetDomain ? { assetDomain: sourcePayment.assetDomain } : {},
|
|
911
|
+
timeoutMs
|
|
912
|
+
});
|
|
913
|
+
} catch (err) {
|
|
914
|
+
throw new UpstreamPaymentRequiredError(
|
|
915
|
+
quote,
|
|
916
|
+
`Cannot pay ${quote.resource}: ${err instanceof Error ? err.message : String(err)}`
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (!terms) {
|
|
921
|
+
throw new UpstreamPaymentRequiredError(
|
|
922
|
+
quote,
|
|
923
|
+
`Gateway reports ${quote.resource} is paywalled but the source returned no payment terms`
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
lastError = new UpstreamPaymentRequiredError(quote);
|
|
927
|
+
requote = true;
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
538
930
|
const errorDetail = !res.ok ? await parseGatewayErrorDetail(res) : void 0;
|
|
539
931
|
if (res.status === 400 || res.status === 401) {
|
|
540
932
|
throw new GatewayError(
|
|
@@ -569,12 +961,14 @@ var MolphaGateway = class {
|
|
|
569
961
|
}
|
|
570
962
|
lastError = new Error(`Gateway returned status: ${json.status}`);
|
|
571
963
|
} catch (err) {
|
|
572
|
-
if (err instanceof
|
|
964
|
+
if (err instanceof UpstreamPaymentRequiredError) throw err;
|
|
965
|
+
if (err instanceof GatewayError && (err.status === 400 || err.status === 401 || err.status === 402)) {
|
|
573
966
|
throw err;
|
|
574
967
|
}
|
|
575
968
|
lastError = err;
|
|
576
969
|
}
|
|
577
970
|
}
|
|
971
|
+
if (requote) continue;
|
|
578
972
|
}
|
|
579
973
|
throw lastError instanceof Error ? lastError : new Error("Gateway round failed after retries");
|
|
580
974
|
}
|
|
@@ -11341,6 +11735,6 @@ var MolphaSDK = class {
|
|
|
11341
11735
|
}
|
|
11342
11736
|
};
|
|
11343
11737
|
|
|
11344
|
-
export { DEFAULT_GATEWAY_ENDPOINT, GatewayError, MESSAGE_PREFIX, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_VERIFIER_ABI, MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_STARKNET_ADDRESSES, MOLPHA_VERIFIER_STARKNET_SEPOLIA, MolphaGateway, MolphaSDK, MolphaSolanaClient, PlanType, REQUEST_AUTH_DOMAIN, addressToBytes, attestationMessageHash, attestationMessageHashFromResult, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, commitmentAddressToStarknetFelt, concatBytes, deriveApiConfigHash, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, effectiveSelectionSize, encodeRequestAuth, ensureLength, feedPda, gatewayPda, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseGatewayInfo, planIdFromVariant, planPda, planVariant, protocolConfigPda, registryPda, registryStatePda, resolveRemainingAccounts, secp256k1PublicKeyFromCoordinates, selectedIndices, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, subscriptionPda, toFixedBytes, toFixedHex, u256beFromBigInt, u32be, u32le, u64be, u64le, utf8 };
|
|
11738
|
+
export { DEFAULT_GATEWAY_ENDPOINT, EIP712_DOMAIN_TYPEHASH, GatewayError, MESSAGE_PREFIX, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_VERIFIER_ABI, MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_STARKNET_ADDRESSES, MOLPHA_VERIFIER_STARKNET_SEPOLIA, MolphaGateway, MolphaSDK, MolphaSolanaClient, PlanType, REQUEST_AUTH_DOMAIN, TRANSFER_WITH_AUTHORIZATION_TYPEHASH, UpstreamPaymentRequiredError, addressToBytes, attestationMessageHash, attestationMessageHashFromResult, base64ToBytes, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToBase64, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, commitmentAddressToStarknetFelt, concatBytes, createEvmSignerFromPrivateKey, deriveApiConfigHash, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, domainSeparator, effectiveSelectionSize, eligibleSetSize, encodeRequestAuth, ensureLength, evmAddressFromPrivateKey, feedPda, gatewayPda, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseGatewayInfo, parseUpstreamQuote, planIdFromVariant, planPda, planVariant, probeSource, protocolConfigPda, registryPda, registryStatePda, resolveRemainingAccounts, secp256k1PublicKeyFromCoordinates, selectedIndices, signSourcePayments, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, subscriptionPda, toChecksumAddress, toFixedBytes, toFixedHex, transferWithAuthorizationDigest, transferWithAuthorizationHash, u256beFromBigInt, u32be, u32le, u64be, u64le, utf8, validateSuppliedTerms };
|
|
11345
11739
|
//# sourceMappingURL=index.js.map
|
|
11346
11740
|
//# sourceMappingURL=index.js.map
|