@dvmkit/sdk 0.3.4-rc.10 → 0.4.0-rc.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.
Files changed (50) hide show
  1. package/dist/{chunk-C2AGARLM.js → chunk-2VGWYG4P.js} +5 -3
  2. package/dist/{chunk-A4CM2BD3.js → chunk-3MHE725Z.js} +1 -1
  3. package/dist/{chunk-VAMSJRJT.js → chunk-5LR6P7WN.js} +5 -3
  4. package/dist/{chunk-YYQG4R7T.js → chunk-74PUEZJ4.js} +6 -4
  5. package/dist/{chunk-MKI6OVW4.js → chunk-CZULM42S.js} +0 -51
  6. package/dist/{chunk-M4QDXD2A.js → chunk-FYRR2UEZ.js} +1 -1
  7. package/dist/{chunk-4UZN2GP5.js → chunk-GAOAYHZA.js} +209 -33
  8. package/dist/{chunk-Z4BNLUZF.js → chunk-IWQHUSMO.js} +3 -105
  9. package/dist/{chunk-4J32DTLT.js → chunk-IXR5RFK7.js} +82 -4
  10. package/dist/{chunk-2SD2W2BG.js → chunk-JH3SX3LY.js} +252 -26
  11. package/dist/{chunk-UZC4A4DM.js → chunk-K2YX2VRA.js} +2480 -206
  12. package/dist/chunk-KA6VN5FK.js +245 -0
  13. package/dist/chunk-LIRAHP7B.js +53 -0
  14. package/dist/{chunk-MG67KXU7.js → chunk-MZRJR2CW.js} +4 -2
  15. package/dist/{chunk-JDFLRNAZ.js → chunk-PB4FYZET.js} +67 -7
  16. package/dist/{chunk-BQ2NMWKE.js → chunk-PLG4XER6.js} +1 -1
  17. package/dist/{chunk-Z3EHBW2G.js → chunk-R4DQZFAL.js} +7 -5
  18. package/dist/chunk-RLTSCKRI.js +109 -0
  19. package/dist/{chunk-2KGG5ORW.js → chunk-TXX4GOUB.js} +3 -3
  20. package/dist/{chunk-YRDA7EWN.js → chunk-W2EQGDF5.js} +14 -10
  21. package/dist/{chunk-EAMZZ2B4.js → chunk-X7EZ5CLI.js} +59 -0
  22. package/dist/{chunk-7X6UDVCQ.js → chunk-Z4ZT5LNT.js} +66 -2
  23. package/dist/{credit-ledger-A35WH2TI.js → credit-ledger-MI2H7NEC.js} +3 -1
  24. package/dist/{credit-menu-0-UtJ3hh.d.ts → credit-menu-B8_p_vUz.d.ts} +133 -7
  25. package/dist/{fx-DhyT1Cjh.d.ts → fx-DuaYHvVn.d.ts} +1 -1
  26. package/dist/index.d.ts +6 -6
  27. package/dist/internal/caller.d.ts +6 -6
  28. package/dist/internal/caller.js +28 -24
  29. package/dist/internal/server.d.ts +11 -85
  30. package/dist/internal/server.js +18 -16
  31. package/dist/{job-store-D3CcZp_g.d.ts → job-store-D6Rjj5Oi.d.ts} +1 -1
  32. package/dist/{lightning-backend-BhsyrBLa.d.ts → lightning-backend-BaeCPmx7.d.ts} +1 -1
  33. package/dist/{memory-credit-ledger-2LBZVMKJ.js → memory-credit-ledger-W2BAUYJP.js} +4 -2
  34. package/dist/{mpp-setup-7CGBKNUP.js → mpp-setup-RF5FXGGV.js} +5 -1
  35. package/dist/{payout-reporter-ZMOA37GN.js → payout-reporter-3Y2U55ZX.js} +1 -1
  36. package/dist/{postgres-job-store-MSD2HZOI.js → postgres-job-store-VLXLWXUQ.js} +1 -1
  37. package/dist/{revenue-reporter-EKJOGQYR.js → revenue-reporter-MAERPYGK.js} +1 -1
  38. package/dist/server/index.d.ts +14 -10
  39. package/dist/server/index.js +54 -17
  40. package/dist/{step-cache-v2yhyhSZ.d.ts → step-cache-Bi43A0JN.d.ts} +1769 -1549
  41. package/dist/{tempo-charge-store-4WX7FPOV.js → tempo-charge-store-Q3D7KYMH.js} +1 -1
  42. package/dist/tempo-lifecycle-OTHV757Y.js +14 -0
  43. package/dist/{tempo-session-store-LCSDHB7G.js → tempo-session-store-ZRNCOX7P.js} +1 -1
  44. package/dist/{tempo-wallet-RCGBBAJH.js → tempo-wallet-32JWQZKQ.js} +4 -3
  45. package/dist/testing/index.d.ts +3 -3
  46. package/dist/{usd-Cu9G6xv2.d.ts → usd-AZPVq4eU.d.ts} +1 -1
  47. package/dist/{x402-S47Q77UE.js → x402-CEDHGZDI.js} +17 -14
  48. package/package.json +1 -1
  49. package/dist/chunk-YHN3WMMF.js +0 -122
  50. package/dist/tempo-lifecycle-TJ62A7UX.js +0 -13
