@dvmkit/sdk 0.1.4-rc.7 → 0.1.5-rc.8

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 (31) hide show
  1. package/README.md +10 -2
  2. package/dist/{chunk-ANFX5HEG.js → chunk-2UUXIIOC.js} +43 -13
  3. package/dist/{chunk-C6JHBLMW.js → chunk-BIP6G74V.js} +1 -1
  4. package/dist/{chunk-EDDYHZ6W.js → chunk-CEOAHV2I.js} +0 -30
  5. package/dist/{chunk-TSWKITGR.js → chunk-CGKZDODG.js} +8 -1
  6. package/dist/{chunk-DBCLBYHP.js → chunk-KVEHHC7W.js} +11 -1
  7. package/dist/{chunk-VRQDX5P4.js → chunk-KXCKFIJI.js} +30 -0
  8. package/dist/{chunk-5GFED3GJ.js → chunk-M7LHFJ5K.js} +49 -6
  9. package/dist/{chunk-7AKBC4PW.js → chunk-TQWGQCNV.js} +1 -1
  10. package/dist/{chunk-QK3VJNCK.js → chunk-UB5FZ43T.js} +339 -212
  11. package/dist/{credit-menu-C1ezIFlJ.d.ts → credit-menu-s5HmGCqx.d.ts} +64 -21
  12. package/dist/{fx-C6dl2LVI.d.ts → fx-D860pZvP.d.ts} +1 -1
  13. package/dist/index.d.ts +5 -5
  14. package/dist/internal/caller.d.ts +31 -11
  15. package/dist/internal/caller.js +23 -11
  16. package/dist/internal/index.js +1 -1
  17. package/dist/internal/server.d.ts +8 -8
  18. package/dist/internal/server.js +4 -4
  19. package/dist/{job-store-DHnW4Cg_.d.ts → job-store-BUGqvCfL.d.ts} +1 -1
  20. package/dist/{lightning-backend-Ci1nogk_.d.ts → lightning-backend-BozcevPZ.d.ts} +1 -1
  21. package/dist/{memory-credit-ledger-OP24Z2KO.js → memory-credit-ledger-MNUOTQO5.js} +1 -1
  22. package/dist/{mpp-setup-MOBWGTWJ.js → mpp-setup-4FJD6ZHV.js} +11 -1
  23. package/dist/server/index.d.ts +12 -13
  24. package/dist/server/index.js +215 -51
  25. package/dist/{step-cache-BLPZNizw.d.ts → step-cache-5dljDqrQ.d.ts} +4 -2
  26. package/dist/{tempo-lifecycle-SQL3KLEZ.js → tempo-lifecycle-DFIXQ54Q.js} +3 -3
  27. package/dist/{tempo-wallet-QOLEIPCV.js → tempo-wallet-4QKSV65O.js} +10 -2
  28. package/dist/testing/index.d.ts +2 -2
  29. package/dist/{usd-BgOfZlk6.d.ts → usd-DoRuAckA.d.ts} +1 -1
  30. package/dist/{x402-T2C5MX3T.js → x402-5H27DCBE.js} +2 -2
  31. package/package.json +1 -1
package/README.md CHANGED
@@ -40,7 +40,7 @@ import dvm from "./handler.mjs";
40
40
  await serve(dvm, { devMode: true });
41
41
  ```
42
42
 
43
- Submit a local request. `devMode` makes Postgres optional and skips payment verification when no Cashu mint is configured:
43
+ Submit a local request. `devMode` makes Postgres optional and skips payment verification only when no Cashu, x402, or Tempo rail is configured:
44
44
 
45
45
  ```sh
46
46
  curl -sS http://localhost:8080/v1/job \
@@ -76,6 +76,14 @@ Run that file with `node --test handler.test.mjs`. The repository carries the co
76
76
 
77
77
  The generated [API reference](./docs/API.md) lists every declaration under these three entries. Deep imports from `dist/` are unsupported.
78
78
 
