@dvmkit/sdk 0.6.0-rc.1 → 0.7.0-rc.10
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 +39 -2
- package/dist/{chunk-PLG4XER6.js → chunk-2NUFE2PG.js} +47 -5
- package/dist/{chunk-7ZUKL4IV.js → chunk-3E4C6W7W.js} +16 -5
- package/dist/{chunk-HHBDBMEO.js → chunk-53SNRGQQ.js} +375 -158
- package/dist/{chunk-IWQHUSMO.js → chunk-62FI3NJF.js} +78 -2
- package/dist/chunk-6HJEVCVI.js +17 -0
- package/dist/chunk-A262WVYX.js +6 -0
- package/dist/{chunk-RLTSCKRI.js → chunk-BPYMEMQX.js} +50 -3
- package/dist/{chunk-IV6Q7NRW.js → chunk-C5RIQWG4.js} +1 -1
- package/dist/{chunk-GAOAYHZA.js → chunk-FBQVOOYF.js} +37 -6
- package/dist/{chunk-Z4ZT5LNT.js → chunk-GEI4MAXE.js} +3 -3
- package/dist/{chunk-FYRR2UEZ.js → chunk-GLIAR7RN.js} +1 -1
- package/dist/{chunk-X7EZ5CLI.js → chunk-HCATFJDD.js} +3 -3
- package/dist/{chunk-BYCEKO65.js → chunk-HPCXF3DV.js} +220 -32
- package/dist/{chunk-JH3SX3LY.js → chunk-IRXIH775.js} +6 -6
- package/dist/{chunk-QR2EF2NI.js → chunk-KCQDXIJU.js} +1 -42
- package/dist/chunk-MDW7ZR7V.js +47 -0
- package/dist/{chunk-KA6VN5FK.js → chunk-MQYM4T66.js} +3 -3
- package/dist/{chunk-P6QON7ER.js → chunk-NV7Z7377.js} +1 -1
- package/dist/{chunk-OQA7IHI4.js → chunk-OBBVDKLE.js} +484 -234
- package/dist/{chunk-EQLA4FFY.js → chunk-OHUXCAOM.js} +17 -6
- package/dist/{chunk-FI4CST2D.js → chunk-PZ5IAWFD.js} +5 -7
- package/dist/{chunk-IXR5RFK7.js → chunk-R3TQSXWT.js} +191 -5
- package/dist/{chunk-VHNPVGSA.js → chunk-T5DIXOLR.js} +3 -3
- package/dist/{chunk-RCM3P2HE.js → chunk-UKD3R2KU.js} +55 -4
- package/dist/chunk-WMGXJQI3.js +120 -0
- package/dist/{chunk-N5RZ5HVB.js → chunk-X6S2CHFM.js} +2 -2
- package/dist/{chunk-ETXDTUJO.js → chunk-ZCNPIILZ.js} +112 -1
- package/dist/{content-retention-BTGSQRfP.d.ts → content-retention-D2K4MKNO.d.ts} +1 -1
- package/dist/{credit-ledger-MI2H7NEC.js → credit-ledger-MFIIOSZH.js} +4 -5
- package/dist/{credit-menu-BAsrRu3z.d.ts → credit-menu-D2t1-Tf6.d.ts} +13 -6
- package/dist/{fx-BgDmyTdd.d.ts → fx-Ql4nvdbh.d.ts} +10 -3
- package/dist/index.d.ts +5 -5
- package/dist/index.js +248 -16
- package/dist/internal/caller.d.ts +4586 -7318
- package/dist/internal/caller.js +4431 -8127
- package/dist/internal/index.js +1 -1
- package/dist/internal/server.d.ts +7 -7
- package/dist/internal/server.js +30 -33
- package/dist/{job-store-BVeF5_Jq.d.ts → job-store-DCvJQUX0.d.ts} +1 -1
- package/dist/{lightning-backend-40fIzz1W.d.ts → lightning-backend-CY5ML3km.d.ts} +4 -2
- package/dist/memory-credit-ledger-44NE4PZC.js +11 -0
- package/dist/{mpp-setup-RF5FXGGV.js → mpp-setup-HRWUBULX.js} +4 -2
- package/dist/{payout-reporter-3Y2U55ZX.js → payout-reporter-7P66T77I.js} +6 -4
- package/dist/{postgres-consumed-credential-store-BUVCAPLS.js → postgres-consumed-credential-store-IDGPFKIL.js} +3 -3
- package/dist/{postgres-job-store-VHLQBJZC.js → postgres-job-store-UKNJ2F6Z.js} +2 -2
- package/dist/{postgres-replay-store-ARZSQ45X.js → postgres-replay-store-PMA4ADXN.js} +2 -2
- package/dist/{pricing-XT2IAPNE.js → pricing-52XJHJ6K.js} +12 -12
- package/dist/{revenue-reporter-MAERPYGK.js → revenue-reporter-HV7RMS54.js} +2 -2
- package/dist/server/index.d.ts +12 -11
- package/dist/server/index.js +49 -53
- package/dist/{step-cache-CPdH1hei.d.ts → step-cache-BOWAn_BH.d.ts} +84 -7
- package/dist/{tempo-charge-store-Q3D7KYMH.js → tempo-charge-store-P2Z4RMU7.js} +2 -2
- package/dist/{tempo-session-store-ZRNCOX7P.js → tempo-session-store-VSP7KLBS.js} +3 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +7 -6
- package/dist/{usd-AWlrwlM5.d.ts → usd-MAtMp3DB.d.ts} +1 -1
- package/dist/{x402-R6HPBY5D.js → x402-3IQ3HXSG.js} +9 -29
- package/package.json +3 -2
- package/dist/chunk-AT6V3SY7.js +0 -102
- package/dist/chunk-B2IROPQF.js +0 -1844
- package/dist/chunk-CZULM42S.js +0 -143
- package/dist/chunk-GFY5FRX3.js +0 -218
- package/dist/chunk-HCLVYZUZ.js +0 -244
- package/dist/chunk-LE74UEV7.js +0 -1071
- package/dist/chunk-LIRAHP7B.js +0 -53
- package/dist/chunk-MZRJR2CW.js +0 -776
- package/dist/chunk-OFVV37XC.js +0 -57
- package/dist/chunk-PAIM66YA.js +0 -347
- package/dist/chunk-UOCZXO2Z.js +0 -1663
- package/dist/memory-credit-ledger-W2BAUYJP.js +0 -12
- package/dist/tempo-lifecycle-ZBBLFBMH.js +0 -15
- package/dist/tempo-wallet-B5XXV3YH.js +0 -40
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
x402NetworkByCaip2,
|
|
3
3
|
x402NetworkBySlug
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BPYMEMQX.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/x402/constants.ts
|
|
7
7
|
var X402_DEFAULT_FACILITATOR = "https://x402.org/facilitator";
|
|
@@ -204,6 +204,73 @@ function isOptionalRecord(value) {
|
|
|
204
204
|
return value === void 0 || value === null || isRecord(value);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
// src/lib/payment-amounts.ts
|
|
208
|
+
function msatsToUsd(msats, rate) {
|
|
209
|
+
return msats / 1e11 * rate;
|
|
210
|
+
}
|
|
211
|
+
function usdToMsats(usd, rate) {
|
|
212
|
+
return Math.round(usd / rate * 1e11);
|
|
213
|
+
}
|
|
214
|
+
function msatsToUsdCents(msats, btcPriceUsd) {
|
|
215
|
+
const usd = msatsToUsd(msats, btcPriceUsd);
|
|
216
|
+
return Math.round(usd * 100);
|
|
217
|
+
}
|
|
218
|
+
function msatsToUsdc(msats, btcUsdRate) {
|
|
219
|
+
if (!Number.isSafeInteger(msats) || msats < 0) {
|
|
220
|
+
throw new RangeError("msats must be a non-negative safe integer");
|
|
221
|
+
}
|
|
222
|
+
if (!Number.isFinite(btcUsdRate) || btcUsdRate <= 0) {
|
|
223
|
+
throw new RangeError("BTC/USD rate must be finite and positive");
|
|
224
|
+
}
|
|
225
|
+
const [mantissa, exponent = "0"] = btcUsdRate.toString().split("e");
|
|
226
|
+
const decimalPlaces = mantissa.split(".")[1]?.length ?? 0;
|
|
227
|
+
const shift = Number(exponent) - decimalPlaces;
|
|
228
|
+
let numerator = BigInt(msats) * BigInt(mantissa.replace(".", ""));
|
|
229
|
+
let denominator = 100000n;
|
|
230
|
+
if (shift > 0) numerator *= 10n ** BigInt(shift);
|
|
231
|
+
else if (shift < 0) denominator *= 10n ** BigInt(-shift);
|
|
232
|
+
const micro = (numerator + denominator - 1n) / denominator;
|
|
233
|
+
if (micro > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
234
|
+
throw new RangeError("USDC micro-unit amount exceeds the safe integer range");
|
|
235
|
+
}
|
|
236
|
+
return Number(micro);
|
|
237
|
+
}
|
|
238
|
+
function usdcToMsats(usdc, btcUsdRate) {
|
|
239
|
+
return usdToMsats(usdc, btcUsdRate);
|
|
240
|
+
}
|
|
241
|
+
function msatsToMicro(msats, btcRate) {
|
|
242
|
+
return Math.round(msatsToUsd(msats, btcRate) * 1e6);
|
|
243
|
+
}
|
|
244
|
+
function microToSats(micro, btcRate) {
|
|
245
|
+
return Math.ceil(usdToMsats(micro / 1e6, btcRate) / 1e3);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/lib/payment-rails/rpc.ts
|
|
249
|
+
function resolveRpcOverride(envValue, bundled) {
|
|
250
|
+
const override = envValue?.trim() ?? "";
|
|
251
|
+
return override.length > 0 ? { url: override, source: "override" } : { url: bundled, source: "bundled" };
|
|
252
|
+
}
|
|
253
|
+
function resolveRpcHttpTransportOptions(source, envValue) {
|
|
254
|
+
if (source !== "override" || !envValue?.trim()) return void 0;
|
|
255
|
+
let parsed;
|
|
256
|
+
try {
|
|
257
|
+
parsed = JSON.parse(envValue);
|
|
258
|
+
} catch {
|
|
259
|
+
throw new Error("RPC headers must be a JSON object of non-empty string values.");
|
|
260
|
+
}
|
|
261
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
262
|
+
throw new Error("RPC headers must be a JSON object of non-empty string values.");
|
|
263
|
+
const headers = {};
|
|
264
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
265
|
+
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || typeof value !== "string" || value.length === 0 || /[\r\n]/.test(value))
|
|
266
|
+
throw new Error("RPC headers must be a JSON object of non-empty string values.");
|
|
267
|
+
headers[name] = value;
|
|
268
|
+
}
|
|
269
|
+
if (Object.keys(headers).length === 0)
|
|
270
|
+
throw new Error("RPC headers must be a JSON object of non-empty string values.");
|
|
271
|
+
return { fetchOptions: { headers } };
|
|
272
|
+
}
|
|
273
|
+
|
|
207
274
|
export {
|
|
208
275
|
X402_DEFAULT_FACILITATOR,
|
|
209
276
|
X402_VERSION,
|
|
@@ -226,5 +293,14 @@ export {
|
|
|
226
293
|
encodePaymentRequiredHeader,
|
|
227
294
|
decodePaymentRequiredHeader,
|
|
228
295
|
exactEvmAuthorization,
|
|
229
|
-
encodeSettleResponseHeader
|
|
296
|
+
encodeSettleResponseHeader,
|
|
297
|
+
msatsToUsd,
|
|
298
|
+
usdToMsats,
|
|
299
|
+
msatsToUsdCents,
|
|
300
|
+
msatsToUsdc,
|
|
301
|
+
usdcToMsats,
|
|
302
|
+
msatsToMicro,
|
|
303
|
+
microToSats,
|
|
304
|
+
resolveRpcOverride,
|
|
305
|
+
resolveRpcHttpTransportOptions
|
|
230
306
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
validateCurrency
|
|
3
|
+
} from "./chunk-HPCXF3DV.js";
|
|
4
|
+
|
|
5
|
+
// src/sdk/pricing/upfront.ts
|
|
6
|
+
function buildUpfront(input) {
|
|
7
|
+
const currency = validateCurrency(input.currency);
|
|
8
|
+
const amount = input.amount;
|
|
9
|
+
if (!Number.isFinite(amount) || amount < 0) {
|
|
10
|
+
throw new Error(`invalid upfront amount: ${String(amount)} (must be a non-negative number)`);
|
|
11
|
+
}
|
|
12
|
+
return { amount, currency };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
buildUpfront
|
|
17
|
+
};
|
|
@@ -1,6 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// src/lib/errors.ts
|
|
2
|
+
var DvmError = class extends Error {
|
|
3
|
+
constructor(code, message, hint, data, display, proseSources) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.hint = hint;
|
|
7
|
+
this.data = data;
|
|
8
|
+
this.display = display;
|
|
9
|
+
this.proseSources = proseSources;
|
|
10
|
+
this.name = "DvmError";
|
|
11
|
+
}
|
|
12
|
+
code;
|
|
13
|
+
hint;
|
|
14
|
+
data;
|
|
15
|
+
display;
|
|
16
|
+
proseSources;
|
|
17
|
+
};
|
|
18
|
+
function providerProseSources(...sources) {
|
|
19
|
+
const provenance = {};
|
|
20
|
+
for (const source of sources) {
|
|
21
|
+
if (!source) continue;
|
|
22
|
+
for (const field of ["message", "hint", "display"]) {
|
|
23
|
+
if (typeof source[field] === "string") provenance[field] = "provider";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return Object.keys(provenance).length > 0 ? provenance : void 0;
|
|
27
|
+
}
|
|
28
|
+
function isAbortError(err) {
|
|
29
|
+
return err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError");
|
|
30
|
+
}
|
|
31
|
+
function transportErrorCode(err) {
|
|
32
|
+
let current = err;
|
|
33
|
+
const seen = /* @__PURE__ */ new Set();
|
|
34
|
+
for (let depth = 0; depth < 5 && isErrorLike(current) && !seen.has(current); depth++) {
|
|
35
|
+
seen.add(current);
|
|
36
|
+
const code = current.code;
|
|
37
|
+
if (typeof code === "string" && /^(?:E[A-Z0-9_]{1,63}|UND_ERR_[A-Z0-9_]{1,63})$/.test(code)) {
|
|
38
|
+
return code;
|
|
39
|
+
}
|
|
40
|
+
current = current.cause;
|
|
41
|
+
}
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
function isErrorLike(value) {
|
|
45
|
+
return (typeof value === "object" || typeof value === "function") && value !== null;
|
|
46
|
+
}
|
|
4
47
|
|
|
5
48
|
// src/lib/x402/networks.ts
|
|
6
49
|
var X402_NETWORKS = [
|
|
@@ -99,6 +142,10 @@ var BY_CAIP2 = new Map(X402_NETWORKS.map((n) => [n.caip2, n]));
|
|
|
99
142
|
var BY_SLUG = new Map(X402_NETWORKS.map((n) => [n.slug, n]));
|
|
100
143
|
|
|
101
144
|
export {
|
|
145
|
+
DvmError,
|
|
146
|
+
providerProseSources,
|
|
147
|
+
isAbortError,
|
|
148
|
+
transportErrorCode,
|
|
102
149
|
X402_NETWORKS,
|
|
103
150
|
x402NetworkByCaip2,
|
|
104
151
|
x402NetworkBySlug,
|
|
@@ -1,12 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
diagnosticConsole
|
|
3
|
+
} from "./chunk-ZCNPIILZ.js";
|
|
1
4
|
import {
|
|
2
5
|
withSdkInitLock
|
|
3
6
|
} from "./chunk-COYKJRMM.js";
|
|
4
|
-
import {
|
|
5
|
-
diagnosticConsole
|
|
6
|
-
} from "./chunk-ETXDTUJO.js";
|
|
7
7
|
|
|
8
8
|
// src/sdk/server/payout-reporter.ts
|
|
9
9
|
import { createHash, randomUUID } from "crypto";
|
|
10
|
+
async function x402EarnedCeiling(channel, earnedNative) {
|
|
11
|
+
let earned;
|
|
12
|
+
try {
|
|
13
|
+
earned = await earnedNative(channel.channelId.toLowerCase());
|
|
14
|
+
} catch (error) {
|
|
15
|
+
return { kind: "unresolved", error };
|
|
16
|
+
}
|
|
17
|
+
if (earned === void 0) return { kind: "unbound" };
|
|
18
|
+
const charged = BigInt(channel.chargedCumulativeAmount);
|
|
19
|
+
const wanted = BigInt(earned);
|
|
20
|
+
return { kind: "ceiling", ceiling: wanted < charged ? wanted : charged };
|
|
21
|
+
}
|
|
10
22
|
var MemoryX402BatchStore = class {
|
|
11
23
|
batches = /* @__PURE__ */ new Map();
|
|
12
24
|
get(scope) {
|
|
@@ -719,7 +731,8 @@ var PayoutReporter = class {
|
|
|
719
731
|
}
|
|
720
732
|
/**
|
|
721
733
|
* x402: the open batch (claimed, not yet transferred) plus every channel's
|
|
722
|
-
*
|
|
734
|
+
* earned value not yet claimed — a channel whose earned figure cannot be
|
|
735
|
+
* read contributes nothing; wedged when the settle keeps failing, and the
|
|
723
736
|
* refund settlements stuck between chain and ledger beside it.
|
|
724
737
|
*/
|
|
725
738
|
async snapshotX402() {
|
|
@@ -751,12 +764,29 @@ var PayoutReporter = class {
|
|
|
751
764
|
}
|
|
752
765
|
const unclaimed = [];
|
|
753
766
|
for (const channel of await ctx.storage.list()) {
|
|
754
|
-
const
|
|
767
|
+
const claimed = BigInt(channel.totalClaimed);
|
|
768
|
+
if (BigInt(channel.chargedCumulativeAmount) <= claimed) continue;
|
|
769
|
+
let resolved;
|
|
770
|
+
try {
|
|
771
|
+
resolved = await x402EarnedCeiling(channel, ctx.earnedNative);
|
|
772
|
+
} catch (err) {
|
|
773
|
+
resolved = { kind: "unresolved", error: err };
|
|
774
|
+
}
|
|
775
|
+
if (resolved.kind === "unresolved") {
|
|
776
|
+
warn("payout_pending_snapshot_failed", {
|
|
777
|
+
rail: "x402",
|
|
778
|
+
"channel.id": channel.channelId,
|
|
779
|
+
error: message(resolved.error)
|
|
780
|
+
});
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
if (resolved.kind === "unbound") continue;
|
|
784
|
+
const owed = resolved.ceiling - claimed;
|
|
755
785
|
if (owed > 0n) {
|
|
756
786
|
unclaimed.push({
|
|
757
787
|
id: channel.channelId,
|
|
758
788
|
native: Number(owed),
|
|
759
|
-
label: "
|
|
789
|
+
label: "earned, awaiting claim"
|
|
760
790
|
});
|
|
761
791
|
}
|
|
762
792
|
}
|
|
@@ -902,6 +932,7 @@ function warn(level, fields) {
|
|
|
902
932
|
}
|
|
903
933
|
|
|
904
934
|
export {
|
|
935
|
+
x402EarnedCeiling,
|
|
905
936
|
MemoryX402BatchStore,
|
|
906
937
|
PostgresX402BatchStore,
|
|
907
938
|
PAYOUT_SNAPSHOT_INTERVAL_MS,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
diagnosticConsole
|
|
3
|
+
} from "./chunk-ZCNPIILZ.js";
|
|
1
4
|
import {
|
|
2
5
|
withSdkInitLock
|
|
3
6
|
} from "./chunk-COYKJRMM.js";
|
|
4
|
-
import {
|
|
5
|
-
diagnosticConsole
|
|
6
|
-
} from "./chunk-ETXDTUJO.js";
|
|
7
7
|
|
|
8
8
|
// src/sdk/server/revenue-reporter.ts
|
|
9
9
|
import { randomUUID } from "crypto";
|
|
@@ -2,12 +2,12 @@ import {
|
|
|
2
2
|
parseStoreValue,
|
|
3
3
|
stringifyStoreValue
|
|
4
4
|
} from "./chunk-YG7G4DPZ.js";
|
|
5
|
+
import {
|
|
6
|
+
withPostgresTransactionRecovery
|
|
7
|
+
} from "./chunk-NV7Z7377.js";
|
|
5
8
|
import {
|
|
6
9
|
withSdkInitLock
|
|
7
10
|
} from "./chunk-COYKJRMM.js";
|
|
8
|
-
import {
|
|
9
|
-
withPostgresTransactionRecovery
|
|
10
|
-
} from "./chunk-P6QON7ER.js";
|
|
11
11
|
|
|
12
12
|
// src/sdk/server/tempo-session-store.ts
|
|
13
13
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
diagnosticConsole
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZCNPIILZ.js";
|
|
4
4
|
|
|
5
5
|
// src/sdk/pricing/currency.ts
|
|
6
6
|
var InvalidCurrencyError = class extends Error {
|
|
@@ -35,6 +35,98 @@ function validateCurrency(raw) {
|
|
|
35
35
|
return raw;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// src/sdk/pricing/usd.ts
|
|
39
|
+
var InvalidFxRateError = class extends Error {
|
|
40
|
+
code = "invalid_fx_rate";
|
|
41
|
+
ratePerBtc;
|
|
42
|
+
constructor(ratePerBtc) {
|
|
43
|
+
super(
|
|
44
|
+
`invalid fx rate ${String(ratePerBtc)} \u2014 expected a finite positive number of fiat per BTC`
|
|
45
|
+
);
|
|
46
|
+
this.ratePerBtc = ratePerBtc;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function fiatToSatsCeil(amount, ratePerBtc) {
|
|
50
|
+
if (!Number.isFinite(ratePerBtc) || ratePerBtc <= 0) {
|
|
51
|
+
throw new InvalidFxRateError(ratePerBtc);
|
|
52
|
+
}
|
|
53
|
+
if (!Number.isFinite(amount) || amount < 0) return 0;
|
|
54
|
+
return Math.ceil(amount * 1e8 / ratePerBtc);
|
|
55
|
+
}
|
|
56
|
+
function satsToFiat(sats, ratePerBtc) {
|
|
57
|
+
if (!Number.isFinite(ratePerBtc) || ratePerBtc <= 0) {
|
|
58
|
+
throw new InvalidFxRateError(ratePerBtc);
|
|
59
|
+
}
|
|
60
|
+
return sats / 1e8 * ratePerBtc;
|
|
61
|
+
}
|
|
62
|
+
var InvalidUsdPriceError = class extends Error {
|
|
63
|
+
constructor(field, value, detail) {
|
|
64
|
+
super(`${field}: ${detail}`);
|
|
65
|
+
this.field = field;
|
|
66
|
+
this.value = value;
|
|
67
|
+
this.name = "InvalidUsdPriceError";
|
|
68
|
+
}
|
|
69
|
+
field;
|
|
70
|
+
value;
|
|
71
|
+
code = "invalid_usd_price";
|
|
72
|
+
};
|
|
73
|
+
function parseUsdPrice(value, field) {
|
|
74
|
+
if (!USD_PRICE_RE.test(value)) {
|
|
75
|
+
throw new InvalidUsdPriceError(
|
|
76
|
+
field,
|
|
77
|
+
value,
|
|
78
|
+
`${JSON.stringify(value)} is not a USD price literal. Use a "$X.XX" string, e.g. "$0.05".`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
const usd = parseFloat(value.slice(1));
|
|
82
|
+
if (!Number.isFinite(usd) || usd < MIN_USD_PRICE) {
|
|
83
|
+
throw new InvalidUsdPriceError(
|
|
84
|
+
field,
|
|
85
|
+
value,
|
|
86
|
+
`${JSON.stringify(value)} is below ${MIN_USD_PRICE_LITERAL}, the smallest price the wire can carry \u2014 every price surface rounds to four decimals, so this would advertise as $0 and then charge anyway. Charge at least ${MIN_USD_PRICE_LITERAL}, or omit the price for a free capability.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (roundUsd(usd) !== usd) {
|
|
90
|
+
throw new InvalidUsdPriceError(
|
|
91
|
+
field,
|
|
92
|
+
value,
|
|
93
|
+
`${JSON.stringify(value)} carries more than four decimal places. Every price surface rounds to four, so this would advertise ${formatUsd(roundUsd(usd))} and charge something else. Use at most four decimals.`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (!Number.isSafeInteger(Math.round(usd * 1e6))) {
|
|
97
|
+
throw new InvalidUsdPriceError(
|
|
98
|
+
field,
|
|
99
|
+
value,
|
|
100
|
+
`${JSON.stringify(value)} is too large to denominate. Use an amount under $9,007,199,254.`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return usd;
|
|
104
|
+
}
|
|
105
|
+
function roundUsd(usd) {
|
|
106
|
+
return Math.round(usd * 1e4) / 1e4;
|
|
107
|
+
}
|
|
108
|
+
function formatUsd(usd) {
|
|
109
|
+
if (usd === 0) return "$0.00";
|
|
110
|
+
if (usd >= 0.01) return `$${usd.toFixed(2)}`;
|
|
111
|
+
return `$${usd.toFixed(4)}`;
|
|
112
|
+
}
|
|
113
|
+
function formatFiat(amount, currency) {
|
|
114
|
+
const code = currency.toUpperCase();
|
|
115
|
+
const symbol = FIAT_SYMBOLS[currency] ?? "";
|
|
116
|
+
const digits = currency === "jpy" ? 0 : amount >= 0.01 || amount === 0 ? 2 : 4;
|
|
117
|
+
const body = `${amount.toFixed(digits)} ${code}`;
|
|
118
|
+
return symbol ? `${symbol}${body}` : body;
|
|
119
|
+
}
|
|
120
|
+
var USD_PRICE_RE = /^\$\d+(\.\d+)?$/;
|
|
121
|
+
var MIN_USD_PRICE = 1e-4;
|
|
122
|
+
var MIN_USD_PRICE_LITERAL = '"$0.0001"';
|
|
123
|
+
var FIAT_SYMBOLS = {
|
|
124
|
+
usd: "$",
|
|
125
|
+
eur: "\u20AC",
|
|
126
|
+
gbp: "\xA3",
|
|
127
|
+
jpy: "\xA5"
|
|
128
|
+
};
|
|
129
|
+
|
|
38
130
|
// src/sdk/pricing/fx.ts
|
|
39
131
|
var DEFAULT_FX_CURRENCIES = ["usd", "eur", "gbp", "jpy"];
|
|
40
132
|
var DEFAULT_FX_RATE_SOURCE = `https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=${DEFAULT_FX_CURRENCIES.join(",")}`;
|
|
@@ -52,14 +144,53 @@ var FxRateUnavailableError = class extends Error {
|
|
|
52
144
|
* the last-known rate without it. See {@link FxKnownStale}.
|
|
53
145
|
*/
|
|
54
146
|
knownStale;
|
|
55
|
-
|
|
147
|
+
sourceFailure;
|
|
148
|
+
constructor(message, copy, knownStale, sourceFailure) {
|
|
56
149
|
super(message);
|
|
57
150
|
this.name = "FxRateUnavailableError";
|
|
58
151
|
this.display = copy?.display ?? "Couldn't fetch the BTC reference rate to lock pricing";
|
|
59
152
|
this.hint = copy?.hint ?? "transient \u2014 try the quote again in a few seconds";
|
|
60
153
|
this.knownStale = knownStale;
|
|
154
|
+
this.sourceFailure = normalizeFxSourceFailure(sourceFailure);
|
|
61
155
|
}
|
|
62
156
|
};
|
|
157
|
+
var FX_SOURCE_FAILURE_CATEGORIES = /* @__PURE__ */ new Set([
|
|
158
|
+
"network_error",
|
|
159
|
+
"http_error",
|
|
160
|
+
"invalid_body",
|
|
161
|
+
"no_usable_rate",
|
|
162
|
+
"missing_currency",
|
|
163
|
+
"unknown"
|
|
164
|
+
]);
|
|
165
|
+
function normalizeFxSourceFailure(input) {
|
|
166
|
+
if (input === void 0) return void 0;
|
|
167
|
+
try {
|
|
168
|
+
if (typeof input !== "object" || input === null) return { category: "unknown" };
|
|
169
|
+
const categoryDescriptor = Object.getOwnPropertyDescriptor(input, "category");
|
|
170
|
+
const category = categoryDescriptor && "value" in categoryDescriptor ? categoryDescriptor.value : void 0;
|
|
171
|
+
if (typeof category !== "string" || !FX_SOURCE_FAILURE_CATEGORIES.has(category)) {
|
|
172
|
+
return { category: "unknown" };
|
|
173
|
+
}
|
|
174
|
+
const statusDescriptor = Object.getOwnPropertyDescriptor(input, "httpStatus");
|
|
175
|
+
const status = statusDescriptor && "value" in statusDescriptor ? statusDescriptor.value : void 0;
|
|
176
|
+
return {
|
|
177
|
+
category,
|
|
178
|
+
...category === "http_error" && typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599 && { httpStatus: status }
|
|
179
|
+
};
|
|
180
|
+
} catch {
|
|
181
|
+
return { category: "unknown" };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function readFxSourceFailure(error) {
|
|
185
|
+
try {
|
|
186
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, "sourceFailure");
|
|
187
|
+
if (!descriptor) return void 0;
|
|
188
|
+
if (!("value" in descriptor)) return { category: "unknown" };
|
|
189
|
+
return normalizeFxSourceFailure(descriptor.value);
|
|
190
|
+
} catch {
|
|
191
|
+
return { category: "unknown" };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
63
194
|
function createFxFetcher(opts) {
|
|
64
195
|
const currencies = opts?.currencies ?? DEFAULT_FX_CURRENCIES;
|
|
65
196
|
const source = opts?.source ?? DEFAULT_FX_RATE_SOURCE;
|
|
@@ -100,7 +231,10 @@ function createFxFetcher(opts) {
|
|
|
100
231
|
}
|
|
101
232
|
}
|
|
102
233
|
throw new FxRateUnavailableError(
|
|
103
|
-
lastErr instanceof Error ? lastErr.message : String(lastErr)
|
|
234
|
+
lastErr instanceof Error ? lastErr.message : String(lastErr),
|
|
235
|
+
void 0,
|
|
236
|
+
void 0,
|
|
237
|
+
lastErr instanceof FxRateUnavailableError ? readFxSourceFailure(lastErr) : { category: "unknown" }
|
|
104
238
|
);
|
|
105
239
|
})();
|
|
106
240
|
inFlight = promise;
|
|
@@ -127,7 +261,9 @@ function fxRateFor(snapshot, currency) {
|
|
|
127
261
|
{
|
|
128
262
|
display: `The ${currency.toUpperCase()} reference rate is unavailable right now.`,
|
|
129
263
|
hint: `The rate source stopped carrying ${currency.toUpperCase()}. Retry in a minute; if it persists the source needs to be reconfigured.`
|
|
130
|
-
}
|
|
264
|
+
},
|
|
265
|
+
void 0,
|
|
266
|
+
{ category: "missing_currency" }
|
|
131
267
|
);
|
|
132
268
|
}
|
|
133
269
|
throw new UnsupportedCurrencyError(currency, Object.keys(snapshot.rates));
|
|
@@ -139,19 +275,32 @@ async function fetchOnce(source, currencies, fetchFn) {
|
|
|
139
275
|
return fetchPlatformShape(source, currencies, fetchFn);
|
|
140
276
|
}
|
|
141
277
|
async function fetchCoinGeckoShape(source, currencies, fetchFn) {
|
|
142
|
-
const res = await fetchFn
|
|
278
|
+
const res = await fetchSource(fetchFn, source);
|
|
143
279
|
if (!res.ok) {
|
|
144
|
-
throw new
|
|
280
|
+
throw new FxRateUnavailableError(
|
|
281
|
+
`fx source ${source} returned ${res.status.toString()}`,
|
|
282
|
+
void 0,
|
|
283
|
+
void 0,
|
|
284
|
+
{ category: "http_error", httpStatus: res.status }
|
|
285
|
+
);
|
|
145
286
|
}
|
|
146
|
-
const body = await res
|
|
147
|
-
const bitcoin = body
|
|
148
|
-
if (!bitcoin || typeof bitcoin !== "object") {
|
|
149
|
-
throw new
|
|
287
|
+
const body = await parseRateBody(res);
|
|
288
|
+
const bitcoin = body?.bitcoin;
|
|
289
|
+
if (!bitcoin || typeof bitcoin !== "object" || Array.isArray(bitcoin)) {
|
|
290
|
+
throw new FxRateUnavailableError(
|
|
291
|
+
`fx source ${source} returned an unparsable rate body`,
|
|
292
|
+
void 0,
|
|
293
|
+
void 0,
|
|
294
|
+
{ category: "invalid_body" }
|
|
295
|
+
);
|
|
150
296
|
}
|
|
151
|
-
return {
|
|
297
|
+
return {
|
|
298
|
+
rates: collectRates(source, bitcoin, currencies),
|
|
299
|
+
as_of: (/* @__PURE__ */ new Date()).toISOString()
|
|
300
|
+
};
|
|
152
301
|
}
|
|
153
302
|
async function fetchPlatformShape(source, currencies, fetchFn) {
|
|
154
|
-
const res = await fetchFn
|
|
303
|
+
const res = await fetchSource(fetchFn, source.url, {
|
|
155
304
|
headers: { Authorization: `Bearer ${source.token}`, Accept: "application/json" }
|
|
156
305
|
});
|
|
157
306
|
if (!res.ok) {
|
|
@@ -160,20 +309,57 @@ async function fetchPlatformShape(source, currencies, fetchFn) {
|
|
|
160
309
|
throw new FxRateUnavailableError(
|
|
161
310
|
`fx source ${source.url} refused: rate ${knownStale.staleDays.toString()}d stale` + (knownStale.thresholdDays !== void 0 ? ` (threshold ${knownStale.thresholdDays.toString()}d)` : ""),
|
|
162
311
|
void 0,
|
|
163
|
-
knownStale
|
|
312
|
+
knownStale,
|
|
313
|
+
{ category: "http_error", httpStatus: res.status }
|
|
164
314
|
);
|
|
165
315
|
}
|
|
166
|
-
throw new
|
|
316
|
+
throw new FxRateUnavailableError(
|
|
317
|
+
`fx source ${source.url} returned ${res.status.toString()}`,
|
|
318
|
+
void 0,
|
|
319
|
+
void 0,
|
|
320
|
+
{ category: "http_error", httpStatus: res.status }
|
|
321
|
+
);
|
|
167
322
|
}
|
|
168
|
-
const body = await res
|
|
169
|
-
const raw = body
|
|
170
|
-
if (!raw || typeof raw !== "object") {
|
|
171
|
-
throw new
|
|
323
|
+
const body = await parseRateBody(res);
|
|
324
|
+
const raw = body?.rates;
|
|
325
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
326
|
+
throw new FxRateUnavailableError(
|
|
327
|
+
`fx source ${source.url} returned an unparsable rate body`,
|
|
328
|
+
void 0,
|
|
329
|
+
void 0,
|
|
330
|
+
{ category: "invalid_body" }
|
|
331
|
+
);
|
|
172
332
|
}
|
|
173
333
|
const rates = collectRates(source.url, raw, currencies);
|
|
174
334
|
const as_of = typeof body.as_of === "string" ? body.as_of : (/* @__PURE__ */ new Date()).toISOString();
|
|
175
335
|
return { rates, as_of };
|
|
176
336
|
}
|
|
337
|
+
async function fetchSource(fetchFn, url, init) {
|
|
338
|
+
try {
|
|
339
|
+
return await fetchFn(url, init);
|
|
340
|
+
} catch (err) {
|
|
341
|
+
if (err instanceof FxRateUnavailableError) throw err;
|
|
342
|
+
throw new FxRateUnavailableError(
|
|
343
|
+
err instanceof Error ? err.message : String(err),
|
|
344
|
+
void 0,
|
|
345
|
+
void 0,
|
|
346
|
+
{ category: "network_error" }
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async function parseRateBody(res) {
|
|
351
|
+
try {
|
|
352
|
+
const body = await res.json();
|
|
353
|
+
return typeof body === "object" && body !== null && !Array.isArray(body) ? body : null;
|
|
354
|
+
} catch {
|
|
355
|
+
throw new FxRateUnavailableError(
|
|
356
|
+
"fx source returned an unparsable rate body",
|
|
357
|
+
void 0,
|
|
358
|
+
void 0,
|
|
359
|
+
{ category: "invalid_body" }
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
177
363
|
function collectRates(source, raw, currencies) {
|
|
178
364
|
const rates = {};
|
|
179
365
|
for (const currency of currencies) {
|
|
@@ -182,8 +368,11 @@ function collectRates(source, raw, currencies) {
|
|
|
182
368
|
rates[currency] = value;
|
|
183
369
|
}
|
|
184
370
|
if (Object.keys(rates).length === 0) {
|
|
185
|
-
throw new
|
|
186
|
-
`fx source ${source} returned no usable rate for any of [${currencies.join(", ")}]
|
|
371
|
+
throw new FxRateUnavailableError(
|
|
372
|
+
`fx source ${source} returned no usable rate for any of [${currencies.join(", ")}]`,
|
|
373
|
+
void 0,
|
|
374
|
+
void 0,
|
|
375
|
+
{ category: "no_usable_rate" }
|
|
187
376
|
);
|
|
188
377
|
}
|
|
189
378
|
return rates;
|
|
@@ -240,6 +429,7 @@ async function parsePlatformStaleRefusal(res) {
|
|
|
240
429
|
} catch {
|
|
241
430
|
return null;
|
|
242
431
|
}
|
|
432
|
+
if (typeof body !== "object" || body === null) return null;
|
|
243
433
|
const error = body.error;
|
|
244
434
|
if (!error || typeof error !== "object") return null;
|
|
245
435
|
const { code, detail } = error;
|
|
@@ -268,28 +458,26 @@ function resolveFxSourceFromEnv(env) {
|
|
|
268
458
|
return raw;
|
|
269
459
|
}
|
|
270
460
|
|
|
271
|
-
// src/sdk/pricing/upfront.ts
|
|
272
|
-
function buildUpfront(input) {
|
|
273
|
-
const currency = validateCurrency(input.currency);
|
|
274
|
-
const amount = input.amount;
|
|
275
|
-
if (!Number.isFinite(amount) || amount < 0) {
|
|
276
|
-
throw new Error(`invalid upfront amount: ${String(amount)} (must be a non-negative number)`);
|
|
277
|
-
}
|
|
278
|
-
return { amount, currency };
|
|
279
|
-
}
|
|
280
|
-
|
|
281
461
|
export {
|
|
282
462
|
InvalidCurrencyError,
|
|
283
463
|
UnsupportedCurrencyError,
|
|
284
464
|
validateCurrency,
|
|
465
|
+
InvalidFxRateError,
|
|
466
|
+
fiatToSatsCeil,
|
|
467
|
+
satsToFiat,
|
|
468
|
+
InvalidUsdPriceError,
|
|
469
|
+
parseUsdPrice,
|
|
470
|
+
roundUsd,
|
|
471
|
+
formatUsd,
|
|
472
|
+
formatFiat,
|
|
285
473
|
DEFAULT_FX_CURRENCIES,
|
|
286
474
|
DEFAULT_FX_RATE_SOURCE,
|
|
287
475
|
FX_CACHE_TTL_MS,
|
|
288
476
|
FX_RETRY_COUNT,
|
|
289
477
|
FxRateUnavailableError,
|
|
478
|
+
readFxSourceFailure,
|
|
290
479
|
createFxFetcher,
|
|
291
480
|
warmFxSnapshot,
|
|
292
481
|
fxRateFor,
|
|
293
|
-
resolveFxSourceFromEnv
|
|
294
|
-
buildUpfront
|
|
482
|
+
resolveFxSourceFromEnv
|
|
295
483
|
};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
withSdkInitLock
|
|
3
|
-
} from "./chunk-COYKJRMM.js";
|
|
4
1
|
import {
|
|
5
2
|
applyRetryToPool
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NV7Z7377.js";
|
|
7
4
|
import {
|
|
8
5
|
x402NetworkByCaip2
|
|
9
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BPYMEMQX.js";
|
|
10
7
|
import {
|
|
11
8
|
diagnosticConsole
|
|
12
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ZCNPIILZ.js";
|
|
10
|
+
import {
|
|
11
|
+
withSdkInitLock
|
|
12
|
+
} from "./chunk-COYKJRMM.js";
|
|
13
13
|
|
|
14
14
|
// src/sdk/server/credit-ledger.ts
|
|
15
15
|
import { randomUUID } from "crypto";
|