@@ -1,11 +1,13 @@
1
1
  import {
2
2
  IMPLICIT_CREDIT_TTL_MS
3
- } from "./chunk-A4CM2BD3.js";
3
+ } from "./chunk-3MHE725Z.js";
4
4
  import {
5
5
  CREDITS_FILE,
6
- DvmError,
7
6
  ensureConfigDir
8
- } from "./chunk-MKI6OVW4.js";
7
+ } from "./chunk-CZULM42S.js";
8
+ import {
9
+ DvmError
10
+ } from "./chunk-LIRAHP7B.js";
9
11
 
10
12
  // src/lib/output.ts
11
13
  import { writeSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BTC_RATE_FILE,
3
3
  ensureConfigSubdir
4
- } from "./chunk-MKI6OVW4.js";
4
+ } from "./chunk-CZULM42S.js";
5
5
  import {
6
6
  formatFiat
7
7
  } from "./chunk-AT6V3SY7.js";
@@ -2,22 +2,24 @@ import {
2
2
  costAnchor,
3
3
  formatUsd,
4
4
  msatsToUsd
5
- } from "./chunk-A4CM2BD3.js";
5
+ } from "./chunk-3MHE725Z.js";
6
6
  import {
7
7
  AGENT_MNEMONIC_FILE,
8
8
  BUILDER_CONFIG_DIR,
9
9
  BUILDER_FILE,
10
10
  BUILDER_KEY_FILE,
11
- DvmError,
12
11
  WALLET_FILE,
13
12
  ensureConfigDir
14
- } from "./chunk-MKI6OVW4.js";
13
+ } from "./chunk-CZULM42S.js";
15
14
  import {
16
15
  canonicaliseForSigning
17
16
  } from "./chunk-KXZUCCEY.js";
18
17
  import {
19
18
  callerLoggers
20
19
  } from "./chunk-66HGCPBU.js";
20
+ import {
21
+ DvmError
22
+ } from "./chunk-LIRAHP7B.js";
21
23
  import {
22
24
  diagnosticConsole
23
25
  } from "./chunk-ETXDTUJO.js";
@@ -2,16 +2,18 @@ import {
2
2
  isValidEvmPrivateKey,
3
3
  loadConfig,
4
4
  updateConfig
5
- } from "./chunk-MG67KXU7.js";
5
+ } from "./chunk-MZRJR2CW.js";
6
6
  import {
7
- DvmError,
8
7
  resolveRpcHttpTransportOptions,
9
8
  resolveRpcOverride
10
- } from "./chunk-MKI6OVW4.js";
9
+ } from "./chunk-CZULM42S.js";
11
10
  import {
12
11
  redactUrl,
13
12
  redactUrlsInText
14
13
  } from "./chunk-FUJ36YDV.js";
14
+ import {
15
+ DvmError
16
+ } from "./chunk-LIRAHP7B.js";
15
17
 
16
18
  // src/lib/payment-rails/tempo-wallet.ts