79
+ ## Verified payments in development
80
+
81
+ `serve()` returns a `paymentModes` snapshot describing whether each configured rail is verifying, unavailable, or absent. A priced development server skips verification only when it has no configured payment rail. Configuring an unusable rail fails closed instead of turning the request into a synthetic development payment.
82
+
83
+ Cashu development without Postgres is limited to known test or loopback mints. It uses a fresh lock identity and process-local accounting on every boot, so proofs, balances, and jobs do not survive restart. Production Cashu remains PostgreSQL-backed. Both paths require valid mint-provided NUT-12 DLEQ evidence on every proof before accepting its value; older tokens without that evidence must be replaced with fresh tokens from a compatible mint.
84
+
85
+ Builders can pass `onPaidJobCompleted` to observe settled customer charges after successful completion. The event contains `jobId`, verified rail, checked integer `amountMicro`, the credit currency, and whether configuration identifies test funds. Reconciliation can repeat an event, so consumers deduplicate by `jobId`. Free jobs, synthetic development payments, failed jobs, and released draws do not emit.
86
+
79
87
  ## Production next steps
80
88
 
81
89
  - Use Postgres through `DATABASE_URL`; production boot refuses an in-memory job store unless you deliberately supply another durable implementation.
@@ -88,7 +96,7 @@ The public [SDK guide](https://dvmkit.com/docs/sdk) covers signed requests, cust
88
96
 
89
97
  ## Repository development
90
98
 
91
- Use the Node.js release in `.nvmrc` (Node 22). Install with `npm ci`, then run the same gate CI runs with `npm run verify`: the toolchain check, lint, build, typecheck (including the release scripts under `checkJs`), unit and database tests, the public API surface and package-shape checks, a consumer install, a public-safety scan, the release contract, and a clean-install smoke.
99
+ Use the Node.js release in `.nvmrc` (Node 22). Install with `npm ci`, then run the same gate CI runs with `npm run verify`. The gate checks repository metadata in the checkout, copies only tracked and unignored source into an isolated directory, installs its locked dependencies, and runs lint, build, typecheck (including the release scripts under `checkJs`), unit and database tests, API, package, consumer, packed-artifact safety, and release checks there. The clean source tree owns the complete build and test pass, so verification never depends on the checkout's `node_modules` or pre-existing `dist` and does not repeat the full suite; after every check passes, its verified `dist` replaces the checkout's build output for release packaging.
92
100
 
93
101
  `DATABASE_URL` is optional locally. Without it the database suite is skipped rather than passed, and `verify` says so in its summary, because a skipped suite verified nothing and must not be reported as green. Point `DATABASE_URL` at a scratch Postgres to run it, and pass `npm run verify -- --require-database` to make a missing one a failure, which is what CI does.
94
102
 
@@ -2,7 +2,7 @@ import {
2
2
  isValidEvmPrivateKey,
3
3
  loadConfig,
4
4
  updateConfig
5
- } from "./chunk-TSWKITGR.js";
5
+ } from "./chunk-CGKZDODG.js";
6
6
  import {
7
7
  DvmError,
8
8
  resolveRpcHttpTransportOptions,
@@ -16,8 +16,24 @@ import {
16
16
  // src/lib/payment-rails/tempo-wallet.ts
17
17
  var TEMPO_USDC_MAINNET = "0x20C000000000000000000000b9537d11c60E8b50";
18
18
  var TEMPO_CHAIN_ID = 4217;
19
- function resolveTempoAsset(env = process.env) {
20
- const address = env.DVM_TEMPO_CURRENCY ?? TEMPO_USDC_MAINNET;
19
+ var TEMPO_MODERATO_CHAIN_ID = 42431;
20
+ var TEMPO_PATH_USD_MODERATO = "0x20c0000000000000000000000000000000000000";
21
+ var TEMPO_MODERATO_RPC = "https://rpc.moderato.tempo.xyz";
22
+ function resolveTempoNetwork(network = "mainnet") {
23
+ return network === "moderato" ? {
24
+ network,
25
+ chainId: TEMPO_MODERATO_CHAIN_ID,
26
+ rpcUrl: TEMPO_MODERATO_RPC,
27
+ defaultToken: TEMPO_PATH_USD_MODERATO
28
+ } : {
29
+ network,
30
+ chainId: TEMPO_CHAIN_ID,
31
+ rpcUrl: "https://rpc.tempo.xyz",
32
+ defaultToken: TEMPO_USDC_MAINNET
33
+ };
34
+ }
35
+ function resolveTempoAsset(env = process.env, network = "mainnet") {
36
+ const address = env.DVM_TEMPO_CURRENCY ?? resolveTempoNetwork(network).defaultToken;
21
37
  if (!HEX_ADDRESS_RE.test(address)) {
22
38
  throw new DvmError(
23
39
  "tempo_config_invalid",
@@ -58,7 +74,9 @@ function tempoWrongAssetHint(asset) {
58
74
  return `This rail settles only in ${asset.label} (${asset.address}) on Tempo. Another Tempo dollar \u2014 USDT0 or any other stablecoin \u2014 is a different contract and will never show up here, however much of it your wallet says you hold. If you funded with the wrong one, swap it to ${asset.label} on Tempo (an on-chain swap, fees are sub-cent) or send ${asset.label} to this address.`;
59
75
  }
60
76
  function tempoTokenSymbol(address) {
61
- return address.toLowerCase() === TEMPO_USDC_MAINNET.toLowerCase() ? "USDC.e" : null;
77
+ if (address.toLowerCase() === TEMPO_USDC_MAINNET.toLowerCase()) return "USDC.e";
78
+ if (address.toLowerCase() === TEMPO_PATH_USD_MODERATO.toLowerCase()) return "pathUSD";
79
+ return null;
62
80
  }
63
81
  var HEX_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
64
82
  async function connectedTempoAccount() {
@@ -83,7 +101,7 @@ async function generateTempoKey(opts = {}) {
83
101
  );
84
102
  }
85
103
  if (existing && opts.force) {
86
- const { assertTempoWalletIdle } = await import("./tempo-lifecycle-SQL3KLEZ.js");
104
+ const { assertTempoWalletIdle } = await import("./tempo-lifecycle-DFIXQ54Q.js");
87
105
  await assertTempoWalletIdle("replace");
88
106
  }
89
107
  const { privateKeyToAccount, generatePrivateKey } = await import("viem/accounts");
@@ -91,22 +109,30 @@ async function generateTempoKey(opts = {}) {
91
109
  const account = privateKeyToAccount(privateKey);
92
110
  const generated = {
93
111
  privateKey,
94
- address: account.address.toLowerCase()
112
+ address: account.address.toLowerCase(),
113
+ network: opts.network ?? "mainnet"
95
114
  };
96
115
  await opts.beforePersist?.(generated);
97
116
  await updateConfig({
98
117
  reason: "tempo_wallet_generate",
99
- patch: () => ({ set: { tempo: { method: "tempo", apiKey: privateKey } } })
118
+ patch: () => ({
119
+ set: {
120
+ tempo: { method: "tempo", apiKey: privateKey, network: generated.network }
121
+ }
122
+ })
100
123
  });
101
124
  return generated;
102
125
  }
103
126
  async function getTempoUsdcBalance(account) {
104
- const tokenAddress = resolveTempoAsset().address;
127
+ const accountNetwork = account.network ?? "mainnet";
128
+ const network = resolveTempoNetwork(accountNetwork);
129
+ const tokenAddress = resolveTempoAsset(process.env, accountNetwork).address;
105
130
  const { createPublicClient, http, erc20Abi } = await import("viem");
106
- const { tempo } = await import("viem/chains");
131
+ const { tempo, tempoModerato } = await import("viem/chains");
132
+ const chain = accountNetwork === "moderato" ? tempoModerato : tempo;
107
133
  const { url: rpcUrl, source: rpcSource } = resolveRpcOverride(
108
134
  process.env.DVM_TEMPO_RPC_URL,
109
- tempo.rpcUrls.default.http[0]
135
+ network.rpcUrl
110
136
  );
111
137
  try {
112
138
  const transportOptions = resolveRpcHttpTransportOptions(
@@ -114,7 +140,7 @@ async function getTempoUsdcBalance(account) {
114
140
  process.env.DVM_TEMPO_RPC_HEADERS_JSON
115
141
  );
116
142
  const client = createPublicClient({
117
- chain: tempo,
143
+ chain,
118
144
  transport: transportOptions ? http(rpcUrl, transportOptions) : http(rpcUrl)
119
145
  });
120
146
  const raw = await client.readContract({
@@ -128,7 +154,7 @@ async function getTempoUsdcBalance(account) {
128
154
  rawBaseUnits: raw,
129
155
  usdc: Number(raw) / 1e6,
130
156
  tokenAddress,
131
- chainId: TEMPO_CHAIN_ID,
157
+ chainId: network.chainId,
132
158
  // internal-review: redacted here rather than at a display surface, so the clear
133
159
  // endpoint never leaves this function on the success path either.
134
160
  rpcUrlRedacted: redactUrl(rpcUrl),
@@ -160,7 +186,7 @@ async function tempoAccountFromConfig(config) {
160
186
  if (!apiKey || !isValidEvmPrivateKey(apiKey)) return null;
161
187
  const privateKey = apiKey;
162
188
  const address = await deriveTempoAddress(privateKey);
163
- return { privateKey, address };
189
+ return { privateKey, address, network: config.tempo.network ?? "mainnet" };
164
190
  }
165
191
  function invalidTempoPrivateKey() {
166
192
  return new DvmError(
@@ -173,6 +199,10 @@ function invalidTempoPrivateKey() {
173
199
  export {
174
200
  TEMPO_USDC_MAINNET,
175
201
  TEMPO_CHAIN_ID,
202
+ TEMPO_MODERATO_CHAIN_ID,
203
+ TEMPO_PATH_USD_MODERATO,
204
+ TEMPO_MODERATO_RPC,
205
+ resolveTempoNetwork,
176
206
  resolveTempoAsset,
177
207
  tempoTokenLabel,
178
208
  tempoTokenReference,
@@ -10,7 +10,7 @@ import {
10
10
  isValidEvmPrivateKey,
11
11
  loadConfig,
12
12
  updateConfig
13
- } from "./chunk-TSWKITGR.js";
13
+ } from "./chunk-CGKZDODG.js";
14
14
  import {
15
15
  X402_DEFAULT_NETWORK,
16
16
  X402_V1_VERSION,
@@ -2,34 +2,6 @@ import {
2
2
  callerLoggers
3
3
  } from "./chunk-66HGCPBU.js";
4
4
 
5
- // src/lib/mints.ts
6
- var RECOMMENDED_MINTS = [
7
- {
8
- name: "Coinos",
9
- description: "Primary Cashu mint accepted by dvmkit DVMs",
10
- url: "https://mint.coinos.io"
11
- },
12
- {
13
- name: "Voltz Mint",
14
- description: "Fallback Cashu mint accepted by dvmkit DVMs",
15
- url: "https://mint.lnvoltz.com"
16
- }
17
- ];
18
- function isTestMintUrl(url) {
19
- let host;
20
- try {
21
- host = new URL(url).hostname.toLowerCase();
22
- } catch {
23
- return false;
24
- }
25
- if (host.startsWith("[") && host.endsWith("]")) host = host.slice(1, -1);
26
- if (LOOPBACK_HOSTS.has(host)) return true;
27
- if (host.endsWith(".localhost")) return true;
28
- return TEST_MINT_HOSTS.has(host);
29
- }
30
- var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]);
31
- var TEST_MINT_HOSTS = /* @__PURE__ */ new Set(["testnut.cashu.space"]);
32
-
33
5
  // src/lib/x402/env.ts
34
6
  import { isAddress } from "viem";
35
7
  import { privateKeyToAccount } from "viem/accounts";
@@ -981,8 +953,6 @@ var NwcBackend = class {
981
953
  };
982
954
 
983
955
  export {
984
- RECOMMENDED_MINTS,
985
- isTestMintUrl,
986
956
  X402_SELF_RELAY_KEY,
987
957
  X402_RECEIVER_AUTHORIZER_KEY,
988
958
  X402_BATCH_SETTLEMENT_KEY,
@@ -181,6 +181,13 @@ function assertValidConfig(config, fields) {
181
181
  const value = owner[leaf];
182
182
  if (value === void 0 || !matchesKind(value, kind)) throw invalidFieldError(path, kind);
183
183
  }
184
+ if ((!scope || scope.has("tempo")) && isPlainObject(record.tempo) && record.tempo.network !== void 0 && record.tempo.network !== "mainnet" && record.tempo.network !== "moderato") {
185
+ throw new DvmError(
186
+ "config_invalid",
187
+ `The caller configuration field 'tempo.network' must be 'mainnet' or 'moderato'.`,
188
+ `Correct tempo.network in ${CONFIG_FILE}.${rollbackSuffix()}`
189
+ );
190
+ }
184
191
  }
185
192
  function readRawConfig() {
186
193
  if (!existsSync(CONFIG_FILE)) return null;
@@ -308,7 +315,7 @@ var PROTECTED_FIELDS = {
308
315
  var CONFIG_PATH_SHAPE = {
309
316
  float: { statedBudget: {}, connectedAt: {}, alias: {}, methods: {} },
310
317
  x402: { privateKey: {}, network: {} },
311
- tempo: { method: {}, accountId: {}, apiKey: {} },
318
+ tempo: { method: {}, accountId: {}, apiKey: {}, network: {} },
312
319
  credit: {
313
320
  targetJobs: {},
314
321
  posture: {},
@@ -19,7 +19,7 @@ import {
19
19
 
20
20
  // src/sdk/server/memory-credit-ledger.ts
21
21
  import { randomUUID } from "crypto";
22
- var MemoryCreditLedger = class {
22
+ var MemoryCreditLedger = class _MemoryCreditLedger {
23
23
  /**
24
24
  * Per-process and lost on restart, so a DVM backed by this ledger never
25
25
  * advertises the funding menu outside devMode (internal-review) — funding on one
@@ -32,6 +32,16 @@ var MemoryCreditLedger = class {
32
32
  tempoLosses = /* @__PURE__ */ new Map();
33
33
  x402Losses = /* @__PURE__ */ new Map();
34
34
  x402Settlements;
35
+ /** Internal disposable transaction snapshot. The caller must serialize every ledger access. */
36
+ forkForTransaction() {
37
+ const fork = new _MemoryCreditLedger();
38
+ for (const name of ["credits", "fundings", "invoices", "tempoLosses", "x402Losses"]) {
39
+ const target = fork[name];
40
+ for (const [key, value] of this[name]) target.set(key, structuredClone(value));
41
+ }
42
+ fork.x402Settlements = this.x402Settlements;
43
+ return fork;
44
+ }
35
45
  /** See {@link CreditLedger.useX402SettlementGate} — same contract (internal-review). */
36
46
  useX402SettlementGate(gate) {
37
47
  this.x402Settlements = gate;
@@ -29,6 +29,34 @@ function isX402SettlementReconciliationReason(value) {
29
29
  return value === "settlement_not_on_chain" || value === "chain_unreachable" || value === "settlement_unbookmarked" || value === "facilitator_channel_state_unavailable";
30
30
  }
31
31
 
32
+ // src/lib/mints.ts
33
+ var RECOMMENDED_MINTS = [
34
+ {
35
+ name: "Coinos",
36
+ description: "Primary Cashu mint accepted by dvmkit DVMs",
37
+ url: "https://mint.coinos.io"
38
+ },
39
+ {
40
+ name: "Voltz Mint",
41
+ description: "Fallback Cashu mint accepted by dvmkit DVMs",
42
+ url: "https://mint.lnvoltz.com"
43
+ }
44
+ ];
45
+ function isTestMintUrl(url) {
46
+ let host;
47
+ try {
48
+ host = new URL(url).hostname.toLowerCase();
49
+ } catch {
50
+ return false;
51
+ }
52
+ if (host.startsWith("[") && host.endsWith("]")) host = host.slice(1, -1);
53
+ if (LOOPBACK_HOSTS.has(host)) return true;
54
+ if (host.endsWith(".localhost")) return true;
55
+ return TEST_MINT_HOSTS.has(host);
56
+ }
57
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]);
58
+ var TEST_MINT_HOSTS = /* @__PURE__ */ new Set(["testnut.cashu.space", "dvmkit-testmint.fly.dev"]);
59
+
32
60
  // src/lib/secret-backup.ts
33
61
  import { existsSync, readFileSync, writeFileSync } from "fs";
34
62
  function backupSecretFile(file, nextContent) {
@@ -1666,6 +1694,8 @@ export {
1666
1694
  verifyCashuToken,
1667
1695
  normaliseMintUrl,
1668
1696
  TEMPO_SESSION_UPDATED_REASON,
1697
+ RECOMMENDED_MINTS,
1698
+ isTestMintUrl,
1669
1699
  backupSecretFile,
1670
1700
  readAgentMnemonic,
1671
1701
  assertAgentMnemonicReplaceable,
@@ -313,6 +313,10 @@ var TEMPO_SETTLEMENT_LEASE_OWNER_FIELD = "settlementLeaseOwner";
313
313
  var TEMPO_SETTLEMENT_LEASE_MS = 3e5;
314
314
  var TEMPO_SETTLEMENT_IN_FLIGHT = "settlement already in flight";
315
315
  var TEMPO_USDC_MAINNET = "0x20C000000000000000000000b9537d11c60E8b50";
316
+ var TEMPO_MAINNET_CHAIN_ID = 4217;
317
+ var TEMPO_MODERATO_CHAIN_ID = 42431;
318
+ var TEMPO_PATH_USD_MODERATO = "0x20c0000000000000000000000000000000000000";
319
+ var TEMPO_MODERATO_RPC = "https://rpc.moderato.tempo.xyz";
316
320
  var HEX_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
317
321
  function challengeMeta(challenge) {
318
322
  if (challenge.opaque) {
@@ -417,15 +421,21 @@ function buildMppMethods(opts, env) {
417
421
  `DVMKIT_TEMPO_RECIPIENT must be a 0x-prefixed 40-char hex address; got '${opts.tempoRecipient}'.`
418
422
  );
419
423
  }
420
- const tempoCurrency = env.DVMKIT_TEMPO_CURRENCY ?? TEMPO_USDC_MAINNET;
424
+ const { chainId, currency: tempoCurrency } = resolveTempoNetwork(env);
421
425
  if (!HEX_ADDRESS_RE.test(tempoCurrency)) {
422
426
  throw new Error(
423
427
  `DVMKIT_TEMPO_CURRENCY must be a 0x-prefixed 40-char hex address; got '${tempoCurrency}'.`
424
428
  );
425
429
  }
430
+ if (opts.tempoSession?.chainId !== void 0 && opts.tempoSession.chainId !== chainId) {
431
+ throw new Error(
432
+ `Tempo session chain ${opts.tempoSession.chainId} does not match the configured Tempo chain ${chainId}.`
433
+ );
434
+ }
426
435
  tempoConfiguration = {
427
436
  recipient: opts.tempoRecipient,
428
437
  paymentToken: tempoCurrency,
438
+ chainId,
429
439
  ...opts.tempoSession && {
430
440
  operator: opts.tempoSession.account.address,
431
441
  feeToken: tempoCurrency
@@ -435,6 +445,11 @@ function buildMppMethods(opts, env) {
435
445
  tempo.charge({
436
446
  recipient: opts.tempoRecipient,
437
447
  currency: tempoCurrency,
448
+ // mppx resolves the route's verification requirement from `testnet`.
449
+ // Passing `chainId` only adds a challenge default, so a Moderato
450
+ // challenge can otherwise be re-derived as mainnet when the credential
451
+ // returns and fail before payment verification.
452
+ testnet: chainId === TEMPO_MODERATO_CHAIN_ID,
438
453
  decimals: 6,
439
454
  // Without this mppx installs `Store.memory()`, so the consumed-hash
440
455
  // guard is per-process and a credential replayed onto a sibling machine
@@ -462,9 +477,7 @@ function buildMppMethods(opts, env) {
462
477
  // dvmkit's watcher owns scheduled settlement because it gates on
463
478
  // terminal ledger consumption. mppx's in-request scheduler settles
464
479
  // the full accepted voucher and can capture caller-owned balance.
465
- ...opts.tempoSession.chainId !== void 0 && {
466
- chainId: opts.tempoSession.chainId
467
- },
480
+ chainId: opts.tempoSession.chainId ?? chainId,
468
481
  ...opts.tempoSession.getClient && { getClient: opts.tempoSession.getClient },
469
482
  ...opts.tempoSession.onSessionSettlement && {
470
483
  onSessionSettlement: opts.tempoSession.onSessionSettlement
@@ -476,6 +489,29 @@ function buildMppMethods(opts, env) {
476
489
  if (methods.length === 0 || !tempoConfiguration) return void 0;
477
490
  return { methods, tempoConfiguration };
478
491
  }
492
+ function resolveTempoNetwork(env) {
493
+ const testnet = env.DVMKIT_TEMPO_TESTNET === "1" || env.DVMKIT_TEMPO_TESTNET === "true";
494
+ const rawChainId = env.DVMKIT_TEMPO_CHAIN_ID?.trim();
495
+ const chainId = rawChainId ? Number(rawChainId) : testnet ? TEMPO_MODERATO_CHAIN_ID : TEMPO_MAINNET_CHAIN_ID;
496
+ if (!Number.isSafeInteger(chainId) || chainId <= 0) {
497
+ throw new Error(
498
+ `DVMKIT_TEMPO_CHAIN_ID must be a positive safe integer; got '${env.DVMKIT_TEMPO_CHAIN_ID}'.`
499
+ );
500
+ }
501
+ if (chainId !== TEMPO_MAINNET_CHAIN_ID && chainId !== TEMPO_MODERATO_CHAIN_ID) {
502
+ throw new Error(
503
+ `DVMKIT_TEMPO_CHAIN_ID must be ${TEMPO_MAINNET_CHAIN_ID} (mainnet) or ${TEMPO_MODERATO_CHAIN_ID} (Moderato); got '${chainId}'.`
504
+ );
505
+ }
506
+ const currency = env.DVMKIT_TEMPO_CURRENCY ?? (chainId === TEMPO_MODERATO_CHAIN_ID ? TEMPO_PATH_USD_MODERATO : TEMPO_USDC_MAINNET);
507
+ const normalizedCurrency = currency.toLowerCase();
508
+ if (chainId === TEMPO_MODERATO_CHAIN_ID && normalizedCurrency === TEMPO_USDC_MAINNET.toLowerCase()) {
509
+ throw new Error(
510
+ `DVMKIT_TEMPO_CURRENCY ${currency} is incompatible with DVMKIT_TEMPO_CHAIN_ID ${chainId}.`
511
+ );
512
+ }
513
+ return { chainId, currency, testnet };
514
+ }
479
515
  function isHmacMismatch(err) {
480
516
  if (!(err instanceof MppxErrors.InvalidChallengeError)) return false;
481
517
  return typeof err.message === "string" && err.message.includes(MPPX_HMAC_MISMATCH_REASON);
@@ -491,14 +527,16 @@ function decorateRapidRotation(original, rapidRotationAtMs) {
491
527
  }
492
528
  function wrapMppx(mppx) {
493
529
  const dispatcher = mppx.challenge;
530
+ const server = mppx;
494
531
  const issueChallenge = (method, intent, o) => {
495
532
  const handler = dispatcher[method][intent];
496
533
  if (!handler) {
497
534
  throw new Error(`Mppx: no challenge handler registered for ${method}/${intent}`);
498
535
  }
499
- return handler(o);
536
+ const chainId = method === "tempo" ? server.tempoConfiguration?.chainId : void 0;
537
+ return handler(chainId === void 0 ? o : { chainId, ...o });
500
538
  };
501
- return Object.assign(mppx, { issueChallenge });
539
+ return Object.assign(server, { issueChallenge });
502
540
  }
503
541
  var DEFAULT_TEMPO_SESSION_CHAIN_OPS = {
504
542
  getChannelStatesBatch: TempoSession.Precompile.Chain.getChannelStatesBatch,
@@ -945,9 +983,14 @@ export {
945
983
  TEMPO_SETTLEMENT_LEASE_MS,
946
984
  TEMPO_SETTLEMENT_IN_FLIGHT,
947
985
  TEMPO_USDC_MAINNET,
986
+ TEMPO_MAINNET_CHAIN_ID,
987
+ TEMPO_MODERATO_CHAIN_ID,
988
+ TEMPO_PATH_USD_MODERATO,
989
+ TEMPO_MODERATO_RPC,
948
990
  challengeMeta,
949
991
  createMppFromOpts,
950
992
  createDualKeyMppxFromOpts,
993
+ resolveTempoNetwork,
951
994
  wrapMppx,
952
995
  parseMppMethodsAllowlist,
953
996
  advertisedMethods,
@@ -2,7 +2,7 @@ import {
2
2
  connectedTempoAccount,
3
3
  tempoBalanceCheckHint,
4
4
  tempoTokenReference
5
- } from "./chunk-ANFX5HEG.js";
5
+ } from "./chunk-2UUXIIOC.js";
6
6
  import {
7
7
  fundingReachableAt,
8
8
  listCredits,