run402 4.117.2 → 4.119.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/cli.mjs +2 -2
- package/lib/init.mjs +1 -1
- package/lib/pay.mjs +65 -2
- package/lib/pay.test.mjs +59 -2
- package/lib/wallets.mjs +20 -61
- package/package.json +1 -1
- package/sdk/dist/namespaces/pay.d.ts +15 -1
- package/sdk/dist/namespaces/pay.d.ts.map +1 -1
- package/sdk/dist/namespaces/pay.js +19 -0
- package/sdk/dist/namespaces/pay.js.map +1 -1
- package/sdk/dist/node/index.d.ts +2 -0
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +2 -0
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/init.d.ts +3 -0
- package/sdk/dist/node/init.d.ts.map +1 -1
- package/sdk/dist/node/init.js +11 -8
- package/sdk/dist/node/init.js.map +1 -1
- package/sdk/dist/node/lightning-paid-fetch.d.ts +41 -2
- package/sdk/dist/node/lightning-paid-fetch.d.ts.map +1 -1
- package/sdk/dist/node/lightning-paid-fetch.js +157 -20
- package/sdk/dist/node/lightning-paid-fetch.js.map +1 -1
- package/sdk/dist/node/lightning-x402-lnbtc.d.ts +15 -2
- package/sdk/dist/node/lightning-x402-lnbtc.d.ts.map +1 -1
- package/sdk/dist/node/lightning-x402-lnbtc.js +29 -4
- package/sdk/dist/node/lightning-x402-lnbtc.js.map +1 -1
- package/sdk/dist/node/nwc.d.ts +6 -0
- package/sdk/dist/node/nwc.d.ts.map +1 -1
- package/sdk/dist/node/nwc.js +9 -0
- package/sdk/dist/node/nwc.js.map +1 -1
- package/sdk/dist/node/onchain-balances.d.ts +24 -0
- package/sdk/dist/node/onchain-balances.d.ts.map +1 -0
- package/sdk/dist/node/onchain-balances.js +72 -0
- package/sdk/dist/node/onchain-balances.js.map +1 -0
- package/sdk/dist/node/paid-fetch.d.ts +26 -1
- package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
- package/sdk/dist/node/paid-fetch.js +125 -7
- package/sdk/dist/node/paid-fetch.js.map +1 -1
- package/sdk/dist/node/status.d.ts +4 -0
- package/sdk/dist/node/status.d.ts.map +1 -1
- package/sdk/dist/node/status.js +6 -47
- package/sdk/dist/node/status.js.map +1 -1
- package/sdk/dist/node/tempo.d.ts +30 -0
- package/sdk/dist/node/tempo.d.ts.map +1 -0
- package/sdk/dist/node/tempo.js +36 -0
- package/sdk/dist/node/tempo.js.map +1 -0
- package/sdk/dist/secret-gate.d.ts +5 -0
- package/sdk/dist/secret-gate.d.ts.map +1 -1
- package/sdk/dist/secret-gate.js +9 -0
- package/sdk/dist/secret-gate.js.map +1 -1
- package/vault-surface.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wallet's on-chain token balances, one per network a rail pays on, under
|
|
3
|
+
* the snake_case keys `run402 wallets balance`, `run402 status`, and
|
|
4
|
+
* `run402 init` share (spec `status-account-balances`). Test money and real
|
|
5
|
+
* money are separate keys: a sum alone would hide which one the wallet holds.
|
|
6
|
+
*
|
|
7
|
+
* Every read is best-effort: a network whose read fails is `null`, never 0.
|
|
8
|
+
*/
|
|
9
|
+
import { PATH_USD, TEMPO_MAINNET, TEMPO_MODERATO } from "./tempo.js";
|
|
10
|
+
const BALANCE_OF_ABI = [{ name: "balanceOf", type: "function", stateMutability: "view", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }] }];
|
|
11
|
+
const USDC_MAINNET = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
12
|
+
const USDC_SEPOLIA = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
|
|
13
|
+
export const ALL_ON_CHAIN_BALANCE_KEYS = [
|
|
14
|
+
"base_mainnet_usd_micros",
|
|
15
|
+
"base_sepolia_usd_micros",
|
|
16
|
+
"tempo_mainnet_pathusd_micros",
|
|
17
|
+
"tempo_moderato_pathusd_micros",
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* The networks a rail pays on: Tempo for `mpp`; Base for `x402` and for
|
|
21
|
+
* `lightning`, whose fallback is x402.
|
|
22
|
+
*/
|
|
23
|
+
export function railBalanceKeys(rail) {
|
|
24
|
+
return rail === "mpp"
|
|
25
|
+
? ["tempo_mainnet_pathusd_micros", "tempo_moderato_pathusd_micros"]
|
|
26
|
+
: ["base_mainnet_usd_micros", "base_sepolia_usd_micros"];
|
|
27
|
+
}
|
|
28
|
+
async function defaultReader() {
|
|
29
|
+
const { createPublicClient, defineChain, http } = await import("viem");
|
|
30
|
+
const { base, baseSepolia } = await import("viem/chains");
|
|
31
|
+
const tempo = (chain) => defineChain({
|
|
32
|
+
id: chain.id,
|
|
33
|
+
name: chain.name,
|
|
34
|
+
nativeCurrency: { name: "pathUSD", symbol: "pathUSD", decimals: 6 },
|
|
35
|
+
rpcUrls: { default: { http: [chain.rpcUrl] } },
|
|
36
|
+
});
|
|
37
|
+
const targets = {
|
|
38
|
+
base_mainnet_usd_micros: { chain: base, token: USDC_MAINNET },
|
|
39
|
+
base_sepolia_usd_micros: { chain: baseSepolia, token: USDC_SEPOLIA },
|
|
40
|
+
tempo_mainnet_pathusd_micros: { chain: tempo(TEMPO_MAINNET), token: PATH_USD },
|
|
41
|
+
tempo_moderato_pathusd_micros: { chain: tempo(TEMPO_MODERATO), token: PATH_USD },
|
|
42
|
+
};
|
|
43
|
+
return async (key, address) => {
|
|
44
|
+
const { chain, token } = targets[key];
|
|
45
|
+
const client = createPublicClient({ chain: chain, transport: http() });
|
|
46
|
+
const raw = await client.readContract({
|
|
47
|
+
address: token,
|
|
48
|
+
abi: BALANCE_OF_ABI,
|
|
49
|
+
functionName: "balanceOf",
|
|
50
|
+
args: [address],
|
|
51
|
+
});
|
|
52
|
+
return Number(raw);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Read the given networks in parallel; a failed read is `null`. */
|
|
56
|
+
export async function readOnChainBalances(address, keys = ALL_ON_CHAIN_BALANCE_KEYS, reader) {
|
|
57
|
+
let read;
|
|
58
|
+
try {
|
|
59
|
+
read = reader ?? await defaultReader();
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return Object.fromEntries(keys.map((key) => [key, null]));
|
|
63
|
+
}
|
|
64
|
+
const values = await Promise.all(keys.map((key) => read(key, address).catch(() => null)));
|
|
65
|
+
return Object.fromEntries(keys.map((key, i) => [key, values[i]]));
|
|
66
|
+
}
|
|
67
|
+
/** The sum over `keys`, or null when every one of them is unknown. */
|
|
68
|
+
export function sumOnChainBalances(balances, keys) {
|
|
69
|
+
const known = keys.map((key) => balances[key]).filter((value) => typeof value === "number");
|
|
70
|
+
return known.length === 0 ? null : known.reduce((total, value) => total + value, 0);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=onchain-balances.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onchain-balances.js","sourceRoot":"","sources":["../../src/node/onchain-balances.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,7 @@ import { Run402Error } from "../errors.js";
|
|
|
20
20
|
import type { MppStack, X402Stack } from "./_paid-stack.js";
|
|
21
21
|
import { PaymentBuyerError, type PayExecutor } from "../namespaces/pay.js";
|
|
22
22
|
import { type PaymentAttemptStore } from "./payment-attempts.js";
|
|
23
|
+
import { type TempoChain } from "./tempo.js";
|
|
23
24
|
type FetchFn = typeof globalThis.fetch;
|
|
24
25
|
interface RpcClient {
|
|
25
26
|
readContract: (args: unknown) => Promise<bigint>;
|
|
@@ -29,6 +30,8 @@ interface BalanceRetryOptions {
|
|
|
29
30
|
baseDelayMs?: number;
|
|
30
31
|
sleep?: (milliseconds: number) => Promise<void>;
|
|
31
32
|
random?: () => number;
|
|
33
|
+
/** What the balance is of, for messages: "x402 USDC" unless set. */
|
|
34
|
+
balanceLabel?: string;
|
|
32
35
|
}
|
|
33
36
|
interface PaymentRequirementLike {
|
|
34
37
|
network?: string;
|
|
@@ -108,7 +111,7 @@ export declare class X402BalanceError extends Run402Error {
|
|
|
108
111
|
readonly kind: "local_error";
|
|
109
112
|
readonly code: X402BalanceErrorCode;
|
|
110
113
|
readonly cause?: unknown;
|
|
111
|
-
constructor(code: X402BalanceErrorCode, message: string, details: Record<string, unknown>, cause?: unknown, nextActions?: import("../errors.js").NextAction[]);
|
|
114
|
+
constructor(code: X402BalanceErrorCode, message: string, details: Record<string, unknown>, cause?: unknown, nextActions?: import("../errors.js").NextAction[], context?: string);
|
|
112
115
|
}
|
|
113
116
|
export type X402PaymentNetwork = "eip155:8453" | "eip155:84532";
|
|
114
117
|
/** Public-chain operations exposed to an opaque signer provider. */
|
|
@@ -208,6 +211,28 @@ export declare function filterAffordableRequirements(requirements: PaymentRequir
|
|
|
208
211
|
* `payers` detail.
|
|
209
212
|
*/
|
|
210
213
|
payers?: Readonly<Record<string, string>>): PaymentRequirementLike[];
|
|
214
|
+
/** One mppx challenge candidate; only the challenge's own fields are read. */
|
|
215
|
+
interface MppChallengeCandidate {
|
|
216
|
+
challenge: {
|
|
217
|
+
method: string;
|
|
218
|
+
intent: string;
|
|
219
|
+
request: unknown;
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* MPP on Tempo: keep the Tempo challenges the wallet can cover, in the order
|
|
224
|
+
* the server offered them (spec `mpp-tempo-client-surface`). Run402 offers
|
|
225
|
+
* both Tempo chains on every SKU, mainnet first on paid SKUs and Moderato
|
|
226
|
+
* first on the free tier; mppx pays the first candidate it is handed, so
|
|
227
|
+
* dropping the chains this wallet cannot pay lets a faucet-funded wallet pay
|
|
228
|
+
* Moderato and a mainnet-funded wallet pay mainnet with no chain setting.
|
|
229
|
+
*
|
|
230
|
+
* "Cover" is the amount plus the sender's Tempo fee headroom. When nothing is
|
|
231
|
+
* covered this raises the same balance-preflight errors as x402: an RPC
|
|
232
|
+
* failure when a read failed, else `X402_INSUFFICIENT_FUNDS` naming every
|
|
233
|
+
* offered chain, before anything is signed.
|
|
234
|
+
*/
|
|
235
|
+
export declare function orderTempoCandidates<C extends MppChallengeCandidate>(candidates: readonly C[], address: string, readBalance: (chain: TempoChain) => Promise<bigint>): Promise<C[]>;
|
|
211
236
|
export declare function setupPaidFetch(options?: PaidFetchOptions): Promise<ConfiguredPaidFetch | null>;
|
|
212
237
|
declare function createLazyPaidFetchFrom(setup: () => Promise<FetchFn | null>): FetchFn;
|
|
213
238
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paid-fetch.d.ts","sourceRoot":"","sources":["../../src/node/paid-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,OAAO,KAAK,EAAc,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAGL,WAAW,EAEZ,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAEL,iBAAiB,EAYjB,KAAK,WAAW,EAMjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAQL,KAAK,mBAAmB,EACzB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"paid-fetch.d.ts","sourceRoot":"","sources":["../../src/node/paid-fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,10 +22,11 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
22
22
|
import { createHash } from "node:crypto";
|
|
23
23
|
import { LocalError, PaymentAttemptError, Run402Error, } from "../errors.js";
|
|
24
24
|
import { PaidStackUnavailable, loadMppStack, loadX402Stack } from "./_paid-stack.js";
|
|
25
|
-
import { createLightningFetch } from "./lightning-paid-fetch.js";
|
|
25
|
+
import { createLightningFetch, createLightningPayExecutor } from "./lightning-paid-fetch.js";
|
|
26
26
|
import { DEFAULT_PAYMENT_MAX_USD_MICROS, PaymentBuyerError, PaymentPolicyError, gatewayPaymentBuyerError, isTrustedRun402PaymentUrl, isTrustedRun402PendingResponse, payResponseMetadata, merchantReceiptRequiredError, paymentExceedsMaxError, readPaymentErrorEnvelope, responseSignalsReplay, paidStackUnavailableError, walletUnavailableError, } from "../namespaces/pay.js";
|
|
27
27
|
import { attemptIdFromRequest, createFilePaymentAttemptStore, createPaymentAttemptId, hasPaymentAuthorization, requestSummary, withPaymentAttemptHeader, } from "./payment-attempts.js";
|
|
28
28
|
import { sdkFetch } from "./http-dispatcher.js";
|
|
29
|
+
import { PATH_USD, TEMPO_FEE_HEADROOM_USD_MICROS, tempoChainById } from "./tempo.js";
|
|
29
30
|
/**
|
|
30
31
|
* A machine-readable x402 balance-preflight failure.
|
|
31
32
|
*
|
|
@@ -135,7 +136,7 @@ export class X402BalanceError extends Run402Error {
|
|
|
135
136
|
kind = "local_error";
|
|
136
137
|
code;
|
|
137
138
|
cause;
|
|
138
|
-
constructor(code, message, details, cause, nextActions) {
|
|
139
|
+
constructor(code, message, details, cause, nextActions, context = "checking x402 USDC balance") {
|
|
139
140
|
const rpcFailure = code !== "X402_INSUFFICIENT_FUNDS";
|
|
140
141
|
super(message, null, {
|
|
141
142
|
error: code,
|
|
@@ -164,7 +165,7 @@ export class X402BalanceError extends Run402Error {
|
|
|
164
165
|
why: "Fund the configured wallet on an accepted network, then retry.",
|
|
165
166
|
},
|
|
166
167
|
]),
|
|
167
|
-
},
|
|
168
|
+
}, context);
|
|
168
169
|
this.code = code;
|
|
169
170
|
if (cause !== undefined)
|
|
170
171
|
this.cause = cause;
|
|
@@ -286,13 +287,13 @@ export async function checkBalanceAcrossProviders(clients, tokenAddress, walletA
|
|
|
286
287
|
}
|
|
287
288
|
}
|
|
288
289
|
const code = exhaustedRpcCode(failures);
|
|
289
|
-
throw new X402BalanceError(code, `Unable to confirm the x402 USDC balance on ${network}; no zero balance was assumed.`, {
|
|
290
|
+
throw new X402BalanceError(code, `Unable to confirm the ${options.balanceLabel ?? "x402 USDC"} balance on ${network}; no zero balance was assumed.`, {
|
|
290
291
|
network,
|
|
291
292
|
balance_status: "unknown",
|
|
292
293
|
providers_exhausted: true,
|
|
293
294
|
providers_attempted: clients.length,
|
|
294
295
|
attempts: failures,
|
|
295
|
-
}, lastCause);
|
|
296
|
+
}, lastCause, undefined, options.balanceLabel ? `checking ${options.balanceLabel} balance` : undefined);
|
|
296
297
|
}
|
|
297
298
|
function bigintAmount(value) {
|
|
298
299
|
if (typeof value !== "string" || !/^\d+$/.test(value))
|
|
@@ -389,6 +390,91 @@ async function balanceState(clients, tokenAddress, walletAddress, network) {
|
|
|
389
390
|
throw err;
|
|
390
391
|
}
|
|
391
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* MPP on Tempo: keep the Tempo challenges the wallet can cover, in the order
|
|
395
|
+
* the server offered them (spec `mpp-tempo-client-surface`). Run402 offers
|
|
396
|
+
* both Tempo chains on every SKU, mainnet first on paid SKUs and Moderato
|
|
397
|
+
* first on the free tier; mppx pays the first candidate it is handed, so
|
|
398
|
+
* dropping the chains this wallet cannot pay lets a faucet-funded wallet pay
|
|
399
|
+
* Moderato and a mainnet-funded wallet pay mainnet with no chain setting.
|
|
400
|
+
*
|
|
401
|
+
* "Cover" is the amount plus the sender's Tempo fee headroom. When nothing is
|
|
402
|
+
* covered this raises the same balance-preflight errors as x402: an RPC
|
|
403
|
+
* failure when a read failed, else `X402_INSUFFICIENT_FUNDS` naming every
|
|
404
|
+
* offered chain, before anything is signed.
|
|
405
|
+
*/
|
|
406
|
+
export async function orderTempoCandidates(candidates, address, readBalance) {
|
|
407
|
+
const offers = candidates.flatMap((candidate) => {
|
|
408
|
+
const { method, intent, request } = candidate.challenge;
|
|
409
|
+
if (method !== "tempo" || intent !== "charge")
|
|
410
|
+
return [];
|
|
411
|
+
const req = (request ?? {});
|
|
412
|
+
const chain = tempoChainById(req.methodDetails?.chainId);
|
|
413
|
+
const amount = bigintAmount(req.amount);
|
|
414
|
+
return chain && amount !== null ? [{ candidate, chain, amount }] : [];
|
|
415
|
+
});
|
|
416
|
+
if (offers.length === 0)
|
|
417
|
+
return [...candidates];
|
|
418
|
+
const chains = [...new Set(offers.map((offer) => offer.chain))];
|
|
419
|
+
const states = new Map(await Promise.all(chains.map(async (chain) => {
|
|
420
|
+
try {
|
|
421
|
+
return [chain, { status: "known", balance: await readBalance(chain) }];
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
424
|
+
const error = err instanceof X402BalanceError
|
|
425
|
+
? err
|
|
426
|
+
: new X402BalanceError("X402_RPC_UNAVAILABLE", `Could not read the wallet's pathUSD balance on ${chain.name}.`, { network: chain.network }, err, undefined, "checking MPP pathUSD balance");
|
|
427
|
+
return [chain, { status: "unknown", error }];
|
|
428
|
+
}
|
|
429
|
+
})));
|
|
430
|
+
const covered = new Set(offers
|
|
431
|
+
.filter(({ chain, amount }) => {
|
|
432
|
+
const state = states.get(chain);
|
|
433
|
+
return state?.status === "known" && state.balance >= amount + TEMPO_FEE_HEADROOM_USD_MICROS;
|
|
434
|
+
})
|
|
435
|
+
.map((offer) => offer.candidate));
|
|
436
|
+
if (covered.size > 0) {
|
|
437
|
+
const tempo = new Set(offers.map((offer) => offer.candidate));
|
|
438
|
+
return candidates.filter((candidate) => !tempo.has(candidate) || covered.has(candidate));
|
|
439
|
+
}
|
|
440
|
+
const unknown = offers.map(({ chain }) => states.get(chain)).find((state) => state?.status === "unknown");
|
|
441
|
+
if (unknown)
|
|
442
|
+
throw unknown.error;
|
|
443
|
+
const first = offers[0].chain;
|
|
444
|
+
const moderatoOffered = offers.some(({ chain }) => chain.testnet);
|
|
445
|
+
const nextActions = first.testnet
|
|
446
|
+
? [{ type: "fund_wallet", command: "run402 wallets fund", why: `Request Moderato test pathUSD for ${address}, then retry.` }]
|
|
447
|
+
: [
|
|
448
|
+
{ type: "fund_wallet", why: `Fund ${address} with pathUSD on ${first.name} (${first.network}), then retry.` },
|
|
449
|
+
...(moderatoOffered
|
|
450
|
+
? [{ type: "fund_wallet", command: "run402 wallets fund", why: "Or request Moderato test pathUSD, which this purchase also accepts, then retry." }]
|
|
451
|
+
: []),
|
|
452
|
+
];
|
|
453
|
+
throw new X402BalanceError("X402_INSUFFICIENT_FUNDS", "The configured wallet has insufficient confirmed pathUSD on every Tempo chain this payment accepts.", {
|
|
454
|
+
token: "pathUSD",
|
|
455
|
+
asset: PATH_USD,
|
|
456
|
+
fee_headroom_usd_micros: Number(TEMPO_FEE_HEADROOM_USD_MICROS),
|
|
457
|
+
requirements: offers.map(({ chain, amount }) => ({ network: chain.network, amount: amount.toString() })),
|
|
458
|
+
balances: Object.fromEntries(chains.map((chain) => [chain.network, states.get(chain).balance.toString()])),
|
|
459
|
+
payers: Object.fromEntries(chains.map((chain) => [chain.network, address])),
|
|
460
|
+
}, undefined, nextActions, "checking MPP pathUSD balance");
|
|
461
|
+
}
|
|
462
|
+
/** Reads pathUSD on one Tempo chain through its RPC, with the x402 retry policy. */
|
|
463
|
+
async function tempoBalanceReader(address) {
|
|
464
|
+
const { createPublicClient, defineChain, http } = await import("viem");
|
|
465
|
+
return (chain) => {
|
|
466
|
+
const client = createPublicClient({
|
|
467
|
+
chain: defineChain({
|
|
468
|
+
id: chain.id,
|
|
469
|
+
name: chain.name,
|
|
470
|
+
nativeCurrency: { name: "pathUSD", symbol: "pathUSD", decimals: 6 },
|
|
471
|
+
rpcUrls: { default: { http: [chain.rpcUrl] } },
|
|
472
|
+
}),
|
|
473
|
+
transport: http(chain.rpcUrl),
|
|
474
|
+
});
|
|
475
|
+
return checkBalanceAcrossProviders([client], PATH_USD, address, chain.network, { balanceLabel: "MPP pathUSD" });
|
|
476
|
+
};
|
|
477
|
+
}
|
|
392
478
|
/**
|
|
393
479
|
* True when `response` genuinely came back from `configuredApiBase`'s
|
|
394
480
|
* origin — never redirected, and never merely a matching string (an actual
|
|
@@ -428,23 +514,40 @@ export async function setupPaidFetch(options = {}) {
|
|
|
428
514
|
// agent's budgeted wallet on Run402's Hub; fall back to the x402 buyer
|
|
429
515
|
// built from the same wallet when no Lightning challenge is offered.
|
|
430
516
|
const lightning = localWallet.lightning;
|
|
517
|
+
const fallback = async () => setupPaidFetch({ ...options, railOverride: "x402" });
|
|
431
518
|
const fetchFn = createLightningFetch({
|
|
432
519
|
pairingUri: lightning.nwc,
|
|
433
520
|
baseFetch: sdkFetch,
|
|
434
|
-
fallback
|
|
521
|
+
fallback,
|
|
435
522
|
});
|
|
523
|
+
// `pay.fetch` on this rail: the same buyer, reporting the `lnbtc`
|
|
524
|
+
// payment it made; a 402 with no Lightning entry is the x402 buyer's.
|
|
525
|
+
const pay = async (url, init, payOptions) => {
|
|
526
|
+
const executor = createLightningPayExecutor({ pairingUri: lightning.nwc, baseFetch: sdkFetch, fallback: async () => null });
|
|
527
|
+
const result = await executor(url, init, payOptions);
|
|
528
|
+
if (result.payment || result.response.status !== 402)
|
|
529
|
+
return result;
|
|
530
|
+
await result.response.body?.cancel().catch(() => undefined);
|
|
531
|
+
const x402 = await fallback();
|
|
532
|
+
if (!x402?.pay)
|
|
533
|
+
return result;
|
|
534
|
+
return x402.pay(url, init, payOptions);
|
|
535
|
+
};
|
|
436
536
|
return withPayer(fetchFn, {
|
|
437
537
|
source: resolvedWallet.source,
|
|
438
538
|
rail: "lightning",
|
|
439
539
|
payers: [{ address: lightning.lightning_address ?? lightning.wallet_id }],
|
|
440
|
-
});
|
|
540
|
+
}, undefined, pay);
|
|
441
541
|
}
|
|
442
542
|
if (rail === "mpp" && localWallet) {
|
|
443
543
|
const stack = await stackLoaders.mpp();
|
|
444
544
|
const account = stack.privateKeyToAccount(localWallet.privateKey);
|
|
545
|
+
const readBalance = await tempoBalanceReader(localWallet.address);
|
|
445
546
|
const mppx = stack.Mppx.create({
|
|
446
547
|
polyfill: false,
|
|
447
548
|
methods: [stack.tempo({ account })],
|
|
549
|
+
// Pay the first offered Tempo chain this wallet can cover.
|
|
550
|
+
orderChallenges: (candidates) => orderTempoCandidates(candidates, localWallet.address, readBalance),
|
|
448
551
|
});
|
|
449
552
|
return withPayer(mppx.fetch, {
|
|
450
553
|
source: resolvedWallet.source,
|
|
@@ -691,6 +794,21 @@ export function createLazyPaidFetch(options = {}) {
|
|
|
691
794
|
},
|
|
692
795
|
async pay(url, init, payOptions) {
|
|
693
796
|
const configured = await initialize();
|
|
797
|
+
if (payOptions.lightningPairing) {
|
|
798
|
+
// A bring-your-own pairing pays the `lnbtc` entry on its node's
|
|
799
|
+
// network; a 402 without one falls to the configured buyer, if any.
|
|
800
|
+
const executor = createLightningPayExecutor({
|
|
801
|
+
pairingUri: payOptions.lightningPairing,
|
|
802
|
+
baseFetch: sdkFetch,
|
|
803
|
+
fallback: async () => null,
|
|
804
|
+
walletNetwork: "probe",
|
|
805
|
+
});
|
|
806
|
+
const result = await executor(url, init, payOptions);
|
|
807
|
+
if (result.payment || result.response.status !== 402 || !configured?.pay)
|
|
808
|
+
return result;
|
|
809
|
+
await result.response.body?.cancel().catch(() => undefined);
|
|
810
|
+
return configured.pay(url, init, payOptions);
|
|
811
|
+
}
|
|
694
812
|
if (configured?.pay)
|
|
695
813
|
return configured.pay(url, init, payOptions);
|
|
696
814
|
const response = await sdkFetch(url, init);
|