17
19
  var TEMPO_USDC_MAINNET = "0x20C000000000000000000000b9537d11c60E8b50";
@@ -101,7 +103,7 @@ async function generateTempoKey(opts = {}) {
101
103
  );
102
104
  }
103
105
  if (existing && opts.force) {
104
- const { assertTempoWalletIdle } = await import("./tempo-lifecycle-TJ62A7UX.js");
106
+ const { assertTempoWalletIdle } = await import("./tempo-lifecycle-OTHV757Y.js");
105
107
  await assertTempoWalletIdle("replace");
106
108
  }
107
109
  const { privateKeyToAccount, generatePrivateKey } = await import("viem/accounts");
@@ -1,50 +1,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
- }
47
-
48
1
  // src/defaults.ts
49
2
  import { homedir } from "os";
50
3
  import { join } from "path";
@@ -158,10 +111,6 @@ function resolveRpcHttpTransportOptions(source, envValue) {
158
111
  }
159
112
 
160
113
  export {
161
- DvmError,
162
- providerProseSources,
163
- isAbortError,
164
- transportErrorCode,
165
114
  CONFIG_DIR,
166
115
  CONFIG_FILE,
167
116
  CONFIG_BACKUP_FILE,
@@ -15,7 +15,7 @@ import {
15
15
  lotOwedSats,
16
16
  netOwedSats,
17
17
  x402SettlementPending
18
- } from "./chunk-2SD2W2BG.js";
18
+ } from "./chunk-JH3SX3LY.js";
19
19
 
20
20
  // src/sdk/server/memory-credit-ledger.ts
21
21
  import { randomUUID } from "crypto";
@@ -52,9 +52,13 @@ var PostgresX402BatchStore = class {
52
52
  first_failed_at BIGINT,
53
53
  last_error TEXT,
54
54
  next_retry_at BIGINT,
55
+ obligation_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
55
56
  updated_at BIGINT NOT NULL
56
57
  )
