stellar-tollbooth 0.1.0 → 0.1.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/wallet.js +36 -6
- package/package.json +1 -1
package/dist/wallet.js
CHANGED
|
@@ -6,6 +6,12 @@ export const NETWORK = (process.env.STELLAR_NETWORK ?? "stellar:testnet");
|
|
|
6
6
|
export const NATIVE_SAC = process.env.NATIVE_SAC ?? "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
|
|
7
7
|
export const API = (process.env.TOLLBOOTH_API ?? "http://localhost:3000").replace(/\/$/, "");
|
|
8
8
|
export const HORIZON = process.env.HORIZON_URL ?? "https://horizon-testnet.stellar.org";
|
|
9
|
+
/**
|
|
10
|
+
* Optional RPC override. The shared public testnet RPC does blip — a Cloudflare 521
|
|
11
|
+
* was observed during testing — and a room of agents hammering it is exactly when
|
|
12
|
+
* that matters. Point this at a dedicated endpoint for a live event.
|
|
13
|
+
*/
|
|
14
|
+
const RPC_URL = process.env.STELLAR_RPC_URL;
|
|
9
15
|
/** Per-payment ceiling, in atomic XLM units (7dp). */
|
|
10
16
|
const MAX_PER_CALL = process.env.TOLLBOOTH_MAX_PER_CALL ?? "5000000"; // 0.5 XLM
|
|
11
17
|
/** Cumulative ceiling for this process, in XLM. */
|
|
@@ -35,7 +41,7 @@ function assertBudget(priceXlm) {
|
|
|
35
41
|
}
|
|
36
42
|
const signer = createEd25519Signer(secret, NETWORK);
|
|
37
43
|
export const payingFetch = wrapFetchWithPaymentFromConfig(fetch, {
|
|
38
|
-
schemes: [{ network: NETWORK, client: new ExactStellarScheme(signer) }],
|
|
44
|
+
schemes: [{ network: NETWORK, client: new ExactStellarScheme(signer, RPC_URL ? { url: RPC_URL } : undefined) }],
|
|
39
45
|
spendControls: {
|
|
40
46
|
// Native XLM is NOT one of x402's "default assets" (those are USDC only), so it
|
|
41
47
|
// must be opted in explicitly or the client refuses before any network call.
|
|
@@ -53,15 +59,39 @@ export function agentHeaders(path) {
|
|
|
53
59
|
"x-agent-sig": Buffer.from(keypair.sign(Buffer.from(msg, "utf8"))).toString("base64"),
|
|
54
60
|
};
|
|
55
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* True only for failures raised while BUILDING the payment payload — i.e. before any
|
|
64
|
+
* transfer is signed or submitted. Retrying those is safe. Anything that happened
|
|
65
|
+
* after the payload exists could already have moved money, so it is never retried:
|
|
66
|
+
* a double charge is far worse than a failed call.
|
|
67
|
+
*/
|
|
68
|
+
function isPrePaymentFailure(e) {
|
|
69
|
+
const m = e?.message ?? "";
|
|
70
|
+
return /Failed to create payment payload|ECONNRESET|ETIMEDOUT|socket hang up|status code 5\d\d/i.test(m);
|
|
71
|
+
}
|
|
56
72
|
/** POST to a paid tollbooth route: budget check, pay, settle, return body + receipt. */
|
|
57
73
|
export async function paidPost(path, body, priceXlm) {
|
|
58
74
|
assertBudget(priceXlm);
|
|
59
75
|
const t0 = Date.now();
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
let res;
|
|
77
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
78
|
+
try {
|
|
79
|
+
res = await payingFetch(`${API}${path}`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { "Content-Type": "application/json", ...agentHeaders(path) },
|
|
82
|
+
body: JSON.stringify(body),
|
|
83
|
+
});
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
if (attempt === 2 || !isPrePaymentFailure(e))
|
|
88
|
+
throw e;
|
|
89
|
+
console.error(`[tollbooth] pre-payment failure, retrying (${attempt + 1}/2): ${e.message}`);
|
|
90
|
+
await new Promise((r) => setTimeout(r, 600 * 2 ** attempt));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!res)
|
|
94
|
+
throw new Error("payment failed with no response");
|
|
65
95
|
const settledMs = Date.now() - t0;
|
|
66
96
|
const json = (await res.json().catch(() => ({})));
|
|
67
97
|
let txHash;
|