57
58
  `);
59
+ await this.db.query(
60
+ "ALTER TABLE payout_x402_batches ADD COLUMN IF NOT EXISTS obligation_ids JSONB NOT NULL DEFAULT '[]'::jsonb"
61
+ );
58
62
  });
59
63
  }
60
64
  async get(scope) {
@@ -71,15 +75,16 @@ var PostgresX402BatchStore = class {
71
75
  attempts: row.attempts,
72
76
  firstFailedAt: row.first_failed_at === null ? null : Number(row.first_failed_at),
73
77
  lastError: row.last_error,
74
- nextRetryAt: row.next_retry_at === null ? null : Number(row.next_retry_at)
78
+ nextRetryAt: row.next_retry_at === null ? null : Number(row.next_retry_at),
79
+ ...row.obligation_ids.length ? { obligationIds: row.obligation_ids } : {}
75
80
  };
76
81
  }
77
82
  async put(batch) {
78
83
  await this.db.query(
79
84
  `INSERT INTO payout_x402_batches (
80
85
  scope, batch_id, claimed_native, voucher_count, claims, opened_at,
81
- attempts, first_failed_at, last_error, next_retry_at, updated_at
82
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
86
+ attempts, first_failed_at, last_error, next_retry_at, obligation_ids, updated_at
87
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
83
88
  ON CONFLICT (scope) DO UPDATE SET
84
89
  batch_id = EXCLUDED.batch_id,
85
90
  claimed_native = EXCLUDED.claimed_native,
@@ -90,6 +95,7 @@ var PostgresX402BatchStore = class {
90
95
  first_failed_at = EXCLUDED.first_failed_at,
91
96
  last_error = EXCLUDED.last_error,
92
97
  next_retry_at = EXCLUDED.next_retry_at,
98
+ obligation_ids = EXCLUDED.obligation_ids,
93
99
  updated_at = EXCLUDED.updated_at`,
94
100
  [
95
101
  batch.scope,
@@ -102,6 +108,7 @@ var PostgresX402BatchStore = class {
102
108
  batch.firstFailedAt,
103
109
  batch.lastError,
104
110
  batch.nextRetryAt,
111
+ JSON.stringify(batch.obligationIds ?? []),
105
112
  Date.now()
106
113
  ]
107
114
  );
@@ -142,6 +149,7 @@ var PayoutReporter = class {
142
149
  ownsBatchStore;
143
150
  intervalMs;
144
151
  now;
152
+ hostedObligations;
145
153
  lastX402SnapshotAt = 0;
146
154
  x402;
147
155
  tempo;
@@ -156,6 +164,7 @@ var PayoutReporter = class {
156
164
  this.batches = opts.batchStore ?? (opts.db ? new PostgresX402BatchStore(opts.db) : new MemoryX402BatchStore());
157
165
  this.intervalMs = opts.snapshotIntervalMs ?? PAYOUT_SNAPSHOT_INTERVAL_MS;
158
166
  this.now = opts.now ?? Date.now;
167
+ this.hostedObligations = opts.hostedObligations;
159
168
  }
160
169
  async init() {
161
170
  if (this.ownsBatchStore && this.batches instanceof PostgresX402BatchStore) {
@@ -171,10 +180,12 @@ var PayoutReporter = class {
171
180
  if (this.timer) return;
172
181
  this.warmup = setTimeout(() => {
173
182
  void this.snapshot();
183
+ void this.reconcileHosted();
174
184
  }, WARMUP_MS);
175
185
  this.warmup.unref();
176
186
  this.timer = setInterval(() => {
177
187
  void this.snapshot();
188
+ void this.reconcileHosted();
178
189
  }, this.intervalMs);
179
190
  this.timer.unref();
180
191
  }
@@ -184,6 +195,45 @@ var PayoutReporter = class {
184
195
  if (this.timer) clearInterval(this.timer);
185
196
  this.timer = null;
186
197
  }
198
+ async reconcileHosted() {
199
+ if (!this.hostedObligations) return;
200
+ try {
201
+ for (const failure of await this.hostedObligations.recoverOriginalX402Refunds()) {
202
+ warn("hosted_refund_recovery_pending", { id: failure.id, error: message(failure.error) });
203
+ }
204
+ for (const candidate of await this.hostedObligations.recoveryCandidates()) {
205
+ if (candidate.blockHash) continue;
206
+ try {
207
+ await this.hostedObligations.observeCanonical(candidate.id);
208
+ } catch (error) {
209
+ warn("hosted_effect_observation_pending", { id: candidate.id, error: message(error) });
210
+ }
211
+ }
212
+ for (const failure of await this.hostedObligations.reconcileX402Batches()) {
213
+ warn("hosted_batch_recovery_pending", { id: failure.id, error: message(failure.error) });
214
+ }
215
+ for (const candidate of await this.hostedObligations.recoveryCandidates()) {
216
+ if (!candidate.blockHash || candidate.reportId) continue;
217
+ try {
218
+ if (candidate.rail === "tempo" && (candidate.operation === "settle" || candidate.operation === "close") && candidate.deltaNative !== null && BigInt(candidate.deltaNative) > 0n) {
219
+ await this.tempoSettled({
220
+ txHash: candidate.txHash,
221
+ channelId: candidate.channelId,
222
+ trigger: candidate.operation,
223
+ amount: BigInt(candidate.deltaNative),
224
+ delta: BigInt(candidate.deltaNative)
225
+ });
226
+ } else if (candidate.rail === "x402" && candidate.operation === "settle" && this.x402) {
227
+ await this.recordX402Settle(this.x402, candidate.txHash);
228
+ }
229
+ } catch (error) {
230
+ warn("hosted_effect_report_pending", { id: candidate.id, error: message(error) });
231
+ }
232
+ }
233
+ } catch (error) {
234
+ warn("hosted_effect_recovery_pending", { error: message(error) });
235
+ }
236
+ }
187
237
  enqueuePayout(payload, tx) {
188
238
  return this.transport.enqueuePayout(payload, tx);
189
239
  }
@@ -263,8 +313,24 @@ var PayoutReporter = class {
263
313
  */
264
314
  async tempoSettled(event) {
265
315
  try {
316
+ let hostedIds;
317
+ if (this.hostedObligations) {
318
+ hostedIds = await this.hostedObligations.submittedIds(
319
+ "tempo",
320
+ event.trigger === "close" ? "close" : "settle",
321
+ event.txHash
322
+ );
323
+ if (hostedIds.length === 1) {
324
+ const observed = await this.hostedObligations.observeCanonical(hostedIds[0]);
325
+ if (BigInt(observed.deltaNative) !== event.delta) {
326
+ throw new Error("Hosted Tempo callback differs from finalized settlement amount");
327
+ }
328
+ } else if (await this.hostedObligations.hasCompleteHistory()) {
329
+ throw new Error("Hosted Tempo payout has no exact original settlement obligation");
330
+ }
331
+ }
266
332
  if (event.delta <= 0n) return;
267
- await this.reportPayout({
333
+ const payload = {
268
334
  dvmId: this.dvmId,
269
335
  payoutId: `tempo:${event.txHash}`,
270
336
  rail: "tempo",
@@ -279,9 +345,23 @@ var PayoutReporter = class {
279
345
  ...this.tempo?.network ? { network: this.tempo.network } : {}
280
346
  },
281
347
  landedAt: this.now()
282
- });
348
+ };
349
+ if (this.hostedObligations) {
350
+ if (hostedIds?.length !== 1 || !this.transport.enqueueHostedPayout) {
351
+ if (await this.hostedObligations.hasCompleteHistory()) {
352
+ throw new Error("Hosted Tempo payout has no exact original settlement obligation");
353
+ }
354
+ await this.reportPayout(payload);
355
+ } else {
356
+ await this.transport.enqueueHostedPayout(payload, hostedIds);
357
+ void this.flush();
358
+ }
359
+ } else {
360
+ await this.reportPayout(payload);
361
+ }
283
362
  } catch (err) {
284
363
  warn("payout_report_failed", { rail: "tempo", kind: event.trigger, error: message(err) });
364
+ if (this.hostedObligations) throw err;
285
365
  }
286
366
  void this.snapshotTempo();
287
367
  }
@@ -300,29 +380,49 @@ var PayoutReporter = class {
300
380
  * this does not report the caller refund.
301
381
  */
302
382
  async tempoCloseReconciled(event, tx) {
303
- const settled = BigInt(event.settledToPayee);
383
+ let settled = BigInt(event.settledToPayee);
384
+ let hostedIds;
385
+ if (this.hostedObligations) {
386
+ hostedIds = await this.hostedObligations.submittedIds("tempo", "close", event.txHash);
387
+ if (hostedIds.length === 1) {
388
+ const alreadySettled = await this.hostedObligations.originalTempoCloseSettled(
389
+ hostedIds[0],
390
+ event.channelId,
391
+ event.txHash
392
+ );
393
+ settled -= alreadySettled;
394
+ if (settled < 0n) throw new Error("Hosted Tempo repair precedes its original settled amount");
395
+ } else if (await this.hostedObligations.hasCompleteHistory(tx)) {
396
+ throw new Error("Hosted Tempo repair has no original close obligation");
397
+ }
398
+ }
304
399
  if (settled <= 0n) return;
305
- await this.enqueuePayout(
306
- {
307
- dvmId: this.dvmId,
308
- payoutId: `tempo:${event.txHash}`,
309
- rail: "tempo",
310
- kind: "close",
311
- nativeAmount: Number(settled),
312
- nativeAsset: "usdc",
313
- recipient: this.tempo?.recipient ?? null,
314
- refs: {
315
- channelId: event.channelId,
316
- txHash: event.txHash,
317
- trigger: "close",
318
- ...this.tempo?.network ? { network: this.tempo.network } : {}
319
- },
320
- landedAt: this.now(),
321
- repaired: true,
322
- repairKind: "tempo_close_reconciled"
400
+ const payload = {
401
+ dvmId: this.dvmId,
402
+ payoutId: `tempo:${event.txHash}`,
403
+ rail: "tempo",
404
+ kind: "close",
405
+ nativeAmount: Number(settled),
406
+ nativeAsset: "usdc",
407
+ recipient: this.tempo?.recipient ?? null,
408
+ refs: {
409
+ channelId: event.channelId,
410
+ txHash: event.txHash,
411
+ trigger: "close",
412
+ ...this.tempo?.network ? { network: this.tempo.network } : {}
323
413
  },
324
- tx
325
- );
414
+ landedAt: this.now(),
415
+ repaired: true,
416
+ repairKind: "tempo_close_reconciled"
417
+ };
418
+ if (hostedIds?.length === 1) {
419
+ if (!this.transport.enqueueHostedPayout) {
420
+ throw new Error("Hosted Tempo repair lacks its durable report queue");
421
+ }
422
+ await this.transport.enqueueHostedPayout(payload, hostedIds, tx);
423
+ } else {
424
+ await this.enqueuePayout(payload, tx);
425
+ }
326
426
  if (!tx) {
327
427
  void this.flush();
328
428
  void this.snapshotTempo();
@@ -343,6 +443,17 @@ var PayoutReporter = class {
343
443
  const ctx = this.x402;
344
444
  if (ctx) await this.recordX402SettleFailure(ctx, error);
345
445
  void this.snapshotX402();
446
+ },
447
+ recordRefundClaim: async (transaction, delta) => {
448
+ const ctx = this.x402;
449
+ if (!ctx) throw new Error("Hosted x402 refund has no payout context");
450
+ await this.recordX402Claim(ctx, {
451
+ delta,
452
+ vouchers: 1,
453
+ transactions: [transaction],
454
+ operation: "refund"
455
+ });
456
+ void this.snapshotX402();
346
457
  }
347
458
  };
348
459
  }
@@ -370,7 +481,8 @@ var PayoutReporter = class {
370
481
  }
371
482
  await this.recordX402Claim(ctx, {
372
483
  delta,
373
- vouchers: results.reduce((sum, r) => sum + r.vouchers, 0)
484
+ vouchers: results.reduce((sum, r) => sum + r.vouchers, 0),
485
+ transactions: results.map((result) => result.transaction)
374
486
  });
375
487
  void this.snapshotX402();
376
488
  }
@@ -389,10 +501,32 @@ var PayoutReporter = class {
389
501
  throw err;
390
502
  }
391
503
  };
504
+ const refund = async (channelIds) => {
505
+ const ctx = this.x402;
506
+ const before = ctx ? await claimedByChannel(ctx.storage) : /* @__PURE__ */ new Map();
507
+ const results = await manager.refund(channelIds);
508
+ if (ctx && this.hostedObligations) {
509
+ for (const result of results) {
510
+ const original = before.get(result.channel.toLowerCase());
511
+ if (!original) throw new Error("Hosted x402 refund lost its original channel snapshot");
512
+ const delta = original.charged - original.claimed;
513
+ if (delta > 0n) {
514
+ await this.recordX402Claim(ctx, {
515
+ delta,
516
+ vouchers: 1,
517
+ transactions: [result.transaction],
518
+ operation: "refund"
519
+ });
520
+ }
521
+ }
522
+ }
523
+ return results;
524
+ };
392
525
  return new Proxy(manager, {
393
526
  get(target, prop, receiver) {
394
527
  if (prop === "claim") return claim;
395
528
  if (prop === "settle") return settle;
529
+ if (prop === "refund" && target.refund) return refund;
396
530
  const value = Reflect.get(target, prop, receiver);
397
531
  return typeof value === "function" ? value.bind(target) : value;
398
532
  }
@@ -401,7 +535,11 @@ var PayoutReporter = class {
401
535
  async recordX402Claim(ctx, claim) {
402
536
  try {
403
537
  const now = this.now();
404
- const open = await this.batches.get(ctx.scope) ?? {
538
+ const existing = await this.batches.get(ctx.scope);
539
+ if (this.hostedObligations && !existing) {
540
+ throw new Error("Hosted x402 claim lost its pre-effect payout batch");
541
+ }
542
+ const open = existing ?? {
405
543
  scope: ctx.scope,
406
544
  batchId: randomUUID(),
407
545
  claimedNative: 0n,
@@ -416,9 +554,22 @@ var PayoutReporter = class {
416
554
  open.claimedNative += claim.delta;
417
555
  open.voucherCount += claim.vouchers;
418
556
  open.claims += 1;
557
+ if (this.hostedObligations) {
558
+ const ids = (await Promise.all(claim.transactions.map((txHash) => this.hostedObligations.submittedIds(
559
+ "x402",
560
+ claim.operation ?? "claim",
561
+ txHash,
562
+ open.batchId
563
+ )))).flat();
564
+ if (!ids.length || ids.length < claim.vouchers || new Set(ids).size !== ids.length) {
565
+ throw new Error("Hosted x402 claim has no exact reserved voucher set");
566
+ }
567
+ open.obligationIds = [.../* @__PURE__ */ new Set([...open.obligationIds ?? [], ...ids])];
568
+ }
419
569
  await this.batches.put(open);
420
570
  } catch (err) {
421
571
  warn("payout_batch_track_failed", { scope: ctx.scope, error: message(err) });
572
+ if (this.hostedObligations) throw err;
422
573
  }
423
574
  }
424
575
  async recordX402Settle(ctx, transaction) {
@@ -433,6 +584,23 @@ var PayoutReporter = class {
433
584
  warn("payout_batch_empty", { scope: ctx.scope, transaction, batchId: open.batchId });
434
585
  return;
435
586
  }
587
+ if (this.hostedObligations) {
588
+ if (!open.obligationIds?.length) {
589
+ throw new Error("Hosted x402 payout has no original claim identities");
590
+ }
591
+ const settleId = await this.hostedObligations.settlementId(open.batchId, transaction);
592
+ const settled = await this.hostedObligations.observeCanonical(settleId);
593
+ if (BigInt(settled.deltaNative) !== open.claimedNative) {
594
+ throw new Error("Hosted x402 settled amount differs from its original batch");
595
+ }
596
+ let claimed = 0n;
597
+ for (const id of open.obligationIds) {
598
+ claimed += BigInt((await this.hostedObligations.observeCanonical(id)).deltaNative);
599
+ }
600
+ if (claimed !== open.claimedNative) {
601
+ throw new Error("Hosted x402 batch amount differs from finalized original claims");
602
+ }
603
+ }
436
604
  const payload = {
437
605
  dvmId: this.dvmId,
438
606
  payoutId: `x402:batch:${open.batchId}`,
@@ -450,17 +618,25 @@ var PayoutReporter = class {
450
618
  },
451
619
  landedAt: this.now()
452
620
  };
453
- const closed = await this.batches.settle(
454
- ctx.scope,
455
- open.batchId,
456
- (tx) => this.enqueuePayout(payload, tx)
457
- );
621
+ const closed = await this.batches.settle(ctx.scope, open.batchId, async (tx) => {
622
+ if (!this.hostedObligations) return this.enqueuePayout(payload, tx);
623
+ if (!this.transport.enqueueHostedPayout || !open.obligationIds?.length) {
624
+ throw new Error("Hosted x402 payout lacks its original claims or durable queue");
625
+ }
626
+ const settleId = await this.hostedObligations.settlementId(open.batchId, transaction);
627
+ await this.transport.enqueueHostedPayout(
628
+ payload,
629
+ [...open.obligationIds, settleId],
630
+ tx
631
+ );
632
+ });
458
633
  if (!closed) {
459
634
  warn("payout_batch_raced", { scope: ctx.scope, transaction, batchId: open.batchId });
460
635
  return;
461
636
  }
462
637
  } catch (err) {
463
638
  warn("payout_report_failed", { rail: "x402", kind: "batch", error: message(err) });
639
+ if (this.hostedObligations) throw err;
464
640
  return;
465
641
  }
466
642
  void this.flush();
@@ -1,106 +1,11 @@
1
1
  import {
2
- DvmError
3
- } from "./chunk-MKI6OVW4.js";
2
+ x402NetworkByCaip2,
3
+ x402NetworkBySlug
4
+ } from "./chunk-RLTSCKRI.js";
4
5
 
5
6
  // src/lib/x402/constants.ts
6
7
  var X402_DEFAULT_FACILITATOR = "https://x402.org/facilitator";
7
8
 
8
- // src/lib/x402/networks.ts
9
- var X402_NETWORKS = [
10
- {
11
- caip2: "eip155:8453",
12
- slug: "base",
13
- label: "Base",
14
- usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
15
- domainName: "USD Coin",
16
- domainVersion: "2",
17
- viemChain: "base",
18
- nativeGas: "ETH",
19
- confusable: "USDbC"
20
- },
21
- {
22
- caip2: "eip155:84532",
23
- slug: "base-sepolia",
24
- label: "Base Sepolia",
25
- usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
26
- domainName: "USDC",
27
- domainVersion: "2",
28
- viemChain: "baseSepolia",
29
- nativeGas: "ETH",
30
- confusable: null
31
- },
32
- {
33
- caip2: "eip155:43114",
34
- slug: "avalanche",
35
- label: "Avalanche",
36
- usdc: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
37
- domainName: "USD Coin",
38
- domainVersion: "2",
39
- viemChain: "avalanche",
40
- nativeGas: "AVAX",
41
- confusable: "USDC.e"
42
- },
43
- {
44
- caip2: "eip155:43113",
45
- slug: "avalanche-fuji",
46
- label: "Avalanche Fuji",
47
- usdc: "0x5425890298aed601595a70AB815c96711a31Bc65",
48
- domainName: "USD Coin",
49
- domainVersion: "2",
50
- viemChain: "avalancheFuji",
51
- nativeGas: "AVAX",
52
- confusable: null
53
- },
54
- {
55
- caip2: "eip155:137",
56
- slug: "polygon",
57
- label: "Polygon",
58
- usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
59
- domainName: "USD Coin (PoS)",
60
- domainVersion: "2",
61
- viemChain: "polygon",
62
- nativeGas: "POL",
63
- confusable: "USDC.e"
64
- },
65
- {
66
- caip2: "eip155:80002",
67
- slug: "polygon-amoy",
68
- label: "Polygon Amoy",
69
- usdc: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
70
- domainName: "USDC",
71
- domainVersion: "2",
72
- viemChain: "polygonAmoy",
73
- nativeGas: "POL",
74
- confusable: null
75
- }
76
- ];
77
- function x402NetworkByCaip2(caip2) {
78
- return BY_CAIP2.get(caip2);
79
- }
80
- function x402NetworkBySlug(slug) {
81
- return BY_SLUG.get(slug);
82
- }
83
- function x402NetworkLabel(network) {
84
- const spec = x402NetworkByCaip2(network);
85
- return spec ? `${spec.label} (${network})` : network;
86
- }
87
- function x402NetworkListForCopy() {
88
- return X402_NETWORKS.map((n) => `${n.caip2} (${n.label})`).join(", ");
89
- }
90
- function assertSupportedX402Network(network) {
91
- if (x402NetworkByCaip2(network)) return;
92
- throw new DvmError(
93
- "unsupported_network",
94
- `Unsupported x402 network: ${network}.`,
95
- `${x402SupportedNetworksHint()} If a wallet is connected, run 'dvm wallet x402-network <caip2>'. For a first connection, pass --network to 'dvm wallet x402-connect'.`
96
- );
97
- }
98
- function x402SupportedNetworksHint() {
99
- return `This rail settles native USDC on: ${x402NetworkListForCopy()}.`;
100
- }
101
- var BY_CAIP2 = new Map(X402_NETWORKS.map((n) => [n.caip2, n]));
102
- var BY_SLUG = new Map(X402_NETWORKS.map((n) => [n.slug, n]));
103
-
104
9
  // src/lib/x402/spec.ts
105
10
  var X402_VERSION = 2;
106
11
  var X402_V1_VERSION = 1;
@@ -301,13 +206,6 @@ function isOptionalRecord(value) {
301
206
 
302
207
  export {
303
208
  X402_DEFAULT_FACILITATOR,
304
- X402_NETWORKS,
305
- x402NetworkByCaip2,
306
- x402NetworkBySlug,
307
- x402NetworkLabel,
308
- x402NetworkListForCopy,
309
- assertSupportedX402Network,
310
- x402SupportedNetworksHint,
311
209
  X402_VERSION,
312
210
  X402_V1_VERSION,
313
211
  X402_DEFAULT_NETWORK,