@dvmkit/sdk 0.1.0-rc.1 → 0.1.0-rc.3
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 +10 -2
- package/dist/{revenue-reporter-M35KP6V7.js → chunk-2ABMGUDS.js} +78 -2
- package/dist/chunk-5SO7ZVOH.js +375 -0
- package/dist/chunk-66HGCPBU.js +25 -0
- package/dist/{chunk-KQAJVVZT.js → chunk-AZBXSXQT.js} +27 -287
- package/dist/chunk-C2DC4FKR.js +21424 -0
- package/dist/{chunk-H25M54MI.js → chunk-C3MTFLC6.js} +16 -0
- package/dist/{chunk-NTK5DJ6R.js → chunk-EXHBXA4U.js} +11 -1
- package/dist/chunk-FT7IM66W.js +1557 -0
- package/dist/{chunk-KXWROQGK.js → chunk-FUJ36YDV.js} +1 -24
- package/dist/{tempo-charge-store-6GJEMNUU.js → chunk-JZWELPFH.js} +1 -0
- package/dist/{chunk-RPXHKMYE.js → chunk-LWUR4CGG.js} +348 -34
- package/dist/chunk-RHP3BRTH.js +1090 -0
- package/dist/{tempo-session-store-FTEEGZXA.js → chunk-RU7SXHLO.js} +2 -1
- package/dist/{chunk-7IH5SG2A.js → chunk-TKA6ZP4M.js} +62 -41
- package/dist/chunk-TVI4V7GF.js +283 -0
- package/dist/chunk-X3IKFWJA.js +754 -0
- package/dist/{chunk-DCNT4PJS.js → chunk-XY5Y5REG.js} +6 -258
- package/dist/chunk-XYTSDAPH.js +232 -0
- package/dist/chunk-YD3TZNXV.js +1042 -0
- package/dist/{credit-ledger-EDMEZSA2.js → credit-ledger-ED6JXKVD.js} +2 -2
- package/dist/credit-menu-DONAtGVf.d.ts +5076 -0
- package/dist/{ssrf-BdHsrrIb.d.ts → fx-Bq4cvn16.d.ts} +37 -119
- package/dist/index.d.ts +8 -66
- package/dist/index.js +11 -219
- package/dist/internal/index.d.ts +5745 -0
- package/dist/internal/index.js +6379 -0
- package/dist/{job-store-C5n6bhap.d.ts → job-store-m2pYmvbr.d.ts} +1772 -31
- package/dist/{memory-credit-ledger-7TTZDSRS.js → memory-credit-ledger-XJ5VQEVP.js} +3 -3
- package/dist/payout-reporter-3UB5WRCV.js +13 -0
- package/dist/revenue-reporter-JIKUPXOK.js +7 -0
- package/dist/server/index.d.ts +14 -3530
- package/dist/server/index.js +327 -20636
- package/dist/ssrf-DbFkpDv0.d.ts +118 -0
- package/dist/tempo-charge-store-RIFTALZK.js +8 -0
- package/dist/tempo-session-store-DALMRIWN.js +11 -0
- package/dist/testing/index.d.ts +3 -2
- package/dist/testing/index.js +3 -2
- package/dist/usd-DjVAPMlf.d.ts +97 -0
- package/dist/x402-5EVIUSEP.js +81 -0
- package/package.json +6 -2
- package/dist/x402-35VLYFKZ.js +0 -1272
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { ProofLike } from '@cashu/cashu-ts';
|
|
2
|
-
import { Challenge, Credential, Method, Receipt } from 'mppx';
|
|
3
|
-
import {
|
|
4
|
-
import { FacilitatorClient } from '@x402/core/server';
|
|
2
|
+
import { Challenge, Credential, Store, Method, Receipt } from 'mppx';
|
|
3
|
+
import { FacilitatorClient, FacilitatorConfig } from '@x402/core/server';
|
|
5
4
|
import { AuthorizerSigner, ChannelStorage, AutoSettlementConfig, Channel, ChannelUpdateResult } from '@x402/evm/batch-settlement/server';
|
|
5
|
+
import { MiddlewareHandler, Context, Hono } from 'hono';
|
|
6
|
+
import { Account, Client, Hex } from 'viem';
|
|
6
7
|
import { Pool, PoolClient } from 'pg';
|
|
7
|
-
import { PaymentPayload, PaymentRequirements, SettleResponse } from '@x402/core/types';
|
|
8
|
+
import { PaymentPayload as PaymentPayload$1, PaymentRequirements as PaymentRequirements$1, SettleResponse as SettleResponse$1 } from '@x402/core/types';
|
|
8
9
|
import { z } from 'zod';
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -78,6 +79,8 @@ interface X402SelfRelayConfig {
|
|
|
78
79
|
/** Advanced RPC-health hook; platform-hosted DVMs receive an automatic reporter. */
|
|
79
80
|
onRpcHealth?: (observation: X402SelfRelayRpcHealthObservation) => void | Promise<void>;
|
|
80
81
|
}
|
|
82
|
+
/** Caller-visible reasons an ambiguous x402 settlement could not be reconciled. */
|
|
83
|
+
type X402SettlementReconciliationReason = "settlement_not_on_chain" | "chain_unreachable" | "settlement_unbookmarked" | "facilitator_channel_state_unavailable";
|
|
81
84
|
/** Builder-facing configuration for reusable x402 batch-settlement channels. */
|
|
82
85
|
interface X402BatchSettlementConfig {
|
|
83
86
|
/** Receiver-authorizer signer used when the facilitator advertises none. */
|
|
@@ -142,6 +145,13 @@ interface X402ExactVersionSupport {
|
|
|
142
145
|
v1: boolean;
|
|
143
146
|
v2: boolean;
|
|
144
147
|
}
|
|
148
|
+
/** Client-side x402 wallet — EVM key pair for signing payments. */
|
|
149
|
+
interface X402Wallet {
|
|
150
|
+
privateKey: string;
|
|
151
|
+
address: string;
|
|
152
|
+
/** CAIP-2 chain id this wallet pays on. */
|
|
153
|
+
network: string;
|
|
154
|
+
}
|
|
145
155
|
/** Server-side receipt from verifying an x402 payment. */
|
|
146
156
|
interface X402Receipt {
|
|
147
157
|
verified: boolean;
|
|
@@ -181,6 +191,50 @@ interface X402Receipt {
|
|
|
181
191
|
|
|
182
192
|
/** Current x402 protocol version preferred by dvmkit clients and servers. */
|
|
183
193
|
declare const X402_VERSION = 2;
|
|
194
|
+
/** Legacy x402 protocol version retained during the dual-serve window. */
|
|
195
|
+
declare const X402_V1_VERSION = 1;
|
|
196
|
+
/** x402 protocol versions supported by dvmkit's hand-rolled exact codec. */
|
|
197
|
+
type X402Version = typeof X402_V1_VERSION | typeof X402_VERSION;
|
|
198
|
+
/**
|
|
199
|
+
* Chain a caller's x402 wallet reads when it was connected without an explicit
|
|
200
|
+
* `--network`. Single source of truth for the default: the wallet loader, the
|
|
201
|
+
* connect output and the asset resolver all read it from here (internal-review).
|
|
202
|
+
*/
|
|
203
|
+
declare const X402_DEFAULT_NETWORK = "eip155:8453";
|
|
204
|
+
/**
|
|
205
|
+
* Scheme name of the per-call rail — one signed authorization settling one
|
|
206
|
+
* resource. Shared with the credit funding menu's `x402.schemes` block, which
|
|
207
|
+
* has to name the same string the requirement carries (internal-review).
|
|
208
|
+
*/
|
|
209
|
+
declare const X402_EXACT_SCHEME = "exact";
|
|
210
|
+
/**
|
|
211
|
+
* Scheme name of the reusable v2 channel rail. Single source of truth: the
|
|
212
|
+
* facilitator probe, the requirement the server builds, the header dispatch,
|
|
213
|
+
* the caller's requirement selection, and the credit menu's `x402.schemes`
|
|
214
|
+
* block all read it from here — the menu advertises exactly what the challenge
|
|
215
|
+
* will carry, so a rename cannot leave one of them advertising a flavour the
|
|
216
|
+
* others no longer speak (internal-review).
|
|
217
|
+
*/
|
|
218
|
+
declare const X402_BATCH_SETTLEMENT_SCHEME = "batch-settlement";
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* x402-spec PaymentRequirements — the shape advertised on a 402 response's
|
|
222
|
+
* `accepts` array, mirrored from the facilitator's `PaymentRequirementsSchema`
|
|
223
|
+
* (see `x402/types/index.d.ts` in the published `x402` package).
|
|
224
|
+
*/
|
|
225
|
+
interface PaymentRequirementsV1 {
|
|
226
|
+
scheme: string;
|
|
227
|
+
network: string;
|
|
228
|
+
maxAmountRequired: string;
|
|
229
|
+
resource: string;
|
|
230
|
+
description: string;
|
|
231
|
+
mimeType: string;
|
|
232
|
+
payTo: string;
|
|
233
|
+
asset: string;
|
|
234
|
+
maxTimeoutSeconds: number;
|
|
235
|
+
outputSchema?: Record<string, unknown>;
|
|
236
|
+
extra?: Record<string, unknown>;
|
|
237
|
+
}
|
|
184
238
|
/** x402 v2 resource metadata echoed by a paying client. */
|
|
185
239
|
interface ResourceInfo {
|
|
186
240
|
url: string;
|
|
@@ -204,6 +258,73 @@ interface PaymentRequirementsV2 {
|
|
|
204
258
|
*/
|
|
205
259
|
extra?: Record<string, unknown> | null;
|
|
206
260
|
}
|
|
261
|
+
/** Legacy alias retained for server internals that carry the v1 body requirements. */
|
|
262
|
+
type PaymentRequirements = PaymentRequirementsV1;
|
|
263
|
+
/**
|
|
264
|
+
* EIP-3009 `TransferWithAuthorization` parameters that the client signs and
|
|
265
|
+
* the facilitator submits on settlement.
|
|
266
|
+
*/
|
|
267
|
+
interface ExactEvmPayloadAuthorization {
|
|
268
|
+
from: string;
|
|
269
|
+
to: string;
|
|
270
|
+
value: string;
|
|
271
|
+
validAfter: string;
|
|
272
|
+
validBefore: string;
|
|
273
|
+
nonce: string;
|
|
274
|
+
}
|
|
275
|
+
/** Signed EIP-3009 transfer authorization (the `payload` of a PaymentPayload). */
|
|
276
|
+
interface ExactEvmPayload {
|
|
277
|
+
signature: string;
|
|
278
|
+
authorization: ExactEvmPayloadAuthorization;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* x402-spec PaymentPayload — the JSON shape carried inside the `X-PAYMENT`
|
|
282
|
+
* header (base64-encoded). `payload` is scheme-specific; alpha only supports
|
|
283
|
+
* `scheme: "exact"` against an EVM network.
|
|
284
|
+
*/
|
|
285
|
+
interface PaymentPayloadV1 {
|
|
286
|
+
x402Version: typeof X402_V1_VERSION;
|
|
287
|
+
scheme: "exact";
|
|
288
|
+
network: string;
|
|
289
|
+
payload: ExactEvmPayload;
|
|
290
|
+
}
|
|
291
|
+
/** x402 v2 payment payload carried in `PAYMENT-SIGNATURE`. */
|
|
292
|
+
interface PaymentPayloadV2 {
|
|
293
|
+
x402Version: typeof X402_VERSION;
|
|
294
|
+
resource?: ResourceInfo;
|
|
295
|
+
accepted: PaymentRequirementsV2;
|
|
296
|
+
payload: ExactEvmPayload;
|
|
297
|
+
extensions?: Record<string, unknown>;
|
|
298
|
+
}
|
|
299
|
+
/** Exact EVM payment payload accepted by the dual-version codec. */
|
|
300
|
+
type PaymentPayload = PaymentPayloadV1 | PaymentPayloadV2;
|
|
301
|
+
/** Facilitator `/verify` response. */
|
|
302
|
+
interface VerifyResponse {
|
|
303
|
+
isValid: boolean;
|
|
304
|
+
invalidReason?: string;
|
|
305
|
+
payer?: string;
|
|
306
|
+
}
|
|
307
|
+
/** Facilitator `/settle` response — what we base64 into `X-PAYMENT-RESPONSE`. */
|
|
308
|
+
interface SettleResponse {
|
|
309
|
+
success: boolean;
|
|
310
|
+
errorReason?: string;
|
|
311
|
+
payer?: string;
|
|
312
|
+
transaction?: string;
|
|
313
|
+
network?: string;
|
|
314
|
+
amount?: string;
|
|
315
|
+
extensions?: Record<string, unknown>;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Body envelope used to carry x402 PaymentRequirements on a 402 response.
|
|
319
|
+
* The x402 spec puts requirements in the JSON body under `accepts`; the
|
|
320
|
+
* `WWW-Authenticate: Payment` header carries the MPP method advertisements
|
|
321
|
+
* separately, so the two protocols don't fight for the same header slot.
|
|
322
|
+
*/
|
|
323
|
+
interface X402ResponseBody {
|
|
324
|
+
x402Version: typeof X402_V1_VERSION;
|
|
325
|
+
accepts: PaymentRequirementsV1[];
|
|
326
|
+
error?: string;
|
|
327
|
+
}
|
|
207
328
|
/** x402 v2 payment-required declaration encoded into `PAYMENT-REQUIRED`. */
|
|
208
329
|
interface PaymentRequiredV2 {
|
|
209
330
|
x402Version: typeof X402_VERSION;
|
|
@@ -212,6 +333,101 @@ interface PaymentRequiredV2 {
|
|
|
212
333
|
accepts: PaymentRequirementsV2[];
|
|
213
334
|
extensions?: Record<string, unknown>;
|
|
214
335
|
}
|
|
336
|
+
/** Either challenge shape understood by dvmkit callers. */
|
|
337
|
+
type PaymentRequired = X402ResponseBody | PaymentRequiredV2;
|
|
338
|
+
/** Inputs for `buildPaymentRequirements`. */
|
|
339
|
+
interface BuildPaymentRequirementsOpts {
|
|
340
|
+
x402Config: X402Config;
|
|
341
|
+
requiredUsdcMicro: bigint;
|
|
342
|
+
resource: string;
|
|
343
|
+
description?: string;
|
|
344
|
+
mimeType?: string;
|
|
345
|
+
version?: X402Version;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Build a single x402 `PaymentRequirements` entry from the DVM's `X402Config`,
|
|
349
|
+
* the resolved per-call USDC amount, and the live request's resource URL.
|
|
350
|
+
* The resource string is bound into the EIP-3009 `extra` field on signing in
|
|
351
|
+
* the reference clients, so it must match exactly between the 402 response
|
|
352
|
+
* and the retry payload — pass the request URL the client will retry against.
|
|
353
|
+
*/
|
|
354
|
+
declare function buildPaymentRequirements(opts: BuildPaymentRequirementsOpts & {
|
|
355
|
+
version: typeof X402_V1_VERSION;
|
|
356
|
+
}): PaymentRequirementsV1;
|
|
357
|
+
declare function buildPaymentRequirements(opts: (BuildPaymentRequirementsOpts & {
|
|
358
|
+
version: typeof X402_VERSION;
|
|
359
|
+
}) | (BuildPaymentRequirementsOpts & {
|
|
360
|
+
version?: undefined;
|
|
361
|
+
})): PaymentRequirementsV2;
|
|
362
|
+
/** Build the canonical x402 v2 declaration for a `PAYMENT-REQUIRED` header. */
|
|
363
|
+
declare function buildPaymentRequiredV2(opts: Omit<BuildPaymentRequirementsOpts, "version"> & {
|
|
364
|
+
error?: string;
|
|
365
|
+
}): PaymentRequiredV2;
|
|
366
|
+
/** Convert the server's legacy body requirements into the parallel v2 declaration. */
|
|
367
|
+
declare function paymentRequiredV2FromV1(requirements: PaymentRequirementsV1[], error?: string): PaymentRequiredV2;
|
|
368
|
+
/**
|
|
369
|
+
* Translate a CAIP-2 chain id (`eip155:8453`) to the x402-spec network slug
|
|
370
|
+
* (`base`). Operators configure DVMs in CAIP-2 because it's the lingua-franca
|
|
371
|
+
* across our other rails; the x402 facilitator API only accepts named slugs.
|
|
372
|
+
* Unknown CAIP-2 ids fall through to the original string so non-EVM rails
|
|
373
|
+
* (Solana mainnet, etc.) can also be plumbed once supported.
|
|
374
|
+
*/
|
|
375
|
+
declare function caip2ToX402Network(network: string): string;
|
|
376
|
+
/** Translate a legacy x402 network slug back to its CAIP-2 chain id. */
|
|
377
|
+
declare function x402NetworkToCaip2(network: string): string;
|
|
378
|
+
/** EIP-712 chain id derived from a CAIP-2 chain id. */
|
|
379
|
+
declare function chainIdFromCaip2(network: string): number;
|
|
380
|
+
/** USDC contract address for a network slug (post-translation). */
|
|
381
|
+
declare function usdcContractFor(networkSlug: string): `0x${string}` | undefined;
|
|
382
|
+
/**
|
|
383
|
+
* USDC contract address for a CAIP-2 chain id (e.g. `eip155:8453`). Resolves
|
|
384
|
+
* via {@link caip2ToX402Network} so the one network table is the source of
|
|
385
|
+
* truth — keeps callers that hold the CAIP-2 form (CLI wallet) in lockstep
|
|
386
|
+
* with the SDK 402-builder which already speaks x402 slugs.
|
|
387
|
+
*/
|
|
388
|
+
declare function usdcContractByCaip2(caip2: string): `0x${string}` | undefined;
|
|
389
|
+
/** USDC EIP-712 domain `name` for a network slug. */
|
|
390
|
+
declare function usdcDomainNameFor(networkSlug: string): string | undefined;
|
|
391
|
+
/** USDC EIP-712 domain `version` for a network slug. */
|
|
392
|
+
declare function usdcDomainVersionFor(networkSlug: string): string | undefined;
|
|
393
|
+
/**
|
|
394
|
+
* Decode a base64-encoded `X-PAYMENT` header into a structured `PaymentPayload`.
|
|
395
|
+
* Throws on malformed input rather than silently returning a partial — the
|
|
396
|
+
* server treats decode failures as `payment_invalid` per the spec.
|
|
397
|
+
*/
|
|
398
|
+
declare function decodePayment(header: string): PaymentPayload;
|
|
399
|
+
/**
|
|
400
|
+
* Encode a `PaymentPayload` for transport in the `X-PAYMENT` header. JSON
|
|
401
|
+
* serialization uses string-typed integer fields (the spec encodes uint256
|
|
402
|
+
* values as decimal strings, so `bigint` must already be stringified by the
|
|
403
|
+
* caller).
|
|
404
|
+
*/
|
|
405
|
+
declare function encodePayment(payload: PaymentPayload): string;
|
|
406
|
+
/** Encode a v2 `PaymentRequired` declaration for the HTTP response header. */
|
|
407
|
+
declare function encodePaymentRequiredHeader(required: PaymentRequiredV2): string;
|
|
408
|
+
/** Decode and structurally validate a v2 `PAYMENT-REQUIRED` header. */
|
|
409
|
+
declare function decodePaymentRequiredHeader(header: string): PaymentRequiredV2;
|
|
410
|
+
/** Read the exact EIP-3009 authorization carried by either protocol version. */
|
|
411
|
+
declare function exactEvmAuthorization(payload: PaymentPayload): ExactEvmPayloadAuthorization;
|
|
412
|
+
/**
|
|
413
|
+
* Encode a `SettleResponse` for the `X-PAYMENT-RESPONSE` header that the
|
|
414
|
+
* server emits on a 2xx after a successful settlement (internal-review, per the
|
|
415
|
+
* x402 spec).
|
|
416
|
+
*/
|
|
417
|
+
declare function encodeSettleResponseHeader(response: SettleResponse): string;
|
|
418
|
+
/**
|
|
419
|
+
* POST a verify request to the facilitator. Returns the parsed `VerifyResponse`
|
|
420
|
+
* or a structured-failure shape when the facilitator rejects the call (network
|
|
421
|
+
* error, non-2xx, or `isValid: false`).
|
|
422
|
+
*/
|
|
423
|
+
declare function verifyWithFacilitator(payload: PaymentPayload, requirements: PaymentRequirementsV1 | PaymentRequirementsV2, config?: Pick<X402Config, "facilitator" | "facilitatorAuth">, createAuthHeaders?: FacilitatorConfig["createAuthHeaders"] | undefined): Promise<VerifyResponse>;
|
|
424
|
+
/**
|
|
425
|
+
* POST a settle request to the facilitator. The facilitator broadcasts the
|
|
426
|
+
* EIP-3009 `transferWithAuthorization` and returns the on-chain tx hash on
|
|
427
|
+
* success; the dvmkit DVM mirrors that into `X-PAYMENT-RESPONSE` for the
|
|
428
|
+
* client and into `revenue_events.tx_hash` for the platform ledger.
|
|
429
|
+
*/
|
|
430
|
+
declare function settleWithFacilitator(payload: PaymentPayload, requirements: PaymentRequirementsV1 | PaymentRequirementsV2, config?: Pick<X402Config, "facilitator" | "facilitatorAuth">, createAuthHeaders?: FacilitatorConfig["createAuthHeaders"] | undefined): Promise<SettleResponse>;
|
|
215
431
|
|
|
216
432
|
/** Identifies which side of the conversation sent a message. */
|
|
217
433
|
type MessageFrom = "requester" | "provider";
|
|
@@ -419,6 +635,16 @@ type Message = (MessageBase & {
|
|
|
419
635
|
type: "progress";
|
|
420
636
|
content: ProgressContent;
|
|
421
637
|
});
|
|
638
|
+
/** Type guard: message contains an artifact. */
|
|
639
|
+
declare function isArtifactMessage(msg: Message): msg is MessageBase & {
|
|
640
|
+
type: "artifact";
|
|
641
|
+
content: ArtifactContent;
|
|
642
|
+
};
|
|
643
|
+
/** Type guard: message is a payment request. */
|
|
644
|
+
declare function isPaymentRequestMessage(msg: Message): msg is MessageBase & {
|
|
645
|
+
type: "payment-request";
|
|
646
|
+
content: PaymentRequestContent;
|
|
647
|
+
};
|
|
422
648
|
|
|
423
649
|
/** Auth identifier advertised by DVMs using audience-bound caller proofs. */
|
|
424
650
|
declare const SIGNED_REQUEST_AUTH_ID: "secp256k1-schnorr-v2";
|
|
@@ -514,6 +740,42 @@ declare const requireClientCompatibility: ClientCompatibilityGate;
|
|
|
514
740
|
/** Render the normal structured HTTP 426 compatibility error. */
|
|
515
741
|
declare function clientUpgradeRequired(c: Context, compatibility: ClientCompatibility, requirement: ClientCompatibilityRequirement): Response;
|
|
516
742
|
|
|
743
|
+
/**
|
|
744
|
+
* Canonical JSON serialisation used for secp256k1+BIP-340 Schnorr request
|
|
745
|
+
* signing in cast (internal-review).
|
|
746
|
+
*
|
|
747
|
+
* Single source of truth for both the cast DVM's verifier (internal-review) and the
|
|
748
|
+
* client signing helper (internal-review). The same function on both sides guarantees
|
|
749
|
+
* that what the client signs is byte-identical to what the server hashes.
|
|
750
|
+
*
|
|
751
|
+
* Algorithm: recursively sort object keys lexicographically (UTF-16 code-unit
|
|
752
|
+
* order — JavaScript's default), then JSON.stringify with no whitespace.
|
|
753
|
+
* Arrays preserve order. Numbers, strings, booleans, null pass through as
|
|
754
|
+
* `JSON.stringify` formats them. Values of `undefined` or functions are
|
|
755
|
+
* forbidden (we throw rather than silently drop, since both sides must agree).
|
|
756
|
+
*/
|
|
757
|
+
/** Any JSON-serialisable value accepted by `canonicalize`. */
|
|
758
|
+
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
759
|
+
[key: string]: JsonValue;
|
|
760
|
+
};
|
|
761
|
+
/**
|
|
762
|
+
* Serialise `value` to its canonical JSON form (sorted keys, no whitespace).
|
|
763
|
+
*
|
|
764
|
+
* `undefined` properties on objects are silently omitted (mirroring
|
|
765
|
+
* `JSON.stringify`), so callers don't need to explicitly delete optional fields
|
|
766
|
+
* that weren't set. Throws `TypeError` for unsupported scalar types (functions,
|
|
767
|
+
* symbols, non-finite numbers). The caller is responsible for stripping fields
|
|
768
|
+
* that shouldn't appear in the signed payload (e.g. `signature` itself).
|
|
769
|
+
*/
|
|
770
|
+
declare function canonicalize(value: JsonValue): string;
|
|
771
|
+
/**
|
|
772
|
+
* Canonicalise `value` and return the UTF-8 bytes the signer should produce a
|
|
773
|
+
* signature over. Both the cast client signer (`signAddEpisode`) and the
|
|
774
|
+
* server verifier (`verifyCanonicalSignedRequest`) call this — sharing the function
|
|
775
|
+
* guarantees byte-identical input on both sides.
|
|
776
|
+
*/
|
|
777
|
+
declare function canonicaliseForSigning(value: JsonValue): Uint8Array;
|
|
778
|
+
|
|
517
779
|
/** Terminal outcome a receipt attests. Mirrors the SDK's terminal job statuses. */
|
|
518
780
|
type ReceiptOutcome = "completed" | "failed" | "cancelled";
|
|
519
781
|
/** What the caller paid for the job, as persisted on the job record. */
|
|
@@ -611,6 +873,8 @@ interface JobReceipt {
|
|
|
611
873
|
*/
|
|
612
874
|
credit?: ReceiptCredit;
|
|
613
875
|
}
|
|
876
|
+
/** A receipt before signing — every field but the signature itself. */
|
|
877
|
+
type UnsignedJobReceipt = Omit<JobReceipt, "signature">;
|
|
614
878
|
/** Reclaim lifecycle event a {@link DrainReceipt} attests (internal-review, spec §5). */
|
|
615
879
|
type DrainReceiptEvent = "requested" | "parked" | "picked_up" | "sent" | "released";
|
|
616
880
|
/**
|
|
@@ -665,6 +929,8 @@ interface DrainReceipt {
|
|
|
665
929
|
/** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
|
|
666
930
|
signature: string;
|
|
667
931
|
}
|
|
932
|
+
/** A drain receipt before signing — every field but the signature itself. */
|
|
933
|
+
type UnsignedDrainReceipt = Omit<DrainReceipt, "signature">;
|
|
668
934
|
/**
|
|
669
935
|
* A DVM-signed proof that one payment funded a prepaid credit (internal-review).
|
|
670
936
|
* The balance and sequence are the values fixed when the funding committed;
|
|
@@ -698,6 +964,94 @@ interface FundingReceipt {
|
|
|
698
964
|
/** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
|
|
699
965
|
signature: string;
|
|
700
966
|
}
|
|
967
|
+
/** A funding receipt before signing. */
|
|
968
|
+
type UnsignedFundingReceipt = Omit<FundingReceipt, "signature">;
|
|
969
|
+
/**
|
|
970
|
+
* Hash the delivered result so a receipt binds to *what* was returned, not
|
|
971
|
+
* just that something was.
|
|
972
|
+
*
|
|
973
|
+
* `sha256(canonicalize({ summary, artifact_hashes }))`, where each artifact
|
|
974
|
+
* hash is `sha256(canonicalize(artifactContent))` in emission order (`[]`
|
|
975
|
+
* when the job emitted none). Hashing the artifact *content object* rather
|
|
976
|
+
* than trusting `content.sha256` — which is optional and which `ctx.artifact`
|
|
977
|
+
* never fills in — keeps the hash recomputable by any consumer from exactly
|
|
978
|
+
* the messages it received.
|
|
979
|
+
*
|
|
980
|
+
* **Pass `summary` only for a `completed` job.** On `failed`/`cancelled` the
|
|
981
|
+
* SDK's durable terminal writes the terminal *reason* into the job's summary
|
|
982
|
+
* column, so a verifier recomputing a non-completed receipt must pass
|
|
983
|
+
* `undefined` here and read the reason off `receipt.reason` instead. The
|
|
984
|
+
* issuer does the same (`resultHashFor` in `sdk/server/receipt-issuer.ts`).
|
|
985
|
+
*/
|
|
986
|
+
declare function computeResultHash(summary: string | undefined, artifactContents: JsonValue[]): string;
|
|
987
|
+
/**
|
|
988
|
+
* Sign an unsigned receipt with the DVM's receipt secret, returning the
|
|
989
|
+
* complete receipt. Signs `canonicaliseForSigning(receipt)` — the same
|
|
990
|
+
* sorted-key canonical JSON the caller-auth envelope and the builder
|
|
991
|
+
* attestation use, so {@link verifyReceipt} is symmetric with
|
|
992
|
+
* `verifyCanonicalSignedRequest`. There is no second canonicalisation.
|
|
993
|
+
*/
|
|
994
|
+
declare function signReceipt(unsigned: UnsignedJobReceipt, secretHex: string): JobReceipt;
|
|
995
|
+
/**
|
|
996
|
+
* Verify a receipt's self-consistency: the BIP-340 signature over the
|
|
997
|
+
* canonical payload (minus `signature`) under the receipt's own
|
|
998
|
+
* `receipt_pubkey`. Returns `false` rather than throwing on malformed input.
|
|
999
|
+
*
|
|
1000
|
+
* This is only the inner link. A consumer that wants provenance must also
|
|
1001
|
+
* check `receipt_pubkey` against `/v1/info#builder`'s attested
|
|
1002
|
+
* `receipt_pubkey` and verify that attestation under the builder pubkey
|
|
1003
|
+
* (`verifyAttestation` in `builder-identity.ts`).
|
|
1004
|
+
*/
|
|
1005
|
+
declare function verifyReceipt(receipt: JobReceipt): boolean;
|
|
1006
|
+
/**
|
|
1007
|
+
* Structural guard on a receipt that arrived from outside this process — off a
|
|
1008
|
+
* `/v1/job` response, or back out of the hand-editable `~/.dvm/receipts.jsonl`.
|
|
1009
|
+
*
|
|
1010
|
+
* Checks only that the load-bearing fields are the right *kind* of thing; it
|
|
1011
|
+
* deliberately does not judge the receipt. A well-formed receipt whose
|
|
1012
|
+
* signature doesn't hold is `invalid` and the caller must see it said so. A
|
|
1013
|
+
* body missing one of these fields is a different animal: consumers dereference
|
|
1014
|
+
* them unconditionally (`receiptDisplay`'s `paid.msats`, the attestation link's
|
|
1015
|
+
* `receipt_pubkey`), so an unguarded one throws `TypeError` and surfaces as an
|
|
1016
|
+
* `internal` exit — and it could never have verified anyway, since the
|
|
1017
|
+
* signature covers the whole canonical body. Refusing it costs no proof.
|
|
1018
|
+
*
|
|
1019
|
+
* One guard for both directions on purpose: what can't come in over the wire
|
|
1020
|
+
* must not be able to come back out of the file.
|
|
1021
|
+
*
|
|
1022
|
+
* Release coupling: adding or requiring a field here changes both the SDK's
|
|
1023
|
+
* emitted wire shape and the caller CLI's accepted shape. Name the first-party
|
|
1024
|
+
* fleet redeploy as a prerequisite in the release note before publishing the
|
|
1025
|
+
* stricter caller; see `public compatibility guide`.
|
|
1026
|
+
*/
|
|
1027
|
+
declare function isSignedJobReceipt(value: unknown): value is JobReceipt;
|
|
1028
|
+
/** Sign an unsigned drain receipt — same canonicalisation as {@link signReceipt}. */
|
|
1029
|
+
declare function signDrainReceipt(unsigned: UnsignedDrainReceipt, secretHex: string): DrainReceipt;
|
|
1030
|
+
/** Sign an unsigned funding receipt using the common receipt canonicalisation. */
|
|
1031
|
+
declare function signFundingReceipt(unsigned: UnsignedFundingReceipt, secretHex: string): FundingReceipt;
|
|
1032
|
+
/** Structural guard for a funding receipt received over the wire or from disk. */
|
|
1033
|
+
declare function isFundingReceipt(value: unknown): value is FundingReceipt;
|
|
1034
|
+
/** Verify a funding receipt's inner BIP-340 signature. */
|
|
1035
|
+
declare function verifyFundingReceipt(receipt: FundingReceipt): boolean;
|
|
1036
|
+
/**
|
|
1037
|
+
* Structural guard on a drain receipt from outside this process — off a
|
|
1038
|
+
* `/v1/credit` drain response, or back out of `~/.dvm/credits.json`.
|
|
1039
|
+
*
|
|
1040
|
+
* Same contract and same reasoning as {@link isSignedJobReceipt}: both homes
|
|
1041
|
+
* type these `unknown[]` because neither the wire nor a hand-editable file is
|
|
1042
|
+
* evidence of shape, and the chain walk dereferences `receipt_pubkey`
|
|
1043
|
+
* unconditionally. A body missing one of these fields could never have carried
|
|
1044
|
+
* a valid signature anyway, so refusing it costs no proof.
|
|
1045
|
+
* Required-field changes follow the fleet-before-caller release note linked
|
|
1046
|
+
* from {@link isSignedJobReceipt}.
|
|
1047
|
+
*/
|
|
1048
|
+
declare function isDrainReceipt(value: unknown): value is DrainReceipt;
|
|
1049
|
+
/**
|
|
1050
|
+
* Verify a drain receipt's self-consistency, mirroring {@link verifyReceipt}
|
|
1051
|
+
* — the same provenance caveat applies: check `receipt_pubkey` against the
|
|
1052
|
+
* DVM's attested `receipt_pubkey` for the outer link.
|
|
1053
|
+
*/
|
|
1054
|
+
declare function verifyDrainReceipt(receipt: DrainReceipt): boolean;
|
|
701
1055
|
|
|
702
1056
|
/**
|
|
703
1057
|
* Funding lots: the in-kind basis of a non-channel Bitcoin credit (internal-review).
|
|
@@ -734,12 +1088,19 @@ interface FundingReceipt {
|
|
|
734
1088
|
declare const NON_CHANNEL_BITCOIN_RAILS: readonly ["cashu", "lightning"];
|
|
735
1089
|
/** A rail whose unused credit is a sats deposit the provider owes back. */
|
|
736
1090
|
type NonChannelBitcoinRail = (typeof NON_CHANNEL_BITCOIN_RAILS)[number];
|
|
1091
|
+
/** True when `rail` funds a credit with sats the provider holds and owes back. */
|
|
1092
|
+
declare function isNonChannelBitcoinRail(rail: string | null | undefined): rail is NonChannelBitcoinRail;
|
|
737
1093
|
/**
|
|
738
1094
|
* One funding event's deposit: the sats that arrived and the credit micro they
|
|
739
1095
|
* bought, with however much of that micro is still unspent.
|
|
740
1096
|
*
|
|
741
1097
|
* `remainingMicro` is depleted FIFO by settled draws and by reclaims, so the
|
|
742
|
-
* sum of a credit's lots tracks `credits.balance_micro` exactly
|
|
1098
|
+
* sum of a credit's lots tracks `credits.balance_micro` exactly — with one
|
|
1099
|
+
* deliberate exception: an expiry release (internal-review) zeroes the balance and
|
|
1100
|
+
* leaves the lots standing, because a revival restores that balance and its
|
|
1101
|
+
* in-kind basis has to still be there. A released credit is excluded from the
|
|
1102
|
+
* sweep floor by `BITCOIN_CREDIT_PREDICATE`'s own `balance_micro > 0`, so the
|
|
1103
|
+
* over-coverage is never counted as sats the builder owes back.
|
|
743
1104
|
*/
|
|
744
1105
|
interface FundingLot {
|
|
745
1106
|
lotId: string;
|
|
@@ -755,6 +1116,71 @@ interface FundingLot {
|
|
|
755
1116
|
fundingRef: string | null;
|
|
756
1117
|
createdAt: number;
|
|
757
1118
|
}
|
|
1119
|
+
/** One lot's share of a depletion, and what that share is worth in kind. */
|
|
1120
|
+
interface LotDebit {
|
|
1121
|
+
lotId: string;
|
|
1122
|
+
/** Credit micro taken out of this lot. */
|
|
1123
|
+
micro: number;
|
|
1124
|
+
/** Sats that micro is worth at this lot's own funding rate, floored. */
|
|
1125
|
+
sats: number;
|
|
1126
|
+
}
|
|
1127
|
+
/** What a FIFO depletion took, and what it could not cover. */
|
|
1128
|
+
interface LotDepletion {
|
|
1129
|
+
debits: LotDebit[];
|
|
1130
|
+
/** In-kind value of everything taken — the reclaim obligation. */
|
|
1131
|
+
satsOwed: number;
|
|
1132
|
+
/** Micro the lots could not cover. Non-zero means the credit's lots are short. */
|
|
1133
|
+
uncoveredMicro: number;
|
|
1134
|
+
/** Of the micro taken, how much came out of a lot carrying a real sats basis. */
|
|
1135
|
+
backedMicro: number;
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Take `amountMicro` out of `lots`, oldest first, and price what was taken at
|
|
1139
|
+
* each lot's own rate.
|
|
1140
|
+
*
|
|
1141
|
+
* FIFO rather than pro rata across the pool, so a top-up taken at a different
|
|
1142
|
+
* rate is reclaimed at *that* rate once the earlier deposit is spent. A
|
|
1143
|
+
* fraction of a satoshi cannot be handed back, so the conversion floors and
|
|
1144
|
+
* the dust stays with the provider rather than being rounded into a payout the
|
|
1145
|
+
* deposit does not cover.
|
|
1146
|
+
*
|
|
1147
|
+
* A slice is priced as the **decrement in the lot's own obligation** —
|
|
1148
|
+
* `lotSats(before) - lotSats(after)` — rather than by flooring the slice on
|
|
1149
|
+
* its own. The two agree whenever a lot is taken whole, which is every
|
|
1150
|
+
* ordinary reclaim; they diverge once a lot is drawn down in pieces, and there
|
|
1151
|
+
* the independent floor loses up to a satoshi *per piece*, permanently. That
|
|
1152
|
+
* matters since internal-review, where each settling draw is priced through here: the
|
|
1153
|
+
* slices have to telescope, or a lot spent over many jobs pays out less than
|
|
1154
|
+
* it took in and the residue is exactly the unattributable one this was meant
|
|
1155
|
+
* to remove. It also makes {@link lotOwedSats} exact rather than conservative
|
|
1156
|
+
* — the remaining obligation is precisely what future depletions will pay.
|
|
1157
|
+
*
|
|
1158
|
+
* `lots` must already be in FIFO order ({@link fifoOrder}).
|
|
1159
|
+
*/
|
|
1160
|
+
declare function depleteLots(lots: FundingLot[], amountMicro: number): LotDepletion;
|
|
1161
|
+
/**
|
|
1162
|
+
* What a settling draw's fiat debit was worth in kind, in millisatoshis
|
|
1163
|
+
* (internal-review) — `null` where the lots cannot price it.
|
|
1164
|
+
*
|
|
1165
|
+
* This is the single sats authority for a non-channel Bitcoin credit. The
|
|
1166
|
+
* settle already depletes the lots the draw consumed, so the figure costs no
|
|
1167
|
+
* extra read: it is `depletion.satsOwed` in the ledger's own msat unit, and
|
|
1168
|
+
* stamping it on `credit_draws.draw_msats` is what makes
|
|
1169
|
+
* `Σ settled draw sats + reclaim sats == Σ sats funded` close per credit.
|
|
1170
|
+
*
|
|
1171
|
+
* `null` follows {@link isInKindDepletion} exactly, so a settle and a reclaim
|
|
1172
|
+
* fall back together: lots short of the balance, or covering lots with no sats
|
|
1173
|
+
* basis, keep the pooled pro-rata figure rather than blending a lot rate with
|
|
1174
|
+
* a blended one. Mixed coverage counts as unpriceable for the same reason it
|
|
1175
|
+
* does on the reclaim.
|
|
1176
|
+
*/
|
|
1177
|
+
declare function inKindDrawMsats(depletion: LotDepletion, amountMicro: number): number | null;
|
|
1178
|
+
/**
|
|
1179
|
+
* What these lots owe in kind if every remaining micro were reclaimed now —
|
|
1180
|
+
* the deposit-liability figure, and the floor the hub must never be swept
|
|
1181
|
+
* below.
|
|
1182
|
+
*/
|
|
1183
|
+
declare function lotOwedSats(lots: FundingLot[]): number;
|
|
758
1184
|
/**
|
|
759
1185
|
* Sats held back from every non-channel Bitcoin reclaim to pay for handing it
|
|
760
1186
|
* over (internal-review).
|
|
@@ -777,7 +1203,502 @@ interface FundingLot {
|
|
|
777
1203
|
* rule the per-lot flooring already follows.
|
|
778
1204
|
*/
|
|
779
1205
|
declare const DRAIN_DELIVERY_RESERVE_SATS = 8;
|
|
1206
|
+
/**
|
|
1207
|
+
* The figure a reclaim publishes: its in-kind gross less
|
|
1208
|
+
* {@link DRAIN_DELIVERY_RESERVE_SATS}, floored at zero.
|
|
1209
|
+
*
|
|
1210
|
+
* Zero is a refusal rather than a payout — a balance worth no more than what
|
|
1211
|
+
* it costs to send is `drain_below_dust`, since parking a zero-value token
|
|
1212
|
+
* would hold the servicer's queue open forever.
|
|
1213
|
+
*/
|
|
1214
|
+
declare function netOwedSats(grossSats: number): number;
|
|
1215
|
+
/**
|
|
1216
|
+
* The order lots deplete in: oldest first, `lotId` breaking a tie.
|
|
1217
|
+
*
|
|
1218
|
+
* A tiebreak is not decoration — lot ids are surrogates rather than a
|
|
1219
|
+
* per-credit sequence (`fund` takes no credit lock, so two concurrent
|
|
1220
|
+
* fundings cannot cooperate on a counter), and two fundings can land on the
|
|
1221
|
+
* same millisecond. Without it the FIFO order is whatever the planner
|
|
1222
|
+
* returned, and a reclaim's figure would depend on it.
|
|
1223
|
+
*/
|
|
1224
|
+
declare function fifoOrder(left: FundingLot, right: FundingLot): number;
|
|
1225
|
+
/**
|
|
1226
|
+
* Whether a depletion may be settled in kind, or has to fall back to pricing
|
|
1227
|
+
* the fiat balance at a live rate.
|
|
1228
|
+
*
|
|
1229
|
+
* Both failure modes are real and neither is the caller's fault: a credit's
|
|
1230
|
+
* lots can be short (a coverage hole the boot backfill is meant to close), and
|
|
1231
|
+
* a credit funded before internal-review recorded a rail basis at all has lots whose
|
|
1232
|
+
* `satsFunded` is 0 — pricing *that* in kind would answer "we owe you nothing"
|
|
1233
|
+
* for a deposit we plainly hold. Mixed coverage is treated as unbacked too:
|
|
1234
|
+
* blending a lot rate with a live rate produces a figure neither basis
|
|
1235
|
+
* supports.
|
|
1236
|
+
*/
|
|
1237
|
+
declare function isInKindDepletion(depletion: LotDepletion, amountMicro: number): boolean;
|
|
1238
|
+
|
|
1239
|
+
/** The rails a payout lands on. Lightning never pays out: credits fund straight into the receive wallet. */
|
|
1240
|
+
type PayoutRail = "cashu" | "x402" | "tempo";
|
|
1241
|
+
/**
|
|
1242
|
+
* What gathered the payments into one transfer: a cashu accumulator melt, an
|
|
1243
|
+
* x402 batch settle to `pay_to`, a Tempo channel's cooperative close, or a
|
|
1244
|
+
* Tempo channel's scheduled / manual settlement to the recipient.
|
|
1245
|
+
*/
|
|
1246
|
+
type PayoutKind = "melt" | "batch" | "close" | "settle";
|
|
1247
|
+
/** Which repair verb completed a stuck movement. Only `tempo_close_reconciled` is produced here today. */
|
|
1248
|
+
type PayoutRepairKind = "x402_settlement_reconciled" | "lightning_invoice_reconciled" | "tempo_close_reconciled";
|
|
1249
|
+
/**
|
|
1250
|
+
* JSON wire shape POSTed to the platform's `/_internal/payout` endpoint —
|
|
1251
|
+
* one landed movement into the builder's custody. Idempotent platform-side
|
|
1252
|
+
* on `(dvmId, payoutId)`, so the durable retry loop can redeliver freely; a
|
|
1253
|
+
* `repaired` redelivery of an id that already landed upgrades that row rather
|
|
1254
|
+
* than adding a second.
|
|
1255
|
+
*/
|
|
1256
|
+
interface PayoutReportPayload {
|
|
1257
|
+
/** Required. Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
|
|
1258
|
+
dvmId: string;
|
|
1259
|
+
/** Required. The DVM's own id for the movement — `cashu:melt:<quote>`, `x402:batch:<uuid>`, `tempo:<tx>`. */
|
|
1260
|
+
payoutId: string;
|
|
1261
|
+
rail: PayoutRail;
|
|
1262
|
+
kind: PayoutKind;
|
|
1263
|
+
/** Required. Rail-native atomic units: sats, or USDC micro. Always > 0. */
|
|
1264
|
+
nativeAmount: number;
|
|
1265
|
+
/** Required. `sats` for cashu, `usdc` for the stablecoin rails. */
|
|
1266
|
+
nativeAsset: "sats" | "usdc";
|
|
1267
|
+
/** Where it landed; `null` for a melt, whose Lightning destination the platform never records. */
|
|
1268
|
+
recipient: string | null;
|
|
1269
|
+
/** Mechanism references — mint, melt quote, tx hash, batch id, voucher count, channel id. Description material, never arithmetic. */
|
|
1270
|
+
refs: Record<string, unknown>;
|
|
1271
|
+
/** Required. When the money landed (epoch ms). */
|
|
1272
|
+
landedAt: number;
|
|
1273
|
+
/** Set when a repair verb completed this movement. */
|
|
1274
|
+
repaired?: boolean;
|
|
1275
|
+
/** Required iff `repaired`. */
|
|
1276
|
+
repairKind?: PayoutRepairKind;
|
|
1277
|
+
}
|
|
1278
|
+
/** A stuck movement inside a pending snapshot. */
|
|
1279
|
+
interface PayoutWedge {
|
|
1280
|
+
id: string;
|
|
1281
|
+
/**
|
|
1282
|
+
* `settle`: an x402 batch whose transfer keeps failing (auto-retried).
|
|
1283
|
+
* `refund`: an x402 cooperative refund wedged between chain and ledger —
|
|
1284
|
+
* caller money, not in the pool. `close`: a Tempo close wedged the same way.
|
|
1285
|
+
* `melt`: cashu rows whose builder-side melt failed.
|
|
1286
|
+
*/
|
|
1287
|
+
kind: "settle" | "refund" | "close" | "melt";
|
|
1288
|
+
/** Epoch ms the wedge was first observed. */
|
|
1289
|
+
since: number;
|
|
1290
|
+
/** Retry count where the retry is automatic, else null. */
|
|
1291
|
+
attempts: number | null;
|
|
1292
|
+
/** Epoch ms of the next automatic retry, else null. */
|
|
1293
|
+
nextRetry: number | null;
|
|
1294
|
+
lastError: string | null;
|
|
1295
|
+
/** Rail-native amount held up, when known. */
|
|
1296
|
+
native?: number;
|
|
1297
|
+
}
|
|
1298
|
+
/** One pool inside a rail's pending figure — a mint, a batch, a channel. */
|
|
1299
|
+
interface PayoutPool {
|
|
1300
|
+
id: string;
|
|
1301
|
+
native: number;
|
|
1302
|
+
label?: string;
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* JSON wire shape POSTed to the platform's `/_internal/payout-pending`
|
|
1306
|
+
* endpoint — one rail's snapshot of money still moving toward the builder,
|
|
1307
|
+
* replaced on each report. Best-effort: a lost one is superseded by the next
|
|
1308
|
+
* tick, so it rides no durable queue.
|
|
1309
|
+
*/
|
|
1310
|
+
interface PayoutPendingPayload {
|
|
1311
|
+
dvmId: string;
|
|
1312
|
+
rail: PayoutRail;
|
|
1313
|
+
/** Everything on this rail still moving toward the builder, in atomic units. */
|
|
1314
|
+
poolNative: number;
|
|
1315
|
+
nativeAsset: "sats" | "usdc";
|
|
1316
|
+
/** How many pools the figure is made of. */
|
|
1317
|
+
items: number;
|
|
1318
|
+
pools: PayoutPool[];
|
|
1319
|
+
wedged: PayoutWedge[];
|
|
1320
|
+
/** This DVM's clock when the snapshot was taken (epoch ms). */
|
|
1321
|
+
snapshotAt: number;
|
|
1322
|
+
}
|
|
1323
|
+
/** The delivery seam — implemented by `RevenueReporter`, stubbed in tests. */
|
|
1324
|
+
interface PayoutTransport {
|
|
1325
|
+
/**
|
|
1326
|
+
* Queue a payout through the caller's open transaction, so the row and the
|
|
1327
|
+
* fact it reports commit or roll back together; without `tx` it is queued on
|
|
1328
|
+
* its own. Durable once committed — the retry loop delivers it, or
|
|
1329
|
+
* {@link PayoutTransport.drainPending} when a caller wants it now.
|
|
1330
|
+
*/
|
|
1331
|
+
enqueuePayout(payload: PayoutReportPayload, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1332
|
+
/** Queue a payout on its own and try to deliver it at once. */
|
|
1333
|
+
reportPayout(payload: PayoutReportPayload): Promise<void>;
|
|
1334
|
+
/** Best-effort: one POST, replaced by the next snapshot. */
|
|
1335
|
+
reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
|
|
1336
|
+
/** Deliver what is queued now rather than on the next retry tick. */
|
|
1337
|
+
drainPending?(): Promise<void>;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* A money-path hook whose report must commit with the fact it describes.
|
|
1341
|
+
* `enqueue` runs inside the caller's transaction, and a throw rolls the fact
|
|
1342
|
+
* back with it — the drain report's own discipline (internal-review): a call the
|
|
1343
|
+
* caller has to repeat costs a round trip, where a landed movement with no
|
|
1344
|
+
* row understates paid-out forever. `committed` runs once that transaction
|
|
1345
|
+
* has committed, for the delivery and the snapshot that must not read
|
|
1346
|
+
* uncommitted state.
|
|
1347
|
+
*/
|
|
1348
|
+
interface TransactionalPayoutHook<E> {
|
|
1349
|
+
enqueue(event: E, tx: RevenueReporterQuerier): Promise<void>;
|
|
1350
|
+
committed(): void;
|
|
1351
|
+
}
|
|
1352
|
+
/** What `/admin/cashu/mark-melted` knows once the rows are marked. */
|
|
1353
|
+
interface CashuMeltCompleted {
|
|
1354
|
+
rows: {
|
|
1355
|
+
id: string;
|
|
1356
|
+
mintUrl: string;
|
|
1357
|
+
proofAmount: number;
|
|
1358
|
+
}[];
|
|
1359
|
+
meltQuoteId: string;
|
|
1360
|
+
paymentPreimage: string;
|
|
1361
|
+
}
|
|
1362
|
+
/** What mppx reports after a Tempo channel settlement or close confirmed on chain. */
|
|
1363
|
+
interface TempoSessionSettled {
|
|
1364
|
+
txHash: string;
|
|
1365
|
+
channelId: string;
|
|
1366
|
+
trigger: "settle" | "close" | "scheduled";
|
|
1367
|
+
/** Cumulative amount settled to the payee on this channel, atomic units. */
|
|
1368
|
+
amount: bigint;
|
|
1369
|
+
/** Newly settled to the payee by this transaction, atomic units. */
|
|
1370
|
+
delta: bigint;
|
|
1371
|
+
}
|
|
1372
|
+
/** What `reconcile-tempo-drain` re-read from the close receipt it booked against. */
|
|
1373
|
+
interface TempoCloseReconciled {
|
|
1374
|
+
channelId: string;
|
|
1375
|
+
txHash: string;
|
|
1376
|
+
/** Captured by the payee — this DVM — per the receipt, atomic units as a decimal string. */
|
|
1377
|
+
settledToPayee: string;
|
|
1378
|
+
}
|
|
1379
|
+
/** The channel view the tracker sums claims over. */
|
|
1380
|
+
interface X402TrackedChannel {
|
|
1381
|
+
channelId: string;
|
|
1382
|
+
chargedCumulativeAmount: string;
|
|
1383
|
+
totalClaimed: string;
|
|
1384
|
+
}
|
|
1385
|
+
/** A refund settlement wedged between chain and ledger (internal-review), for the wedge list. */
|
|
1386
|
+
interface X402WedgedRefund {
|
|
1387
|
+
settlementId: string;
|
|
1388
|
+
/** Epoch ms the settlement was first prepared. */
|
|
1389
|
+
createdAt: number;
|
|
1390
|
+
native?: number;
|
|
1391
|
+
}
|
|
1392
|
+
/** What the batch-settlement server hands the tracker once it knows its scope. */
|
|
1393
|
+
interface X402PayoutContext {
|
|
1394
|
+
/** `${network}|${payTo}|${token}` — the scope the settle-pending marker is keyed on. */
|
|
1395
|
+
scope: string;
|
|
1396
|
+
payTo: string;
|
|
1397
|
+
network: string;
|
|
1398
|
+
storage: {
|
|
1399
|
+
list(): Promise<X402TrackedChannel[]>;
|
|
1400
|
+
};
|
|
1401
|
+
/** The scheduler's settle cadence — what a wedge's `nextRetry` is derived from. */
|
|
1402
|
+
settleIntervalMs: number;
|
|
1403
|
+
listWedgedRefunds?: () => Promise<X402WedgedRefund[]>;
|
|
1404
|
+
}
|
|
1405
|
+
/** The two manager verbs the tracker wraps. */
|
|
1406
|
+
interface X402TrackedManager {
|
|
1407
|
+
claim(...args: never[]): Promise<{
|
|
1408
|
+
vouchers: number;
|
|
1409
|
+
transaction: string;
|
|
1410
|
+
}[]>;
|
|
1411
|
+
settle(): Promise<{
|
|
1412
|
+
transaction: string;
|
|
1413
|
+
}>;
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* The batch-settlement server's view of the reporter: attach once with the
|
|
1417
|
+
* scope, then route every claim and settle through the tracked manager so
|
|
1418
|
+
* the open batch is kept and the settle emits the payout.
|
|
1419
|
+
*/
|
|
1420
|
+
interface X402PayoutObserver {
|
|
1421
|
+
attach(ctx: X402PayoutContext): void;
|
|
1422
|
+
trackManager<M extends X402TrackedManager>(manager: M): M;
|
|
1423
|
+
/** A settle that landed outside the tracked manager — the manual claim-and-settle's own retry loop. */
|
|
1424
|
+
recordSettle(transaction: string): Promise<void>;
|
|
1425
|
+
/** A settle attempt that failed outside the tracked manager. One call per attempt a builder would count as one. */
|
|
1426
|
+
recordSettleFailure(error: unknown): Promise<void>;
|
|
1427
|
+
}
|
|
1428
|
+
/** The Tempo readers the host attaches after mounting. */
|
|
1429
|
+
interface TempoPayoutReader {
|
|
1430
|
+
/** The recipient address every Tempo payout names. */
|
|
1431
|
+
recipient?: string;
|
|
1432
|
+
/** CAIP-2 network for the refs, when known. */
|
|
1433
|
+
network?: string;
|
|
1434
|
+
listActive?: (limit: number, cursor?: {
|
|
1435
|
+
updatedAt: number;
|
|
1436
|
+
key: string;
|
|
1437
|
+
}) => Promise<{
|
|
1438
|
+
key: string;
|
|
1439
|
+
state: Record<string, unknown>;
|
|
1440
|
+
updatedAt: number;
|
|
1441
|
+
}[]>;
|
|
1442
|
+
listChannelDrains?: (args: {
|
|
1443
|
+
limit: number;
|
|
1444
|
+
after?: ChannelDrainCursor;
|
|
1445
|
+
createdBeforeMs: number;
|
|
1446
|
+
rail: "tempo";
|
|
1447
|
+
}) => Promise<CreditDrainRecord[]>;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* The claims gathered since the last settle — the batch the next settle pays
|
|
1451
|
+
* out. Durable across machines because claims run under the fleet lock on
|
|
1452
|
+
* whichever machine won the tick, and the settle that finally moves the money
|
|
1453
|
+
* may run on another: an in-process figure would report a partial amount
|
|
1454
|
+
* after any restart in between.
|
|
1455
|
+
*/
|
|
1456
|
+
interface X402OpenBatch {
|
|
1457
|
+
scope: string;
|
|
1458
|
+
batchId: string;
|
|
1459
|
+
claimedNative: bigint;
|
|
1460
|
+
voucherCount: number;
|
|
1461
|
+
claims: number;
|
|
1462
|
+
openedAt: number;
|
|
1463
|
+
attempts: number;
|
|
1464
|
+
firstFailedAt: number | null;
|
|
1465
|
+
lastError: string | null;
|
|
1466
|
+
nextRetryAt: number | null;
|
|
1467
|
+
}
|
|
1468
|
+
/** Where the open batch lives — Postgres beside the settlement rows, or memory in dev. */
|
|
1469
|
+
interface X402BatchStore {
|
|
1470
|
+
get(scope: string): Promise<X402OpenBatch | undefined>;
|
|
1471
|
+
put(batch: X402OpenBatch): Promise<void>;
|
|
1472
|
+
delete(scope: string): Promise<void>;
|
|
1473
|
+
/**
|
|
1474
|
+
* Close the open batch and run `enqueue` in the same transaction, so the
|
|
1475
|
+
* payout row is queued exactly when the batch is gone and never otherwise:
|
|
1476
|
+
* a settle can never lose its payout, because a failed enqueue leaves the
|
|
1477
|
+
* batch open for the next settle to report in full. `false` when the batch
|
|
1478
|
+
* under `scope` is no longer `batchId` — a sibling settled it first — in
|
|
1479
|
+
* which case nothing is queued.
|
|
1480
|
+
*/
|
|
1481
|
+
settle(scope: string, batchId: string, enqueue: (tx?: RevenueReporterQuerier) => Promise<void>): Promise<boolean>;
|
|
1482
|
+
}
|
|
1483
|
+
/** Construction options for {@link PayoutReporter}. */
|
|
1484
|
+
interface PayoutReporterOpts {
|
|
1485
|
+
/** Platform DVM record ID, bound into every report. */
|
|
1486
|
+
dvmId: string;
|
|
1487
|
+
transport: PayoutTransport;
|
|
1488
|
+
/**
|
|
1489
|
+
* The SDK's Postgres. Reads the cashu accumulator for the pending pool and
|
|
1490
|
+
* holds the open x402 batch; without it the batch lives in memory (dev) and
|
|
1491
|
+
* no cashu snapshot is taken.
|
|
1492
|
+
*/
|
|
1493
|
+
db?: Pool;
|
|
1494
|
+
/**
|
|
1495
|
+
* Whether this DVM runs the cashu accumulator. The cashu snapshot reads
|
|
1496
|
+
* `wallet_accumulator`, which only that mode creates — on any other DVM the
|
|
1497
|
+
* read would fail every tick and warn about a rail it does not carry.
|
|
1498
|
+
*/
|
|
1499
|
+
cashu?: boolean;
|
|
1500
|
+
/** Override the batch store (tests); defaults to Postgres on `db`, memory without. */
|
|
1501
|
+
batchStore?: X402BatchStore;
|
|
1502
|
+
/** Snapshot cadence, ms. Default 5 minutes. */
|
|
1503
|
+
snapshotIntervalMs?: number;
|
|
1504
|
+
/** Override for the current time (tests). */
|
|
1505
|
+
now?: () => number;
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Builds the payout and pending reports from the SDK's money paths and hands
|
|
1509
|
+
* them to the transport.
|
|
1510
|
+
*
|
|
1511
|
+
* Two entry points are transactional: `cashuMelted` and `tempoCloseReconciled`
|
|
1512
|
+
* queue their row inside the caller's transaction and throw when they cannot,
|
|
1513
|
+
* so the melt or the reconciled close rolls back with its missing report rather
|
|
1514
|
+
* than landing without one. The x402 settle and the pending snapshots are
|
|
1515
|
+
* fail-safe instead: a report that cannot be built or queued there is logged and
|
|
1516
|
+
* picked up on the next tick, never thrown into the settle that produced it.
|
|
1517
|
+
*/
|
|
1518
|
+
declare class PayoutReporter implements PayoutTransport {
|
|
1519
|
+
private readonly dvmId;
|
|
1520
|
+
private readonly transport;
|
|
1521
|
+
private readonly db?;
|
|
1522
|
+
private readonly cashu;
|
|
1523
|
+
private readonly batches;
|
|
1524
|
+
private readonly ownsBatchStore;
|
|
1525
|
+
private readonly intervalMs;
|
|
1526
|
+
private readonly now;
|
|
1527
|
+
private x402?;
|
|
1528
|
+
private tempo?;
|
|
1529
|
+
private timer;
|
|
1530
|
+
private warmup;
|
|
1531
|
+
constructor(opts: PayoutReporterOpts);
|
|
1532
|
+
/** Boot DDL for the durable batch store, when this reporter owns one. */
|
|
1533
|
+
init(): Promise<void>;
|
|
1534
|
+
/**
|
|
1535
|
+
* Start the snapshot loop. Unref'd, so it never keeps a scale-to-zero
|
|
1536
|
+
* machine awake; a first snapshot runs shortly after boot so a fresh process
|
|
1537
|
+
* reports without waiting a whole interval.
|
|
1538
|
+
*/
|
|
1539
|
+
start(): void;
|
|
1540
|
+
stop(): void;
|
|
1541
|
+
enqueuePayout(payload: PayoutReportPayload, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1542
|
+
reportPayout(payload: PayoutReportPayload): Promise<void>;
|
|
1543
|
+
reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
|
|
1544
|
+
/** Deliver what is queued now; a transport without the seam waits for its retry loop. */
|
|
1545
|
+
flush(): Promise<void>;
|
|
1546
|
+
/** The hook `mark-melted` runs inside its transaction: the mark and its payout commit together. */
|
|
1547
|
+
meltHook(): TransactionalPayoutHook<CashuMeltCompleted>;
|
|
1548
|
+
/**
|
|
1549
|
+
* A melt completed: the accumulator rows are SPENT at the mint and the
|
|
1550
|
+
* Lightning payment reached the builder's destination. One payout per mint
|
|
1551
|
+
* in the call — a `mark-melted` normally names one, since a melt quote
|
|
1552
|
+
* belongs to one mint. The amount is the face value melted; the mint's fee
|
|
1553
|
+
* and the Lightning amount received are builder-machine facts this DVM does
|
|
1554
|
+
* not see.
|
|
1555
|
+
*
|
|
1556
|
+
* Queued through `tx` when the caller is inside the transaction that marks
|
|
1557
|
+
* the rows, and a failure is thrown rather than swallowed so that mark rolls
|
|
1558
|
+
* back with it — see {@link TransactionalPayoutHook}. Without `tx` the rows
|
|
1559
|
+
* are queued on their own and delivered at once.
|
|
1560
|
+
*/
|
|
1561
|
+
cashuMelted(event: CashuMeltCompleted, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1562
|
+
/** The host attaches the session store and ledger readers once they exist. */
|
|
1563
|
+
attachTempo(reader: TempoPayoutReader): void;
|
|
1564
|
+
/**
|
|
1565
|
+
* A Tempo channel settled or closed on chain. `delta` is what this
|
|
1566
|
+
* transaction newly paid the recipient; a transaction that paid nothing new
|
|
1567
|
+
* (a close of a fully settled channel) is no payout.
|
|
1568
|
+
*/
|
|
1569
|
+
tempoSettled(event: TempoSessionSettled): Promise<void>;
|
|
1570
|
+
/** The hook `reconcile-tempo-drain` runs inside its transaction: the booking and its payout commit together. */
|
|
1571
|
+
tempoRepairHook(): TransactionalPayoutHook<TempoCloseReconciled>;
|
|
1572
|
+
/**
|
|
1573
|
+
* The operator repaired a wedged cooperative close (internal-review). The close's
|
|
1574
|
+
* payee side landed on chain when the close did; if the live hook reported
|
|
1575
|
+
* it, this upgrades that row to `repair`, otherwise it is the row. The
|
|
1576
|
+
* figure is the receipt's payee total for the channel.
|
|
1577
|
+
*
|
|
1578
|
+
* Queued through `tx` when the caller is inside the transaction that books
|
|
1579
|
+
* the drain, and thrown rather than swallowed so that booking rolls back
|
|
1580
|
+
* with it — see {@link TransactionalPayoutHook}.
|
|
1581
|
+
*/
|
|
1582
|
+
tempoCloseReconciled(event: TempoCloseReconciled, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1583
|
+
/** The observer the batch-settlement server attaches to and routes its manager through. */
|
|
1584
|
+
x402Observer(): X402PayoutObserver;
|
|
1585
|
+
/**
|
|
1586
|
+
* Wrap the upstream channel manager so every claim grows the open batch and
|
|
1587
|
+
* every settle closes it. The claim delta is read off storage rather than
|
|
1588
|
+
* off upstream's result, which carries only a voucher count; under the
|
|
1589
|
+
* fleet lock the before/after read is consistent. A channel the claim
|
|
1590
|
+
* removed on its way through — a refund's claim-then-delete (internal-review) —
|
|
1591
|
+
* moved whatever it still owed before it went, and that value reaches
|
|
1592
|
+
* `pay_to` in this batch too, so it counts at its pre-claim figure.
|
|
1593
|
+
*/
|
|
1594
|
+
private trackX402Manager;
|
|
1595
|
+
private recordX402Claim;
|
|
1596
|
+
private recordX402Settle;
|
|
1597
|
+
private recordX402SettleFailure;
|
|
1598
|
+
/** Every rail this reporter can read, each on its own failure boundary. */
|
|
1599
|
+
snapshot(): Promise<void>;
|
|
1600
|
+
/**
|
|
1601
|
+
* Cashu: every unmelted proof in the accumulator, per mint. Rows whose
|
|
1602
|
+
* builder-side melt failed are still in the pool (they are still at the
|
|
1603
|
+
* mint) and listed as wedged until `restart-failed` clears them.
|
|
1604
|
+
*/
|
|
1605
|
+
snapshotCashu(): Promise<void>;
|
|
1606
|
+
/**
|
|
1607
|
+
* x402: the open batch (claimed, not yet transferred) plus every channel's
|
|
1608
|
+
* unclaimed voucher value; wedged when the settle keeps failing, and the
|
|
1609
|
+
* refund settlements stuck between chain and ledger beside it.
|
|
1610
|
+
*/
|
|
1611
|
+
snapshotX402(): Promise<void>;
|
|
1612
|
+
/**
|
|
1613
|
+
* Tempo: each active channel's spent-but-unsettled balance — earned, not
|
|
1614
|
+
* yet paid to the recipient — and the cooperative closes wedged between
|
|
1615
|
+
* chain and ledger (internal-review), aged past the same floor the repair queue
|
|
1616
|
+
* uses so an in-flight close is not reported as stuck.
|
|
1617
|
+
*/
|
|
1618
|
+
snapshotTempo(): Promise<void>;
|
|
1619
|
+
}
|
|
780
1620
|
|
|
1621
|
+
/**
|
|
1622
|
+
* JSON wire shape POSTed to the platform's `/_internal/job-revenue` endpoint
|
|
1623
|
+
* and stored in `pending_revenue_reports.payload` for durable retry.
|
|
1624
|
+
*
|
|
1625
|
+
* The platform-side consumer is `OnJobCompleted`, implemented by
|
|
1626
|
+
* `createRevenueCallback`. Keep the two shapes in sync: the SDK and platform
|
|
1627
|
+
* deploy in lockstep, and any
|
|
1628
|
+
* backwards-incompatible field change requires a coordinated release. No `version`
|
|
1629
|
+
* field is included pre-launch because there are no external container-runtime
|
|
1630
|
+
* consumers yet; add one once the first third-party builder ships a container-runtime
|
|
1631
|
+
* DVM (trigger condition: external builder onboarded via `dvmctl deploy --container`).
|
|
1632
|
+
*/
|
|
1633
|
+
interface RevenueReportPayload {
|
|
1634
|
+
/** Required. Platform DVM record ID (`dvms.id`). Identifies which DVM earned the revenue. */
|
|
1635
|
+
dvmId: string;
|
|
1636
|
+
/** Required. Job identifier used by the platform to de-duplicate revenue rows and link to the job record. */
|
|
1637
|
+
jobId: string;
|
|
1638
|
+
/** Required. Total amount paid by the caller in millisatoshis, across all credits that satisfied the job. */
|
|
1639
|
+
paidMsats: number;
|
|
1640
|
+
/** Optional. Cashu mint URL from which the payment tokens were issued. Set only for the `cashu` rail; omitted for all other rails. */
|
|
1641
|
+
paymentMint?: string;
|
|
1642
|
+
/**
|
|
1643
|
+
* Required. Payment rail identifier (`"cashu"`, `"tempo"`, `"x402"`, `"stripe"`).
|
|
1644
|
+
* Mirrors `PaymentMethod` on the platform; carried as a plain string over the wire
|
|
1645
|
+
* so the SDK has no platform-type dependency.
|
|
1646
|
+
*/
|
|
1647
|
+
rail: string;
|
|
1648
|
+
/**
|
|
1649
|
+
* Optional. Settlement reference for the credit: EVM tx hash for `x402`, credential
|
|
1650
|
+
* challenge ID for `tempo`, `X-Cashu-Request-Id` UUID for `cashu`. Required by the
|
|
1651
|
+
* platform revenue ledger for `tempo`/`x402` rails (recordJobRevenue throws without it);
|
|
1652
|
+
* nullable for `cashu`.
|
|
1653
|
+
*/
|
|
1654
|
+
paymentTxHash?: string;
|
|
1655
|
+
/**
|
|
1656
|
+
* Optional. Rail-native payment amount in atomic units: satoshis for `tempo`, USDC
|
|
1657
|
+
* microunits for `x402`, USD cents for `stripe`. Omitted for `cashu` (platform derives
|
|
1658
|
+
* `paidMsats / 1000`). Must be set together with `nativeAsset`.
|
|
1659
|
+
*/
|
|
1660
|
+
nativeAmount?: number;
|
|
1661
|
+
/**
|
|
1662
|
+
* Optional. Asset tag paired with `nativeAmount`: `"sats"`, `"usdc"`, `"usdc.e"`, or
|
|
1663
|
+
* `"usd-cents"`. Both fields must be present for the platform to write a non-cashu
|
|
1664
|
+
* revenue row.
|
|
1665
|
+
*/
|
|
1666
|
+
nativeAsset?: string;
|
|
1667
|
+
/**
|
|
1668
|
+
* Optional. Cashu flow discriminator. Set to `"p2pk_accumulator"` when the cashu
|
|
1669
|
+
* accumulator path (internal-review) satisfied the job; omitted for legacy cashu. Written into
|
|
1670
|
+
* `revenue_events.metadata.cashu_flow` by the platform so ops can split per-call cashu
|
|
1671
|
+
* rows by source.
|
|
1672
|
+
*/
|
|
1673
|
+
cashuFlow?: string;
|
|
1674
|
+
/**
|
|
1675
|
+
* Optional. Credit this job's payment drew against (internal-review). Present on
|
|
1676
|
+
* every job a ledger-backed payment satisfied; its presence is what tells
|
|
1677
|
+
* the platform the row is a **draw-keyed** revenue event rather than a
|
|
1678
|
+
* pre-credits per-call one.
|
|
1679
|
+
*/
|
|
1680
|
+
creditId?: string;
|
|
1681
|
+
/** Optional. The draw this job settled — one draw, one revenue row. */
|
|
1682
|
+
drawId?: string;
|
|
1683
|
+
/** Optional. The draw's fiat amount, 1e-6 of `creditCurrency`. The exact liability offset. */
|
|
1684
|
+
drawAmountMicro?: number;
|
|
1685
|
+
/** Optional. Currency the credit is denominated in (lowercase ISO, e.g. `usd`). */
|
|
1686
|
+
creditCurrency?: string;
|
|
1687
|
+
/**
|
|
1688
|
+
* Optional. Settlement instant (epoch ms) — draws are revenue *when they
|
|
1689
|
+
* settle*, so this dates the row. Omitted for pre-credits rows, which the
|
|
1690
|
+
* platform dates at record time as before.
|
|
1691
|
+
*/
|
|
1692
|
+
settledAt?: number;
|
|
1693
|
+
/** Optional. The funding's own rail reference, for reconciling a draw back to its deposit. */
|
|
1694
|
+
fundingRef?: string;
|
|
1695
|
+
/**
|
|
1696
|
+
* Optional. Revenue class when this row is not ordinary service revenue.
|
|
1697
|
+
* `short_pay_forfeit` marks money kept from an underpayment that bought no
|
|
1698
|
+
* job (internal-review operator ruling) so analytics can exclude it.
|
|
1699
|
+
*/
|
|
1700
|
+
kind?: string;
|
|
1701
|
+
}
|
|
781
1702
|
/**
|
|
782
1703
|
* JSON wire shape POSTed to the platform's `/_internal/credit-deposit`
|
|
783
1704
|
* endpoint (internal-review). A funding event is a **deposit** — a liability until
|
|
@@ -855,6 +1776,14 @@ interface CreditDepositPayload {
|
|
|
855
1776
|
/** 32-byte channel id, `0x`-prefixed and lowercased. */
|
|
856
1777
|
channelId: string;
|
|
857
1778
|
};
|
|
1779
|
+
/**
|
|
1780
|
+
* Set by the blocked-invoice repair (internal-review): this deposit is a paid
|
|
1781
|
+
* Lightning invoice the builder's agent reconciled onto a credit. The money
|
|
1782
|
+
* landed in the receive wallet at payment time and this deposit is the one
|
|
1783
|
+
* row it gets, so the settlements table types it `repair` off this flag
|
|
1784
|
+
* rather than off a second row.
|
|
1785
|
+
*/
|
|
1786
|
+
repaired?: boolean;
|
|
858
1787
|
}
|
|
859
1788
|
/**
|
|
860
1789
|
* JSON wire shape POSTed to the platform's `/_internal/credit-draw-release`
|
|
@@ -944,6 +1873,94 @@ type CreditDrawReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditDraw
|
|
|
944
1873
|
* it before committing the status CAS that moved the money.
|
|
945
1874
|
*/
|
|
946
1875
|
type CreditDrainEnqueue = (tx: RevenueReporterQuerier, payload: CreditDrainPayload) => Promise<void>;
|
|
1876
|
+
/**
|
|
1877
|
+
* JSON wire shape POSTed to the platform's `/_internal/credit-expiry-release`
|
|
1878
|
+
* endpoint (internal-review). A prepaid credit that reaches its TTL with an undrawn
|
|
1879
|
+
* remainder RELEASES that remainder to the builder: on cashu, Lightning and
|
|
1880
|
+
* x402 the funding value reached builder custody at funding time or on the
|
|
1881
|
+
* settlement cycle, so expiry extinguishes the caller's remaining claim.
|
|
1882
|
+
* Reported so committed value nets to
|
|
1883
|
+
* `deposited − drawn − drained − released`; without it an expired credit is
|
|
1884
|
+
* owed forever on paper, exactly like an unreported drain.
|
|
1885
|
+
*
|
|
1886
|
+
* Never sent for a Tempo credit. Tempo balances are channel-backed and undrawn
|
|
1887
|
+
* channel value returns to the *caller* at exit, so no release exists on that
|
|
1888
|
+
* rail — the sweep skips them and the platform refuses one outright.
|
|
1889
|
+
*
|
|
1890
|
+
* Two reports share this shape, distinguished only by `reversedAt`:
|
|
1891
|
+
*
|
|
1892
|
+
* - **The release**, written by the expiry sweep with `reversedAt` absent.
|
|
1893
|
+
* - **The reversal**, written when a later funding revives the credit
|
|
1894
|
+
* (`CreditLedger.fund` overwrites `expiry_ms`, so the recorded remainder is
|
|
1895
|
+
* no longer final). It repeats the whole payload rather than sending a
|
|
1896
|
+
* reference, because the retry loop can deliver the two out of order and the
|
|
1897
|
+
* platform's upsert must be able to land either one first.
|
|
1898
|
+
*
|
|
1899
|
+
* Idempotent on `(dvmId, creditId, releaseId)`, so the shared retry loop can
|
|
1900
|
+
* redeliver freely.
|
|
1901
|
+
*/
|
|
1902
|
+
interface CreditExpiryReleasePayload {
|
|
1903
|
+
/** Required. Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
|
|
1904
|
+
dvmId: string;
|
|
1905
|
+
/** Required. Credit whose remainder expired. */
|
|
1906
|
+
creditId: string;
|
|
1907
|
+
/** Required. Per-expiry-episode key; the platform idempotency key with DVM and credit. */
|
|
1908
|
+
releaseId: string;
|
|
1909
|
+
/** Required. Caller whose claim on the remainder ended. */
|
|
1910
|
+
callerPubkey: string;
|
|
1911
|
+
/** Funding rail. `null` on a credit funded before the basis became mandatory. */
|
|
1912
|
+
rail: string | null;
|
|
1913
|
+
/** Required. Fiat micro released — the liability this expiry extinguishes. */
|
|
1914
|
+
amountMicro: number;
|
|
1915
|
+
/** Required. Currency `amountMicro` is 1e-6 of. */
|
|
1916
|
+
creditCurrency: string;
|
|
1917
|
+
/** Required. When the credit's TTL ran out (epoch ms) — when the liability ended. */
|
|
1918
|
+
expiryMs: number;
|
|
1919
|
+
/** Required. When the sweep observed the expiry (epoch ms). */
|
|
1920
|
+
releasedAt: number;
|
|
1921
|
+
/** Set only on the reversal report: when the revival funding landed (epoch ms). */
|
|
1922
|
+
reversedAt?: number | null;
|
|
1923
|
+
}
|
|
1924
|
+
/**
|
|
1925
|
+
* Transactional expiry-release outbox seam, threaded through the sweep's insert
|
|
1926
|
+
* and through the funding that reverses a release (internal-review).
|
|
1927
|
+
*
|
|
1928
|
+
* Implemented by {@link RevenueReporter.enqueueCreditExpiryRelease}; callers
|
|
1929
|
+
* must await it before committing the write that made the release (or its
|
|
1930
|
+
* reversal) true, for the same reason the drain seam does — a report lost
|
|
1931
|
+
* between the two overstates or understates committed value forever.
|
|
1932
|
+
*/
|
|
1933
|
+
type CreditExpiryReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditExpiryReleasePayload) => Promise<void>;
|
|
1934
|
+
/**
|
|
1935
|
+
* JSON wire shape POSTed to the platform's `/_internal/paid-job-death` endpoint
|
|
1936
|
+
* (internal-review) when the stale-job reaper force-fails a *paid* job — its pending
|
|
1937
|
+
* credit draw is released rather than settled, so the caller's payment is back
|
|
1938
|
+
* on their credit balance and reclaimable by drain. The platform resolves fiat
|
|
1939
|
+
* + owner and sends an ambient notice via the shared alert sink. Unlike
|
|
1940
|
+
* {@link RevenueReportPayload} this carries no durable-retry semantics: the
|
|
1941
|
+
* reaper's `stale_job_failed` structured log is the durable backstop, so a lost
|
|
1942
|
+
* report degrades to log-only rather than blocking the sweep.
|
|
1943
|
+
*/
|
|
1944
|
+
interface PaidJobDeathPayload {
|
|
1945
|
+
/** Required. Platform DVM record ID (`dvms.id`). Bound to the authenticating token. */
|
|
1946
|
+
dvmId: string;
|
|
1947
|
+
/** Required. Job identifier the reaper force-failed. */
|
|
1948
|
+
jobId: string;
|
|
1949
|
+
/** Required. Capability the job dispatched to (e.g. `"add-episode"`). */
|
|
1950
|
+
capability: string;
|
|
1951
|
+
/** Required. Amount of the released credit draw in millisatoshis. */
|
|
1952
|
+
paidMsats: number;
|
|
1953
|
+
/** Required. Payment rail of the credit draw (`"cashu"` / `"tempo"` / `"x402"`). */
|
|
1954
|
+
rail: string;
|
|
1955
|
+
/** Required. Terminal reason the reaper set: dead worker vs never-terminal idle. */
|
|
1956
|
+
reason: string;
|
|
1957
|
+
/** Optional. Rail-native released amount, paired with `nativeAsset`. */
|
|
1958
|
+
nativeAmount?: number;
|
|
1959
|
+
/** Optional. Asset tag for `nativeAmount` (`"sats"` / `"usdc"` / …). */
|
|
1960
|
+
nativeAsset?: string;
|
|
1961
|
+
/** Optional. Cashu mint URL the credit draw's tokens were issued from. */
|
|
1962
|
+
paymentMint?: string;
|
|
1963
|
+
}
|
|
947
1964
|
/** Distinct causes for a settled credit draw that could not book revenue. */
|
|
948
1965
|
type RevenueSkippedNoRailReason = "credit_rail_null" | "credit_rail_unrecognized" | "job_rail_missing" | "ledger_not_configured";
|
|
949
1966
|
/**
|
|
@@ -973,6 +1990,210 @@ interface RevenueSkippedNoRailPayload {
|
|
|
973
1990
|
/** Required classification of why the revenue rail was unavailable. */
|
|
974
1991
|
reason: RevenueSkippedNoRailReason;
|
|
975
1992
|
}
|
|
1993
|
+
/** Gas-balance observation POSTed by a platform-hosted x402 self-relay. */
|
|
1994
|
+
interface X402SelfRelayGasBalancePayload {
|
|
1995
|
+
/** Platform DVM record ID, bound to the authenticating platform token. */
|
|
1996
|
+
dvmId: string;
|
|
1997
|
+
/** Dedicated relay EOA whose Base ETH balance was read. */
|
|
1998
|
+
address: `0x${string}`;
|
|
1999
|
+
/** CAIP-2 Base network identifier. */
|
|
2000
|
+
network: string;
|
|
2001
|
+
/** Current native balance as a decimal wei string. */
|
|
2002
|
+
balanceWei: string;
|
|
2003
|
+
/** Configured paging floor as a decimal wei string. */
|
|
2004
|
+
lowBalanceWei: string;
|
|
2005
|
+
/** Millisecond timestamp at which the DVM read the balance. */
|
|
2006
|
+
checkedAt: number;
|
|
2007
|
+
}
|
|
2008
|
+
/** RPC-health transition POSTed by a platform-hosted x402 self-relay. */
|
|
2009
|
+
type X402SelfRelayRpcHealthPayload = {
|
|
2010
|
+
/** Platform DVM record ID, bound to the authenticating platform token. */
|
|
2011
|
+
dvmId: string;
|
|
2012
|
+
/** Dedicated relay EOA identifying the affected self-relay. */
|
|
2013
|
+
address: `0x${string}`;
|
|
2014
|
+
/** CAIP-2 Base network identifier. */
|
|
2015
|
+
network: string;
|
|
2016
|
+
/** Healthy transition clears an open RPC page. */
|
|
2017
|
+
status: "healthy";
|
|
2018
|
+
/** Millisecond timestamp at which the DVM observed the transition. */
|
|
2019
|
+
checkedAt: number;
|
|
2020
|
+
} | {
|
|
2021
|
+
/** Platform DVM record ID, bound to the authenticating platform token. */
|
|
2022
|
+
dvmId: string;
|
|
2023
|
+
/** Dedicated relay EOA identifying the affected self-relay. */
|
|
2024
|
+
address: `0x${string}`;
|
|
2025
|
+
/** CAIP-2 Base network identifier. */
|
|
2026
|
+
network: string;
|
|
2027
|
+
/** Failed transition opens or refreshes an RPC page. */
|
|
2028
|
+
status: "failed";
|
|
2029
|
+
/** Redacted failure class; never the provider error or endpoint URL. */
|
|
2030
|
+
reason: X402SelfRelayRpcFailureReason;
|
|
2031
|
+
/** Millisecond timestamp at which the DVM observed the transition. */
|
|
2032
|
+
checkedAt: number;
|
|
2033
|
+
};
|
|
2034
|
+
/** Configured Tempo operator fee-token balance POSTed by a hosted DVM. */
|
|
2035
|
+
interface TempoSettlementBalancePayload {
|
|
2036
|
+
dvmId: string;
|
|
2037
|
+
address: `0x${string}`;
|
|
2038
|
+
network: string;
|
|
2039
|
+
token: `0x${string}`;
|
|
2040
|
+
balanceMicro: string;
|
|
2041
|
+
lowBalanceMicro: string;
|
|
2042
|
+
checkedAt: number;
|
|
2043
|
+
ready: boolean;
|
|
2044
|
+
}
|
|
2045
|
+
/** Redacted Tempo operator fee-funding rejection POSTed by a hosted DVM. */
|
|
2046
|
+
interface TempoSettlementFailurePayload {
|
|
2047
|
+
dvmId: string;
|
|
2048
|
+
address: `0x${string}`;
|
|
2049
|
+
network: string;
|
|
2050
|
+
token: `0x${string}`;
|
|
2051
|
+
operation: "settle" | "close";
|
|
2052
|
+
errorClass: "InsufficientFundsError";
|
|
2053
|
+
checkedAt: number;
|
|
2054
|
+
balanceMicro?: string;
|
|
2055
|
+
channelId?: string;
|
|
2056
|
+
creditId?: string;
|
|
2057
|
+
drainId?: string;
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* Durable revenue reporter for container-runtime DVMs (internal-review).
|
|
2061
|
+
*
|
|
2062
|
+
* On job completion the SDK calls `report()`, which persists the payload to
|
|
2063
|
+
* a local Postgres table and attempts an immediate POST to the platform.
|
|
2064
|
+
* A background retry loop picks up un-acked rows with exponential backoff.
|
|
2065
|
+
*/
|
|
2066
|
+
declare class RevenueReporter {
|
|
2067
|
+
private readonly db;
|
|
2068
|
+
private readonly platformUrl;
|
|
2069
|
+
private readonly platformToken;
|
|
2070
|
+
private retryTimer;
|
|
2071
|
+
private static readonly RETRY_INTERVAL_MS;
|
|
2072
|
+
private static readonly MAX_BACKOFF_MS;
|
|
2073
|
+
private static readonly ABANDON_AFTER_MS;
|
|
2074
|
+
constructor(db: Pool, platformUrl: string, platformToken: string);
|
|
2075
|
+
/** Create the pending-reports table (idempotent). */
|
|
2076
|
+
init(): Promise<void>;
|
|
2077
|
+
/** The boot DDL itself — always runs under {@link withSdkInitLock} (internal-review). */
|
|
2078
|
+
private createTables;
|
|
2079
|
+
/** Persist a revenue report and attempt immediate delivery. */
|
|
2080
|
+
report(payload: RevenueReportPayload): Promise<void>;
|
|
2081
|
+
/**
|
|
2082
|
+
* Join a credit deposit to the caller-owned rail transaction.
|
|
2083
|
+
*
|
|
2084
|
+
* This method performs no network I/O: it only writes the reporter-owned
|
|
2085
|
+
* durable queue row through `tx`. The retry drain delivers the row after the
|
|
2086
|
+
* rail transaction commits, including after a process restart.
|
|
2087
|
+
*/
|
|
2088
|
+
enqueueDeposit(tx: RevenueReporterQuerier, payload: CreditDepositPayload): Promise<void>;
|
|
2089
|
+
/** Join a released draw to the transaction that made the hold terminal. */
|
|
2090
|
+
enqueueCreditDrawRelease(tx: RevenueReporterQuerier, payload: CreditDrawReleasePayload): Promise<void>;
|
|
2091
|
+
/**
|
|
2092
|
+
* Join a credit drain to the transaction that made it terminal (internal-review).
|
|
2093
|
+
*
|
|
2094
|
+
* Same discipline as {@link enqueueDeposit} and the same reason: no network
|
|
2095
|
+
* I/O here, only the durable queue row, written through `tx` so the report
|
|
2096
|
+
* and the status CAS commit or roll back together. A drain whose row was
|
|
2097
|
+
* lost between the two would overstate outstanding liability forever —
|
|
2098
|
+
* the mirror image of a lost deposit.
|
|
2099
|
+
*
|
|
2100
|
+
* Named for the payload rather than the verb because `drain` already means
|
|
2101
|
+
* "flush the queue" in this class (see {@link drainPending}).
|
|
2102
|
+
*/
|
|
2103
|
+
enqueueCreditDrain(tx: RevenueReporterQuerier, payload: CreditDrainPayload): Promise<void>;
|
|
2104
|
+
/**
|
|
2105
|
+
* Join a payout to the transaction that commits the movement it reports
|
|
2106
|
+
* (internal-review) — the batch close, the accumulator mark, the drain booking.
|
|
2107
|
+
* No network I/O, only the durable queue row through `tx`, the discipline
|
|
2108
|
+
* {@link enqueueCreditDrain} set and for the same reason: the money has
|
|
2109
|
+
* already moved on a rail this ledger does not own, so the platform row is
|
|
2110
|
+
* the only record the builder's dashboard has of it, and a row lost between
|
|
2111
|
+
* the fact and the queue would understate "paid out" forever. Without `tx`
|
|
2112
|
+
* the row is queued on the pool, for callers with no fact of their own to
|
|
2113
|
+
* commit. Idempotent on `(dvmId, payoutId)` platform-side, so the retry
|
|
2114
|
+
* loop redelivers freely.
|
|
2115
|
+
*/
|
|
2116
|
+
enqueuePayout(payload: PayoutReportPayload, tx?: RevenueReporterQuerier): Promise<void>;
|
|
2117
|
+
/**
|
|
2118
|
+
* Report one landed payout on its own and try to deliver it at once — the
|
|
2119
|
+
* live Tempo settlement, which has no dvmkit transaction to join. Durable
|
|
2120
|
+
* like {@link report}.
|
|
2121
|
+
*/
|
|
2122
|
+
reportPayout(payload: PayoutReportPayload): Promise<void>;
|
|
2123
|
+
/**
|
|
2124
|
+
* Report one rail's pending-pool snapshot (internal-review). Best-effort and
|
|
2125
|
+
* fire-and-forget, deliberately unlike {@link reportPayout}: a snapshot is
|
|
2126
|
+
* replaced by the next tick, so queueing a stale one behind an outage would
|
|
2127
|
+
* only deliver figures the platform already has newer ones for.
|
|
2128
|
+
*/
|
|
2129
|
+
reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
|
|
2130
|
+
/**
|
|
2131
|
+
* Join a credit-expiry release — or the revival that reverses one — to the
|
|
2132
|
+
* transaction that made it true (internal-review).
|
|
2133
|
+
*
|
|
2134
|
+
* Same discipline as {@link enqueueCreditDrain}: no network I/O, only the
|
|
2135
|
+
* durable queue row written through `tx`. A release whose row was lost after
|
|
2136
|
+
* the sweep committed would understate committed value forever; a lost
|
|
2137
|
+
* reversal would overstate it, which is the worse direction — the builder
|
|
2138
|
+
* would see money as theirs that a revived credit can still buy work with.
|
|
2139
|
+
*/
|
|
2140
|
+
enqueueCreditExpiryRelease(tx: RevenueReporterQuerier, payload: CreditExpiryReleasePayload): Promise<void>;
|
|
2141
|
+
/**
|
|
2142
|
+
* Report a reaper-force-failed paid job to the platform (internal-review) so the
|
|
2143
|
+
* operator receives an ambient notice that a DVM died or wedged mid-job.
|
|
2144
|
+
* Best-effort and fire-and-forget: unlike {@link report} there is no local
|
|
2145
|
+
* durable queue — a non-2xx or transport failure is structured-logged
|
|
2146
|
+
* (`paid_job_death_report_failed`) and dropped, since the reaper's own
|
|
2147
|
+
* `stale_job_failed` log is the durable record and a failed *alert* must never
|
|
2148
|
+
* wedge the sweep.
|
|
2149
|
+
*/
|
|
2150
|
+
reportPaidJobDeath(payload: PaidJobDeathPayload): Promise<void>;
|
|
2151
|
+
/**
|
|
2152
|
+
* Report a settled draw whose revenue event was skipped (internal-review).
|
|
2153
|
+
* Best-effort and fire-and-forget: the SDK's structured
|
|
2154
|
+
* `revenue_skipped_no_rail` log is the durable backstop, so a failed notice
|
|
2155
|
+
* must never wedge terminal handling or the orphan-draw reconciler.
|
|
2156
|
+
*/
|
|
2157
|
+
reportRevenueSkippedNoRail(payload: RevenueSkippedNoRailPayload): Promise<void>;
|
|
2158
|
+
/** Report the self-relay's Base gas gauge for platform-side paging and recovery. */
|
|
2159
|
+
reportX402SelfRelayGasBalance(payload: X402SelfRelayGasBalancePayload): Promise<void>;
|
|
2160
|
+
/** Report redacted self-relay RPC health for platform-side paging and recovery. */
|
|
2161
|
+
reportX402SelfRelayRpcHealth(payload: X402SelfRelayRpcHealthPayload): Promise<void>;
|
|
2162
|
+
/** Report the Tempo operator's fee-token gauge for paging and recovery. */
|
|
2163
|
+
reportTempoSettlementBalance(payload: TempoSettlementBalancePayload): Promise<void>;
|
|
2164
|
+
/** Report a redacted operator fee rejection immediately. */
|
|
2165
|
+
reportTempoSettlementFailure(payload: TempoSettlementFailurePayload): Promise<void>;
|
|
2166
|
+
private postTempoSettlementReadiness;
|
|
2167
|
+
/** Start the background retry loop. */
|
|
2168
|
+
startRetryLoop(): void;
|
|
2169
|
+
/** Stop the background retry loop. */
|
|
2170
|
+
stop(): void;
|
|
2171
|
+
/**
|
|
2172
|
+
* Operator-facing queue snapshot: distinguishes the live retry queue from
|
|
2173
|
+
* terminal (stale) rows and breaks the latter down by classification reason
|
|
2174
|
+
* (`token_rotated`, `client_error:<status>`, `abandoned`). Lets monitoring tell
|
|
2175
|
+
* transient backlog apart from permanently-failed rows.
|
|
2176
|
+
*/
|
|
2177
|
+
getQueueStats(): Promise<{
|
|
2178
|
+
liveRetry: number;
|
|
2179
|
+
stale: number;
|
|
2180
|
+
byReason: Record<string, number>;
|
|
2181
|
+
}>;
|
|
2182
|
+
/**
|
|
2183
|
+
* Deliver one bounded batch from the durable queue.
|
|
2184
|
+
*
|
|
2185
|
+
* Public so startup/recovery tests and operator tooling can drive the same
|
|
2186
|
+
* drain the background timer uses without reaching into private state.
|
|
2187
|
+
*/
|
|
2188
|
+
drainPending(): Promise<void>;
|
|
2189
|
+
/** Durably queue one report of either kind, then try it once immediately. */
|
|
2190
|
+
private enqueue;
|
|
2191
|
+
/** Insert one pending report through either the pool or an open transaction. */
|
|
2192
|
+
private insertPending;
|
|
2193
|
+
private attemptDelivery;
|
|
2194
|
+
/** Mark a pending row terminal (stale) with a classification reason. */
|
|
2195
|
+
private markStale;
|
|
2196
|
+
}
|
|
976
2197
|
|
|
977
2198
|
/** Query surface shared by the pool and an open Postgres transaction. */
|
|
978
2199
|
type X402ChannelQuerier = Pick<Pool, "query">;
|
|
@@ -992,8 +2213,8 @@ interface X402SettlementIntent {
|
|
|
992
2213
|
operation: "fund" | "refund";
|
|
993
2214
|
effectId: string;
|
|
994
2215
|
paymentId: string;
|
|
995
|
-
payload: PaymentPayload;
|
|
996
|
-
requirements: PaymentRequirements;
|
|
2216
|
+
payload: PaymentPayload$1;
|
|
2217
|
+
requirements: PaymentRequirements$1;
|
|
997
2218
|
channelBefore: Channel;
|
|
998
2219
|
/**
|
|
999
2220
|
* The channel as it stood when the settlement was handed to the facilitator —
|
|
@@ -1012,7 +2233,7 @@ interface X402SettlementIntent {
|
|
|
1012
2233
|
submissionBlock?: string;
|
|
1013
2234
|
pendingId?: string;
|
|
1014
2235
|
status: X402SettlementStatus;
|
|
1015
|
-
response?: SettleResponse;
|
|
2236
|
+
response?: SettleResponse$1;
|
|
1016
2237
|
/** Epoch ms the row was first prepared. */
|
|
1017
2238
|
createdAt: number;
|
|
1018
2239
|
/** Epoch ms of the last status write — what the repair queue ages against. */
|
|
@@ -1279,7 +2500,7 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
|
|
|
1279
2500
|
* reversed. On the live path the columns are already `NULL` and this is a
|
|
1280
2501
|
* no-op.
|
|
1281
2502
|
*/
|
|
1282
|
-
recordSettlementResponse(settlementId: string, response: SettleResponse): Promise<void>;
|
|
2503
|
+
recordSettlementResponse(settlementId: string, response: SettleResponse$1): Promise<void>;
|
|
1283
2504
|
/**
|
|
1284
2505
|
* Mark the external settlement and ledger effect complete in the active transaction.
|
|
1285
2506
|
*
|
|
@@ -1366,15 +2587,15 @@ declare class PostgresX402ChannelStorage implements ChannelStorage {
|
|
|
1366
2587
|
|
|
1367
2588
|
/** Durable channel seam used to compose a settled draw with consumed Tempo value. */
|
|
1368
2589
|
interface TempoSessionSettlementStore {
|
|
1369
|
-
withDrawPlacement<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle) => Promise<value>, tx?: CreditLedgerQuerier): Promise<value>;
|
|
1370
|
-
withTerminalConsumption<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle) => Promise<{
|
|
2590
|
+
withDrawPlacement<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle$1) => Promise<value>, tx?: CreditLedgerQuerier): Promise<value>;
|
|
2591
|
+
withTerminalConsumption<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle$1) => Promise<{
|
|
1371
2592
|
value: value;
|
|
1372
2593
|
amount: bigint;
|
|
1373
2594
|
consume: boolean;
|
|
1374
2595
|
}>): Promise<value>;
|
|
1375
2596
|
}
|
|
1376
2597
|
/** Tempo channel state read while holding its durable row lock. */
|
|
1377
|
-
interface TempoSessionLifecycle {
|
|
2598
|
+
interface TempoSessionLifecycle$1 {
|
|
1378
2599
|
finalized: boolean;
|
|
1379
2600
|
closeRequestedAt: bigint;
|
|
1380
2601
|
spent: bigint;
|
|
@@ -1451,6 +2672,7 @@ declare class CreditLedger {
|
|
|
1451
2672
|
*/
|
|
1452
2673
|
readonly durable = true;
|
|
1453
2674
|
private x402Settlements?;
|
|
2675
|
+
private expiryReleaseOutbox?;
|
|
1454
2676
|
constructor(pool: CreditLedgerPool, tempoSessionStore?: TempoSessionSettlementStore | undefined);
|
|
1455
2677
|
/**
|
|
1456
2678
|
* Bind the durable x402 settlement state this ledger gates spending on
|
|
@@ -1462,6 +2684,22 @@ declare class CreditLedger {
|
|
|
1462
2684
|
* channel storage, where no settlement row can exist to wedge.
|
|
1463
2685
|
*/
|
|
1464
2686
|
useX402SettlementGate(gate: X402RefundSettlementGate): void;
|
|
2687
|
+
/**
|
|
2688
|
+
* Bind the durable outbox that reports credit-expiry releases and their
|
|
2689
|
+
* revivals to the platform (internal-review).
|
|
2690
|
+
*
|
|
2691
|
+
* Held on the ledger rather than threaded through {@link fund} for the same
|
|
2692
|
+
* reason as the gate above: the revival half fires from *every* funding path
|
|
2693
|
+
* — Cashu commit, x402 exact and channel, Tempo, the Lightning invoice
|
|
2694
|
+
* settle, the implicit N=1 per-call payment — and a seam each of those has to
|
|
2695
|
+
* remember to pass is a seam one of them will eventually forget.
|
|
2696
|
+
*
|
|
2697
|
+
* Unbound (a `dvmctl dev` server, a self-hosted builder, a test host) the
|
|
2698
|
+
* sweep and the reversal still run and still write the ledger's own rows;
|
|
2699
|
+
* only the platform report is skipped. Reporting is bookkeeping and must
|
|
2700
|
+
* never gate the ledger.
|
|
2701
|
+
*/
|
|
2702
|
+
useCreditExpiryReleaseOutbox(outbox: CreditExpiryReleaseOutbox): void;
|
|
1465
2703
|
/** Create the `credits` / `credit_draws` tables if absent. Call once at SDK boot. */
|
|
1466
2704
|
init(): Promise<void>;
|
|
1467
2705
|
/** The boot DDL itself — always runs under {@link withSdkInitLock} (internal-review). */
|
|
@@ -1499,9 +2737,15 @@ declare class CreditLedger {
|
|
|
1499
2737
|
*
|
|
1500
2738
|
* Pass `tx` (a client inside a caller-owned `BEGIN`) to commit the rail
|
|
1501
2739
|
* receive and the ledger credit atomically (spec condition 3 — the internal-review
|
|
1502
|
-
* verifier does this). The ledger issues **no** transaction control on `tx
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
2740
|
+
* verifier does this). The ledger issues **no** transaction control on `tx`.
|
|
2741
|
+
*
|
|
2742
|
+
* Without `tx` it opens one of its own, because a funding is no longer a
|
|
2743
|
+
* single statement: it upserts the credit, records its funding lot
|
|
2744
|
+
* (internal-review), and reverses any standing expiry release (internal-review) — and that
|
|
2745
|
+
* last leg restores balance and queues a report. A crash between the upsert
|
|
2746
|
+
* and the reversal would leave a revived credit whose release still stands,
|
|
2747
|
+
* which the sweep's own exclusion then makes permanent: `balance_micro > 0`
|
|
2748
|
+
* but a standing release means it is neither drainable nor re-releasable.
|
|
1505
2749
|
*
|
|
1506
2750
|
* `basis` records the rail value behind the fiat (internal-review) so each draw can
|
|
1507
2751
|
* be allocated its share of the rail-native amount actually received. A
|
|
@@ -1538,21 +2782,110 @@ declare class CreditLedger {
|
|
|
1538
2782
|
* below. This method is the authority on that rule, as it is on the binding
|
|
1539
2783
|
* rules above; every door preflights it where a refusal is still free.
|
|
1540
2784
|
*/
|
|
1541
|
-
fund(args:
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
2785
|
+
fund(args: CreditFundArgs): Promise<CreditSnapshot>;
|
|
2786
|
+
/**
|
|
2787
|
+
* The funding itself, on whichever handle {@link fund} chose. Every refusal
|
|
2788
|
+
* it raises is the caller's to see unchanged; a self-opened transaction rolls
|
|
2789
|
+
* back around it.
|
|
2790
|
+
*/
|
|
2791
|
+
private fundOn;
|
|
2792
|
+
/**
|
|
2793
|
+
* Undo any expiry release this credit still carries, because a funding just
|
|
2794
|
+
* landed on it (internal-review — the revival rule), restoring the balance the
|
|
2795
|
+
* release took.
|
|
2796
|
+
*
|
|
2797
|
+
* A release records the credit's **final** undrawn remainder and zeroes it
|
|
2798
|
+
* (see {@link releaseExpiredCreditLocked}). Funding adds to the balance and
|
|
2799
|
+
* overwrites `expiry_ms` (spec §5 carry-forward: the credit is a rolling
|
|
2800
|
+
* buffer), so the moment money arrives the recorded remainder is no longer
|
|
2801
|
+
* final: the release stops counting and its micro come back.
|
|
2802
|
+
*
|
|
2803
|
+
* Deliberately unconditional on the incoming expiry rather than gated on
|
|
2804
|
+
* `expiryMs > nowMs`. A release exists only for a credit that had already
|
|
2805
|
+
* lapsed, so any funding that reaches one is money the release did not
|
|
2806
|
+
* account for — and a funding that arrives with an already-past expiry leaves
|
|
2807
|
+
* a credit the very next sweep re-releases at its new, larger remainder. One
|
|
2808
|
+
* rule, self-healing in both directions, and it never needs to read the
|
|
2809
|
+
* pre-funding expiry the upsert has already overwritten.
|
|
2810
|
+
*
|
|
2811
|
+
* Runs on the funding's own handle — which {@link fund} now guarantees is a
|
|
2812
|
+
* transaction — so the reversal, the restored balance and the report commit
|
|
2813
|
+
* with the money or not at all. A reversal that committed without its report
|
|
2814
|
+
* would be unrecoverable: `WHERE reversed_at IS NULL` means no later pass
|
|
2815
|
+
* re-derives it.
|
|
2816
|
+
*
|
|
2817
|
+
* @returns the credit row as the restore left it, or undefined when there was
|
|
2818
|
+
* nothing to reverse. The caller reads its snapshot off this rather than off
|
|
2819
|
+
* the funding upsert's `RETURNING`, which predates the restore.
|
|
2820
|
+
*/
|
|
2821
|
+
private reverseExpiryReleases;
|
|
2822
|
+
/**
|
|
2823
|
+
* Release the undrawn remainder of every credit whose TTL has run out
|
|
2824
|
+
* (internal-review). Returns the releases this pass recorded.
|
|
2825
|
+
*
|
|
2826
|
+
* **What is released.** The credit's whole `balance_micro`. Expiry ends
|
|
2827
|
+
* spending but never ownership of the record, so the balance stays readable;
|
|
2828
|
+
* what ends is the caller's claim on it, and on the prepaid rails the value
|
|
2829
|
+
* behind it is already in builder custody.
|
|
2830
|
+
*
|
|
2831
|
+
* **Tempo is excluded**, at both of its markers (`tempo_channel_id` and a
|
|
2832
|
+
* `tempo` rail). A Tempo credit is channel-backed and its undrawn value
|
|
2833
|
+
* returns to the *caller* at channel exit, so releasing it would book the
|
|
2834
|
+
* builder money the chain is about to hand back.
|
|
2835
|
+
*
|
|
2836
|
+
* **A credit with a pending hold is skipped, not partially released.** A draw
|
|
2837
|
+
* placed before expiry stays settleable afterwards (`draw`'s replay lookup
|
|
2838
|
+
* runs above the expiry check, so a lost response is still recoverable), so
|
|
2839
|
+
* the remainder is not final while a hold is outstanding. Skipping costs one
|
|
2840
|
+
* sweep interval and keeps the released figure exactly "what nothing bought";
|
|
2841
|
+
* holds do resolve — the orphan-draw watchdog (internal-review) is what guarantees
|
|
2842
|
+
* a stranded one still reaches a terminal state.
|
|
2843
|
+
*
|
|
2844
|
+
* **Idempotent** two ways. `release_id` is derived from the expiry instant,
|
|
2845
|
+
* so a re-sweep after a crash between the insert and its report collides on
|
|
2846
|
+
* the same primary key instead of writing a second row; and the candidate
|
|
2847
|
+
* scan excludes any credit that already has an un-reversed release.
|
|
2848
|
+
*
|
|
2849
|
+
* Each candidate is re-checked under the credit row lock every
|
|
2850
|
+
* `credit_draws` write is taken under, so a draw racing the sweep either
|
|
2851
|
+
* loses the race (its credit is already released and it would have been
|
|
2852
|
+
* refused `credit_expired` anyway) or wins it and leaves a pending hold the
|
|
2853
|
+
* locked re-check sees.
|
|
2854
|
+
*/
|
|
2855
|
+
sweepExpiredCredits(args?: {
|
|
1554
2856
|
nowMs?: number;
|
|
1555
|
-
|
|
2857
|
+
/** Credits released per pass. Bounds the work a long-idle DVM does at boot. */
|
|
2858
|
+
limit?: number;
|
|
2859
|
+
}): Promise<{
|
|
2860
|
+
released: CreditExpiryRelease[];
|
|
2861
|
+
}>;
|
|
2862
|
+
/**
|
|
2863
|
+
* Write one credit's expiry release under its row lock, zero the balance it
|
|
2864
|
+
* released, and re-assert every condition the unlocked candidate scan tested.
|
|
2865
|
+
* Returns undefined when the credit no longer qualifies — funded, drawn
|
|
2866
|
+
* against, or already released between the scan and the lock.
|
|
2867
|
+
*
|
|
2868
|
+
* **Zeroing the balance is what makes the release real**, and it is the whole
|
|
2869
|
+
* reason this runs under the lock rather than as a bare INSERT. The row alone
|
|
2870
|
+
* records that the caller's claim ended; it does not *end* it. `requestDrain`
|
|
2871
|
+
* has no expiry check by design (expiry gates new draws, not reclaims), so a
|
|
2872
|
+
* credit whose remainder had been released and reported was still fully
|
|
2873
|
+
* drainable: the DVM would pay out money the platform had already booked as
|
|
2874
|
+
* the builder's, and `deposited - drawn - drained - released` would go
|
|
2875
|
+
* negative on the same micro. With the balance at zero, `requestDrain`'s
|
|
2876
|
+
* existing `availableMicro <= 0` guard refuses with `nothing_to_drain` and no
|
|
2877
|
+
* new check is needed anywhere. {@link reverseExpiryReleases} puts the
|
|
2878
|
+
* balance back when a funding revives the credit.
|
|
2879
|
+
*
|
|
2880
|
+
* `status` deliberately stays `active`: a released credit must remain
|
|
2881
|
+
* fundable, or the revival rule has nothing to revive.
|
|
2882
|
+
*
|
|
2883
|
+
* The zeroed balance also takes the credit out of `BITCOIN_CREDIT_PREDICATE`
|
|
2884
|
+
* (`balance_micro > 0`), so the in-kind sweep floor stops counting sats the
|
|
2885
|
+
* builder no longer owes back — and counts them again after a revival, with
|
|
2886
|
+
* the credit's funding lots untouched throughout.
|
|
2887
|
+
*/
|
|
2888
|
+
private releaseExpiredCreditLocked;
|
|
1556
2889
|
/**
|
|
1557
2890
|
* Record this funding's in-kind basis as a lot (internal-review).
|
|
1558
2891
|
*
|
|
@@ -2054,7 +3387,15 @@ declare class CreditLedger {
|
|
|
2054
3387
|
listTempoCreditLosses(args?: {
|
|
2055
3388
|
limit?: number;
|
|
2056
3389
|
}): Promise<TempoCreditLoss[]>;
|
|
2057
|
-
/**
|
|
3390
|
+
/**
|
|
3391
|
+
* Reconcile the credit bound to a chain-proven empty x402 channel.
|
|
3392
|
+
*
|
|
3393
|
+
* A channel with no unclaimed backing makes every remaining customer
|
|
3394
|
+
* liability unsafe, so those credits become terminal. A fully settled credit
|
|
3395
|
+
* has neither a balance nor an unfinished drain and is safe to retain for a
|
|
3396
|
+
* later deposit. This also repairs a zero-liability row an older build
|
|
3397
|
+
* terminalized from the same observation.
|
|
3398
|
+
*/
|
|
2058
3399
|
terminalizeX402Credit(evidence: X402CreditLossEvidence, tx?: CreditLedgerQuerier): Promise<X402CreditLoss[]>;
|
|
2059
3400
|
private terminalizeX402CreditLocked;
|
|
2060
3401
|
/** List terminal x402 credit losses, newest observation first. */
|
|
@@ -2386,6 +3727,27 @@ interface CreditLedgerLike {
|
|
|
2386
3727
|
* {@link CreditLedger.useX402SettlementGate}.
|
|
2387
3728
|
*/
|
|
2388
3729
|
useX402SettlementGate?(gate: X402RefundSettlementGate): void;
|
|
3730
|
+
/**
|
|
3731
|
+
* Bind the outbox that reports credit-expiry releases and their revivals
|
|
3732
|
+
* (internal-review).
|
|
3733
|
+
*
|
|
3734
|
+
* Optional, and absent on `MemoryCreditLedger`: an expiry release is a
|
|
3735
|
+
* durable liability fact, and a ledger whose balances do not survive a
|
|
3736
|
+
* restart has none to report. A host on a non-durable ledger simply never
|
|
3737
|
+
* sweeps. See {@link CreditLedger.useCreditExpiryReleaseOutbox}.
|
|
3738
|
+
*/
|
|
3739
|
+
useCreditExpiryReleaseOutbox?(outbox: CreditExpiryReleaseOutbox): void;
|
|
3740
|
+
/**
|
|
3741
|
+
* Release the undrawn remainder of every credit past its TTL (internal-review).
|
|
3742
|
+
* Optional on the same terms as the binder above. See
|
|
3743
|
+
* {@link CreditLedger.sweepExpiredCredits}.
|
|
3744
|
+
*/
|
|
3745
|
+
sweepExpiredCredits?(args?: {
|
|
3746
|
+
nowMs?: number;
|
|
3747
|
+
limit?: number;
|
|
3748
|
+
}): Promise<{
|
|
3749
|
+
released: CreditExpiryRelease[];
|
|
3750
|
+
}>;
|
|
2389
3751
|
/**
|
|
2390
3752
|
* Ask that gate directly, so a route can refuse a wedged channel **before**
|
|
2391
3753
|
* the rail moves anything (internal-review). Optional on the same terms as the
|
|
@@ -2641,6 +4003,38 @@ interface CreditDrawReleaseOutbox {
|
|
|
2641
4003
|
/** Reporter-owned insert executed through the ledger transaction. */
|
|
2642
4004
|
enqueue: CreditDrawReleaseEnqueue;
|
|
2643
4005
|
}
|
|
4006
|
+
/** Reporter seam for credit-expiry releases and the revivals that reverse them (internal-review). */
|
|
4007
|
+
interface CreditExpiryReleaseOutbox {
|
|
4008
|
+
/** Platform DVM record ID bound into the report. */
|
|
4009
|
+
dvmId: string;
|
|
4010
|
+
/** Reporter-owned insert executed through the ledger transaction. */
|
|
4011
|
+
enqueue: CreditExpiryReleaseEnqueue;
|
|
4012
|
+
}
|
|
4013
|
+
/**
|
|
4014
|
+
* One recorded expiry episode: a credit's undrawn remainder passing to the
|
|
4015
|
+
* builder when its TTL ran out (internal-review).
|
|
4016
|
+
*
|
|
4017
|
+
* `reversedAt` is set when a later funding revived the credit, at which point
|
|
4018
|
+
* the release stops counting against committed value — the row stays as the
|
|
4019
|
+
* record that it once did.
|
|
4020
|
+
*/
|
|
4021
|
+
interface CreditExpiryRelease {
|
|
4022
|
+
creditId: string;
|
|
4023
|
+
/** Per-expiry-episode key, derived from {@link CreditExpiryRelease.expiryMs}. */
|
|
4024
|
+
releaseId: string;
|
|
4025
|
+
callerPubkey: string;
|
|
4026
|
+
currency: string;
|
|
4027
|
+
/** Funding rail, or `null` on a credit funded before the basis was mandatory. */
|
|
4028
|
+
rail: string | null;
|
|
4029
|
+
/** Fiat micro released, 1e-6 of `currency`. */
|
|
4030
|
+
amountMicro: number;
|
|
4031
|
+
/** When the credit's TTL ran out — when the liability ended. */
|
|
4032
|
+
expiryMs: number;
|
|
4033
|
+
/** When the sweep observed the expiry. */
|
|
4034
|
+
releasedAt: number;
|
|
4035
|
+
/** When a funding revived the credit and undid this release. */
|
|
4036
|
+
reversedAt: number | null;
|
|
4037
|
+
}
|
|
2644
4038
|
/** Pool-shaped handle (`query` + `connect`). Required for the locked write paths. */
|
|
2645
4039
|
type CreditLedgerPool = Pick<Pool, "query" | "connect">;
|
|
2646
4040
|
/**
|
|
@@ -3059,6 +4453,22 @@ interface CreditFundingBasis {
|
|
|
3059
4453
|
maxClaimableAmount: string;
|
|
3060
4454
|
};
|
|
3061
4455
|
}
|
|
4456
|
+
/** What {@link CreditLedger.fund} takes. Named so the private body can share the shape. */
|
|
4457
|
+
interface CreditFundArgs {
|
|
4458
|
+
/** Omit to mint a fresh credit id; provide to top up (or idempotently create). */
|
|
4459
|
+
creditId?: string;
|
|
4460
|
+
callerPubkey: string;
|
|
4461
|
+
/** The DVM's pricing currency; `amountMicro` is 1e-6 of it. */
|
|
4462
|
+
currency: string;
|
|
4463
|
+
amountMicro: number;
|
|
4464
|
+
/** Absolute expiry, ms since epoch. Overwrites the previous value on top-up. */
|
|
4465
|
+
expiryMs: number;
|
|
4466
|
+
/** Rail-value basis this funding contributes (internal-review, required by internal-review). */
|
|
4467
|
+
basis: CreditFundingBasis;
|
|
4468
|
+
/** External transaction handle — caller owns BEGIN/COMMIT (spec condition 3). */
|
|
4469
|
+
tx?: CreditLedgerQuerier;
|
|
4470
|
+
nowMs?: number;
|
|
4471
|
+
}
|
|
3062
4472
|
/**
|
|
3063
4473
|
* Allocate a draw's share of a credit's unspent rail value (internal-review). Pure —
|
|
3064
4474
|
* shared by the Postgres and in-memory ledgers so the two can't drift.
|
|
@@ -3244,6 +4654,17 @@ declare class CreditLedgerError extends Error {
|
|
|
3244
4654
|
constructor(code: CreditLedgerErrorCode, message: string, details?: CreditLedgerErrorDetails, opts?: ErrorOptions);
|
|
3245
4655
|
}
|
|
3246
4656
|
|
|
4657
|
+
/** One durable Tempo session-channel row exposed to reconciliation workers. */
|
|
4658
|
+
interface TempoSessionRow {
|
|
4659
|
+
key: string;
|
|
4660
|
+
state: Record<string, unknown>;
|
|
4661
|
+
updatedAt: number;
|
|
4662
|
+
}
|
|
4663
|
+
/** Cursor for stable pagination over active Tempo session channels. */
|
|
4664
|
+
interface TempoSessionCursor {
|
|
4665
|
+
updatedAt: number;
|
|
4666
|
+
key: string;
|
|
4667
|
+
}
|
|
3247
4668
|
/** Durable state joining a credit drain to its cooperative channel close. */
|
|
3248
4669
|
interface TempoSessionCloseRecord {
|
|
3249
4670
|
channelId: string;
|
|
@@ -3261,7 +4682,212 @@ type TempoSessionCloseForecastResolution<value> = {
|
|
|
3261
4682
|
} | {
|
|
3262
4683
|
outcome: "missing";
|
|
3263
4684
|
};
|
|
4685
|
+
/** The voucher increment observed while holding the channel row lock. */
|
|
4686
|
+
interface TempoVoucherDelta {
|
|
4687
|
+
key: string;
|
|
4688
|
+
cumulativeAmount: bigint;
|
|
4689
|
+
priorCumulativeAmount: bigint;
|
|
4690
|
+
delta: bigint;
|
|
4691
|
+
}
|
|
4692
|
+
/** Result of a ledger resolution composed with terminal channel consumption. */
|
|
4693
|
+
interface TempoSessionConsumption<value> {
|
|
4694
|
+
value: value;
|
|
4695
|
+
amount: bigint;
|
|
4696
|
+
consume: boolean;
|
|
4697
|
+
}
|
|
4698
|
+
/** Channel lifecycle fields exposed while its row lock is held. */
|
|
4699
|
+
interface TempoSessionLifecycle {
|
|
4700
|
+
finalized: boolean;
|
|
4701
|
+
closeRequestedAt: bigint;
|
|
4702
|
+
spent: bigint;
|
|
4703
|
+
settledOnChain: bigint;
|
|
4704
|
+
highestVoucherAmount: bigint;
|
|
4705
|
+
}
|
|
4706
|
+
/**
|
|
4707
|
+
* Postgres implementation of mppx's atomic channel store.
|
|
4708
|
+
*
|
|
4709
|
+
* Every mutation takes an advisory transaction lock keyed by the channel
|
|
4710
|
+
* before locking its row with `FOR UPDATE`, applies mppx's synchronous
|
|
4711
|
+
* transition exactly once, and commits before returning. The advisory lock
|
|
4712
|
+
* covers a channel's first write, when there is no row for `FOR UPDATE` to
|
|
4713
|
+
* lock. Together these are the cross-machine linearization point for voucher,
|
|
4714
|
+
* top-up, spend, and close state; mppx's process-local fallback is
|
|
4715
|
+
* intentionally never used in a durable SDK host.
|
|
4716
|
+
*
|
|
4717
|
+
* Lock order remains channel before credit: mutation paths take the advisory
|
|
4718
|
+
* lock and then the row lock, while terminal consumption requires an existing
|
|
4719
|
+
* row and starts there. Both own the channel lock before the ledger takes its
|
|
4720
|
+
* parent-credit lock through the same client.
|
|
4721
|
+
*
|
|
4722
|
+
* {@link withVoucherAcceptance} acquires its pooled client **lazily**, on the
|
|
4723
|
+
* first store mutation rather than up front (internal-review). Its `operation` is
|
|
4724
|
+
* mppx's credential verification, which does the chain work before it touches
|
|
4725
|
+
* the store: an `open` submits the escrow transaction and awaits its receipt,
|
|
4726
|
+
* a `voucher` reads channel state over public RPC. Holding a connection across
|
|
4727
|
+
* that starves everything else on the shared host pool — `/v1/job`, the job
|
|
4728
|
+
* store, KV, the credit ledger, the replay store — and exposes the open
|
|
4729
|
+
* transaction to `idle_in_transaction_session_timeout`. This is the same shape
|
|
4730
|
+
* internal-review removed from the x402 settlement path. Laziness costs nothing in
|
|
4731
|
+
* atomicity: the `FOR UPDATE` row lock was always taken at the first mutation,
|
|
4732
|
+
* never at `BEGIN`, so the critical section is unchanged — only the
|
|
4733
|
+
* connection-holding window shrinks to the database work it actually covers.
|
|
4734
|
+
*/
|
|
4735
|
+
declare class PostgresTempoSessionStore implements Store.AtomicStore {
|
|
4736
|
+
private readonly pool;
|
|
4737
|
+
private readonly acceptance;
|
|
4738
|
+
constructor(pool: Pool);
|
|
4739
|
+
/** Create the channel table under the SDK-wide migration lock. */
|
|
4740
|
+
init(): Promise<void>;
|
|
4741
|
+
/** Read one channel snapshot. */
|
|
4742
|
+
get(key: string): Promise<unknown>;
|
|
4743
|
+
/** Replace one channel snapshot. Used by mppx only for non-RMW maintenance. */
|
|
4744
|
+
put(key: string, value: unknown): Promise<void>;
|
|
4745
|
+
/** Delete one channel snapshot. */
|
|
4746
|
+
delete(key: string): Promise<void>;
|
|
4747
|
+
/** Atomic read-modify-write using a row lock shared by every SDK replica. */
|
|
4748
|
+
update<result>(key: string, fn: (current: unknown) => Store.Change<unknown, result>): Promise<result>;
|
|
4749
|
+
/** Compose voucher acceptance and its ledger mutation under the channel-row lock. */
|
|
4750
|
+
withVoucherAcceptance<value, result>(voucher: {
|
|
4751
|
+
channelId: string;
|
|
4752
|
+
cumulativeAmount: bigint;
|
|
4753
|
+
}, commit: (tx: PoolClient, voucher: TempoVoucherDelta) => Promise<result>, operation: () => Promise<value>): Promise<{
|
|
4754
|
+
value: value;
|
|
4755
|
+
result: result | undefined;
|
|
4756
|
+
}>;
|
|
4757
|
+
/**
|
|
4758
|
+
* Check out the acceptance transaction's client, opening it on first demand.
|
|
4759
|
+
*
|
|
4760
|
+
* An operation that never mutates the store therefore never takes a
|
|
4761
|
+
* connection and never opens an empty transaction — the caller sees the same
|
|
4762
|
+
* `result: undefined` it saw before, because nothing committed either way.
|
|
4763
|
+
*
|
|
4764
|
+
* The in-flight checkout is memoized, not just its result: mppx awaits each
|
|
4765
|
+
* store call today, but two concurrent first mutations would otherwise open
|
|
4766
|
+
* two transactions and leak the one nobody keeps a handle to.
|
|
4767
|
+
*/
|
|
4768
|
+
private acceptanceClient;
|
|
4769
|
+
/** Serialize a credit draw with the channel lifecycle that authorizes it. */
|
|
4770
|
+
withDrawPlacement<value>(channelId: string, operation: (tx: CreditLedgerQuerier, lifecycle: TempoSessionLifecycle) => Promise<value>, tx?: CreditLedgerQuerier): Promise<value>;
|
|
4771
|
+
private withLockedLifecycle;
|
|
4772
|
+
/**
|
|
4773
|
+
* Compose a terminal ledger resolution with the channel's consumed value.
|
|
4774
|
+
*
|
|
4775
|
+
* Voucher acceptance records spending authority; only a successful job
|
|
4776
|
+
* consumes it. Holding the channel row while the callback locks and settles
|
|
4777
|
+
* the credit keeps that distinction atomic across machines and preserves the
|
|
4778
|
+
* shared channel -> credit lock order.
|
|
4779
|
+
*/
|
|
4780
|
+
withTerminalConsumption<value>(channelId: string, operation: (tx: PoolClient, lifecycle: TempoSessionLifecycle) => Promise<TempoSessionConsumption<value>>): Promise<value>;
|
|
4781
|
+
/** Retire lost backing while holding the channel row ahead of every credit lock. */
|
|
4782
|
+
withFinalizedCreditLoss<value>(channelId: string, operation: (tx: PoolClient, lifecycle: TempoSessionLifecycle) => Promise<value>): Promise<value>;
|
|
4783
|
+
/** Page active channel snapshots for the close/settlement watcher. */
|
|
4784
|
+
listActive(limit?: number, cursor?: TempoSessionCursor): Promise<TempoSessionRow[]>;
|
|
4785
|
+
/** Persist a drain's close intent before its credential is broadcast. */
|
|
4786
|
+
beginClose(channelId: string, drainId: string): Promise<TempoSessionCloseRecord>;
|
|
4787
|
+
/** Reserve a close intent and run its ledger debit in the same transaction. */
|
|
4788
|
+
withCloseIntent<value>(channelId: string, drainId: string, operation: (tx: PoolClient) => Promise<value>): Promise<value>;
|
|
4789
|
+
/**
|
|
4790
|
+
* Resolve a reserved close's forecast under the intent row lock.
|
|
4791
|
+
*
|
|
4792
|
+
* `proceed` opens the broadcast gate. `release` deletes the intent and runs
|
|
4793
|
+
* the ledger restore in the same transaction, but only while the gate is
|
|
4794
|
+
* still pending. A concurrent proceed wins by returning `proceed` to the
|
|
4795
|
+
* releaser, which must not restore value that may already be broadcasting.
|
|
4796
|
+
*/
|
|
4797
|
+
resolveCloseForecast<value>(channelId: string, drainId: string, decision: TempoSessionCloseForecastDecision, operation?: (tx: PoolClient) => Promise<value>): Promise<TempoSessionCloseForecastResolution<value>>;
|
|
4798
|
+
/** Delete a matching close intent and release its ledger debit atomically. */
|
|
4799
|
+
releaseCloseIntent<value>(channelId: string, drainId: string, operation: (tx: PoolClient) => Promise<value>): Promise<value>;
|
|
4800
|
+
/** Read a persisted cooperative-close intent or confirmed result. */
|
|
4801
|
+
getClose(channelId: string): Promise<TempoSessionCloseRecord | null>;
|
|
4802
|
+
/** Persist proof that a cooperative close reached terminal chain state. */
|
|
4803
|
+
confirmClose(channelId: string, drainId: string, reference: string): Promise<TempoSessionCloseRecord>;
|
|
4804
|
+
private runCloseIntentTransaction;
|
|
4805
|
+
private transaction;
|
|
4806
|
+
private writeCloseIntent;
|
|
4807
|
+
private updateInTransaction;
|
|
4808
|
+
}
|
|
3264
4809
|
|
|
4810
|
+
/**
|
|
4811
|
+
* Options for assembling an Mppx server handle from explicit recipient strings
|
|
4812
|
+
* and the surrounding environment. The SDK boundary owns env reads so DVM
|
|
4813
|
+
* authors only think in terms of "set DVMKIT_TEMPO_RECIPIENT /
|
|
4814
|
+
* DVMKIT_TEMPO_SECRET_KEY in your DVM env" — same pattern as the existing
|
|
4815
|
+
* payment-rail inputs.
|
|
4816
|
+
*/
|
|
4817
|
+
interface MppOpts {
|
|
4818
|
+
/**
|
|
4819
|
+
* Tempo recipient (0x-prefixed 40-char hex address). When set, registers
|
|
4820
|
+
* `tempo/charge`. Currency defaults to USDC on Tempo mainnet; override via
|
|
4821
|
+
* the `DVMKIT_TEMPO_CURRENCY` env var for testnet/devnet deployments.
|
|
4822
|
+
*/
|
|
4823
|
+
tempoRecipient?: string;
|
|
4824
|
+
/**
|
|
4825
|
+
* Server realm for advertising on `/v1/info`. Default resolution lives in
|
|
4826
|
+
* mppx (env vars `MPP_REALM`, `FLY_APP_NAME`, `VERCEL_URL`, request URL,
|
|
4827
|
+
* `"MPP Payment"`).
|
|
4828
|
+
*/
|
|
4829
|
+
realm?: string;
|
|
4830
|
+
/**
|
|
4831
|
+
* When set, only register MPP methods whose name appears in this list, even
|
|
4832
|
+
* if the per-rail env var (`DVMKIT_TEMPO_RECIPIENT`) is configured
|
|
4833
|
+
* (internal-review). Lets operators temporarily disable a rail without unsetting
|
|
4834
|
+
* other knobs. Empty/undefined falls back to "register everything that has
|
|
4835
|
+
* a configured recipient".
|
|
4836
|
+
*/
|
|
4837
|
+
methodsAllowlist?: string[];
|
|
4838
|
+
/**
|
|
4839
|
+
* Durable replay protection for the one-shot `tempo/charge` method
|
|
4840
|
+
* (internal-review). Omit and mppx falls back to `Store.memory()`, whose consumed
|
|
4841
|
+
* transaction hashes are per-process — invisible to sibling machines and lost
|
|
4842
|
+
* on restart. Pass a cross-machine store (`PostgresTempoChargeStore`) on any
|
|
4843
|
+
* multi-instance deploy.
|
|
4844
|
+
*
|
|
4845
|
+
* The same store also backs mppx's fee-sponsor budget and, because mppx
|
|
4846
|
+
* enables proof-credential replay protection only when a store is supplied,
|
|
4847
|
+
* turns that guard on as a side effect.
|
|
4848
|
+
*/
|
|
4849
|
+
tempoCharge?: {
|
|
4850
|
+
store: Store.AtomicStore;
|
|
4851
|
+
};
|
|
4852
|
+
/**
|
|
4853
|
+
* Durable TIP-1034 session support. Omit to keep the existing one-shot
|
|
4854
|
+
* `tempo/charge` method only. The account must be able to submit settlement
|
|
4855
|
+
* transactions as the channel payee/operator; an address-only client is not
|
|
4856
|
+
* sufficient.
|
|
4857
|
+
*/
|
|
4858
|
+
tempoSession?: {
|
|
4859
|
+
store: Store.AtomicStore;
|
|
4860
|
+
account: Account;
|
|
4861
|
+
getClient?: (parameters: {
|
|
4862
|
+
chainId?: number;
|
|
4863
|
+
}) => Client | Promise<Client>;
|
|
4864
|
+
chainId?: number;
|
|
4865
|
+
settlementSchedule?: {
|
|
4866
|
+
units?: number;
|
|
4867
|
+
amount?: string | bigint;
|
|
4868
|
+
intervalMs?: number;
|
|
4869
|
+
/**
|
|
4870
|
+
* How long a settlement holds its in-flight lease (internal-review). Defaults to
|
|
4871
|
+
* {@link TEMPO_SETTLEMENT_LEASE_MS}, which documents the receipt-timeout
|
|
4872
|
+
* floor an override has to stay above.
|
|
4873
|
+
*/
|
|
4874
|
+
leaseMs?: number;
|
|
4875
|
+
};
|
|
4876
|
+
beforeSessionSettlement?: () => string | undefined | Promise<string | undefined>;
|
|
4877
|
+
onSessionSettlement?: (context: {
|
|
4878
|
+
txHash: `0x${string}`;
|
|
4879
|
+
channelId: `0x${string}`;
|
|
4880
|
+
trigger: "settle" | "close" | "scheduled";
|
|
4881
|
+
amount: bigint;
|
|
4882
|
+
delta: bigint;
|
|
4883
|
+
recoveryVersion?: string;
|
|
4884
|
+
}) => void | Promise<void>;
|
|
4885
|
+
onInsufficientFunds?: (context: {
|
|
4886
|
+
operation: "settle";
|
|
4887
|
+
channelId: string;
|
|
4888
|
+
}) => void | Promise<void>;
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
3265
4891
|
/**
|
|
3266
4892
|
* Loose runtime view of an `Mppx.create(...)` handle. mppx's full generic
|
|
3267
4893
|
* typing is precise but propagates badly through layers — the SDK boundary
|
|
@@ -3464,6 +5090,36 @@ interface TempoSessionDrainState {
|
|
|
3464
5090
|
deposit: bigint;
|
|
3465
5091
|
finalized: boolean;
|
|
3466
5092
|
}
|
|
5093
|
+
/**
|
|
5094
|
+
* Attach the typed `issueChallenge` dispatcher to a freshly-created mppx
|
|
5095
|
+
* handle. Production builds via `createMppFromOpts`; tests build via
|
|
5096
|
+
* `Mppx.create(...)` directly and use `wrapMppx` to satisfy the SDK boundary.
|
|
5097
|
+
*
|
|
5098
|
+
* Accepts `unknown` because mppx's `Mppx<methods, transport>` generic doesn't
|
|
5099
|
+
* collapse cleanly through `ReturnType<typeof Mppx.create>` — the default
|
|
5100
|
+
* pins `methods: readonly []` and rejects any concrete method tuple. We cast
|
|
5101
|
+
* inside the wrapper instead of leaking the generic onto every caller.
|
|
5102
|
+
*/
|
|
5103
|
+
declare function wrapMppx(mppx: unknown): MppxServer;
|
|
5104
|
+
interface TempoSessionChainOps {
|
|
5105
|
+
getChannelStatesBatch(client: Client, channelIds: readonly Hex[], escrowContract: `0x${string}`): Promise<{
|
|
5106
|
+
settled: bigint;
|
|
5107
|
+
deposit: bigint;
|
|
5108
|
+
closeRequestedAt: number;
|
|
5109
|
+
}[]>;
|
|
5110
|
+
settle(store: Store.AtomicStore, client: Client, channelId: Hex, options: {
|
|
5111
|
+
account: Account;
|
|
5112
|
+
escrowContract: `0x${string}`;
|
|
5113
|
+
feeToken: `0x${string}`;
|
|
5114
|
+
onSessionSettlement?: NonNullable<MppOpts["tempoSession"]>["onSessionSettlement"];
|
|
5115
|
+
}): Promise<Hex>;
|
|
5116
|
+
readChannelClose(client: Client, channelId: Hex, txHash: Hex): Promise<TempoChannelCloseAmounts>;
|
|
5117
|
+
}
|
|
5118
|
+
declare function attachTempoSessionRuntime(mppx: MppxServer, config: MppOpts["tempoSession"], chainOpOverrides?: Partial<TempoSessionChainOps>): MppxServer;
|
|
5119
|
+
/** Test-only seams for deterministic watcher coverage without a live Tempo RPC. */
|
|
5120
|
+
declare const _testing: {
|
|
5121
|
+
attachTempoSessionRuntime: typeof attachTempoSessionRuntime;
|
|
5122
|
+
};
|
|
3467
5123
|
|
|
3468
5124
|
/** Envelope fields the verifier validates around the signed payload. */
|
|
3469
5125
|
interface CanonicalEnvelope {
|
|
@@ -3575,6 +5231,28 @@ interface SignedRequestVerifier<T> {
|
|
|
3575
5231
|
}
|
|
3576
5232
|
/** Failure mode taxonomy returned from `verify()`. */
|
|
3577
5233
|
type SignedRequestFailure = "schema_invalid" | "timestamp_drift" | "signature_invalid" | "replay_detected";
|
|
5234
|
+
/**
|
|
5235
|
+
* The four fields every signed-request envelope carries around the payload,
|
|
5236
|
+
* with the runtime type each must have once the schema has parsed.
|
|
5237
|
+
*
|
|
5238
|
+
* The single definition of "which part of a schema failure is the envelope's":
|
|
5239
|
+
* the post-parse shape check in `parse()` iterates it, and `authErrorBody`'s
|
|
5240
|
+
* internal-review classifier reads its keys to decide whether a `schema_invalid` throw
|
|
5241
|
+
* is the caller's *input* being wrong (disclosable: the schema is public on
|
|
5242
|
+
* `/v1/info`) or their *signing* being wrong (folded into `signature_invalid`,
|
|
5243
|
+
* one answer for every key and signature failure). `satisfies` pins it to
|
|
5244
|
+
* {@link CanonicalEnvelope}, so a fifth envelope field can't be added to the
|
|
5245
|
+
* type without both readers picking it up.
|
|
5246
|
+
*/
|
|
5247
|
+
declare const SIGNED_ENVELOPE_TYPES: {
|
|
5248
|
+
readonly pubkey: "string";
|
|
5249
|
+
readonly signature: "string";
|
|
5250
|
+
readonly timestamp: "number";
|
|
5251
|
+
readonly nonce: "string";
|
|
5252
|
+
readonly auth_statement: "object";
|
|
5253
|
+
};
|
|
5254
|
+
/** The envelope field names — {@link SIGNED_ENVELOPE_TYPES}' keys. */
|
|
5255
|
+
declare const SIGNED_ENVELOPE_FIELDS: readonly (keyof CanonicalEnvelope)[];
|
|
3578
5256
|
/** Structured error from `verify()` — `sub_reason` is the failure kind. */
|
|
3579
5257
|
declare class SignedRequestError extends Error {
|
|
3580
5258
|
readonly sub_reason: SignedRequestFailure;
|
|
@@ -3640,6 +5318,23 @@ declare function signedRequestInput(body: {
|
|
|
3640
5318
|
input?: string;
|
|
3641
5319
|
data?: unknown;
|
|
3642
5320
|
}): unknown;
|
|
5321
|
+
/**
|
|
5322
|
+
* Build the bounded in-memory FIFO replay store used as the default when no
|
|
5323
|
+
* cross-machine `SignedRequestReplayStore` is supplied. 10-minute retention
|
|
5324
|
+
* window, 100k entry cap, per-process. Exported so `secp256k1Auth(...)` can
|
|
5325
|
+
* pre-build one and share it across every per-schema verifier plus its own
|
|
5326
|
+
* `recordReplay` path (internal-review).
|
|
5327
|
+
*
|
|
5328
|
+
* When the cap is hit after the time-expired sweep, fresh inserts are
|
|
5329
|
+
* **rejected** (return `true` — treated as replay at the wire) rather than
|
|
5330
|
+
* silently evicting an in-window entry. Silent eviction would flush a
|
|
5331
|
+
* still-valid nonce back to "unseen" and let an attacker who can drive cap
|
|
5332
|
+
* pressure replay it (internal-review). A rate-limited structured warning fires so
|
|
5333
|
+
* operators see saturation in `fly logs`; reaching cap means in-memory has
|
|
5334
|
+
* been pushed past where it's safe and the deploy should move to
|
|
5335
|
+
* `PostgresReplayStore`.
|
|
5336
|
+
*/
|
|
5337
|
+
declare function createDefaultReplayStore(): SignedRequestReplayStore;
|
|
3643
5338
|
|
|
3644
5339
|
/**
|
|
3645
5340
|
* A DVM-level auth scheme, declared on `DVMDescriptor.auth`. Authentication
|
|
@@ -4123,6 +5818,35 @@ type PaymentMethod = "cashu" | "x402" | "tempo";
|
|
|
4123
5818
|
* stops that dead rail from reappearing every time the funding menu grows.
|
|
4124
5819
|
*/
|
|
4125
5820
|
type FundingMethod = PaymentMethod | "lightning";
|
|
5821
|
+
/**
|
|
5822
|
+
* Whether a failed job on a given rail can return the caller's funds (internal-review).
|
|
5823
|
+
*
|
|
5824
|
+
* **No rail refunds on `ctx.fail`, and no code behind the idea any more.** Under
|
|
5825
|
+
* the P2PK-accumulator Cashu path — the only Cashu path post-internal-review/internal-review —
|
|
5826
|
+
* `verifyAccumulatorReceipt` commits the caller's proofs straight into
|
|
5827
|
+
* `wallet_accumulator` and leaves `job.receivedProofs` empty; every producer in
|
|
5828
|
+
* `payment.ts` returns `[]`. The `ctx.fail(err, { refund: true })` branch that
|
|
5829
|
+
* used to send proofs back gated on those held proofs, so it had been an
|
|
5830
|
+
* unreachable no-op for as long as the accumulator has been the commit boundary,
|
|
5831
|
+
* and internal-review deleted it. This fail-closed posture was locked by internal-review
|
|
5832
|
+
* (Decision C) and pinned by internal-review. mpp (Tempo) and x402 credentials are
|
|
5833
|
+
* single-use, final-settlement — no SDK-managed reversal path either.
|
|
5834
|
+
*
|
|
5835
|
+
* **This is a rail-level question, and since internal-review it is no longer the whole
|
|
5836
|
+
* story.** A failed job never debits: the draw is a hold the terminal funnel
|
|
5837
|
+
* releases (`JobManager.resolveCreditDraw`), so the value stays on the caller's
|
|
5838
|
+
* credit. What this flag reports is narrower and still true — no rail hands
|
|
5839
|
+
* value backwards — and reclaiming a released balance runs through the
|
|
5840
|
+
* internal-review `drain` op, which exists only on a DVM that advertises credit.
|
|
5841
|
+
*
|
|
5842
|
+
* `{ refund: true }` survives as a **caller-fault annotation only** — first-party
|
|
5843
|
+
* handlers tag which errors were the caller's doing, and `sdk/testing`'s
|
|
5844
|
+
* `createTestContext` exposes it — and it is not a hook waiting to be re-wired.
|
|
5845
|
+
* internal-review, the accumulator-debit rewrite that would have flipped `cashu` back to
|
|
5846
|
+
* `true`, was cancelled as superseded: the ledger is where reclaim lives now.
|
|
5847
|
+
* x402 refund parity remains a deferred internal-review exploration.
|
|
5848
|
+
*/
|
|
5849
|
+
declare const RAIL_REFUNDABLE: Record<PaymentMethod, boolean>;
|
|
4126
5850
|
/**
|
|
4127
5851
|
* Cashu receive mode for builder DVMs (internal-review).
|
|
4128
5852
|
*
|
|
@@ -5086,5 +6810,22 @@ interface OutgoingMessage {
|
|
|
5086
6810
|
* that calls the member it's missing.
|
|
5087
6811
|
*/
|
|
5088
6812
|
declare function isStreamableJobStore(store: JobStore): store is StreamableJobStore;
|
|
6813
|
+
/**
|
|
6814
|
+
* The stale-job reaper surface (internal-review) — the two methods a sweeper needs to
|
|
6815
|
+
* find and atomically reap worker-stranded jobs. A strict subset of
|
|
6816
|
+
* `StreamableJobStore`: `MemoryJobStore` and `PostgresJobStore` satisfy it via
|
|
6817
|
+
* the full streamable interface, and the platform's `IsolateJobStore` (a plain
|
|
6818
|
+
* single-machine `JobStore`, not streamable) implements just these two so the
|
|
6819
|
+
* `IsolateJobManager` reaper can sweep the `isolate_jobs` table without taking
|
|
6820
|
+
* on the streaming/LISTEN machinery it doesn't need.
|
|
6821
|
+
*/
|
|
6822
|
+
interface StaleJobReapable {
|
|
6823
|
+
/** See {@link StreamableJobStore.findStaleJobs}. */
|
|
6824
|
+
findStaleJobs(processingThresholdMs: number, awaitingThresholdMs: number, limit: number, capabilities: string[] | null): Promise<JobRecord[]>;
|
|
6825
|
+
/** See {@link StreamableJobStore.cancelStaleJob}. */
|
|
6826
|
+
cancelStaleJob(jobId: string, expectedActivityBefore: number, reason: string, terminalStatus: "failed" | "cancelled"): Promise<boolean>;
|
|
6827
|
+
}
|
|
6828
|
+
/** Runtime type guard for StaleJobReapable. */
|
|
6829
|
+
declare function isStaleJobReapable(store: JobStore): store is JobStore & StaleJobReapable;
|
|
5089
6830
|
|
|
5090
|
-
export { type
|
|
6831
|
+
export { type PaymentRequired as $, type ApprovalContent as A, type SignedRequestDomain as B, type Currency as C, type DVMConfig as D, SignedRequestError as E, type SignedRequestFailure as F, type SignedRequestReplayStore as G, type SignedRequestSignOpts as H, type IncomingMessage as I, type JobRecord as J, type KVStore as K, type Logger as L, type SignedRequestStatementHeader as M, type SignedRequestVerifier as N, createSignedRequestVerifier as O, type PaymentContent as P, type QuoteConfig as Q, type ResolvedCreditConfig as R, type SDKJobContext as S, isZodSchema as T, UnsupportedCurrencyError as U, signedRequestStatementHeader as V, validateCurrency as W, type JsonValue as X, type FundingReceipt as Y, type ZodLike as Z, type JobReceipt as _, type DVMDescriptor as a, caip2ToX402Network as a$, type X402Version as a0, type MppxCredential as a1, type Message as a2, type MessageType as a3, type MppxChallenge as a4, X402_BATCH_SETTLEMENT_SCHEME as a5, X402_EXACT_SCHEME as a6, type X402Wallet as a7, type X402Config as a8, type X402ExactVersionSupport as a9, PayoutReporter as aA, PostgresTempoSessionStore as aB, RAIL_REFUNDABLE as aC, type ReceiptCredit as aD, type ReceiptOutcome as aE, type ReceiptPayment as aF, type ResourceInfo as aG, RevenueReporter as aH, SIGNED_ENVELOPE_FIELDS as aI, SIGNED_ENVELOPE_TYPES as aJ, type SettleResponse as aK, type StaleJobReapable as aL, StepCache as aM, type StepRecord as aN, type UnsignedDrainReceipt as aO, type UnsignedFundingReceipt as aP, type UnsignedJobReceipt as aQ, type VerifyResponse as aR, type X402ResponseBody as aS, type X402SelfRelayRpcFailureReason as aT, type X402TrackedChannel as aU, X402_DEFAULT_NETWORK as aV, X402_V1_VERSION as aW, X402_VERSION as aX, _testing as aY, buildPaymentRequiredV2 as aZ, buildPaymentRequirements as a_, type X402Receipt as aa, type TransactionalPayoutHook as ab, type CashuMeltCompleted as ac, type BuildPaymentRequirementsOpts as ad, type CapabilityDescriptor as ae, type CashuMode as af, type CreditDepositPayload as ag, type DrainReceipt as ah, type DrainReceiptEvent as ai, type ExactEvmPayload as aj, type ExactEvmPayloadAuthorization as ak, type FundingLot as al, type FundingMethod as am, type LotDebit as an, type LotDepletion as ao, type MppxServer as ap, NON_CHANNEL_BITCOIN_RAILS as aq, type NonChannelBitcoinRail as ar, type OutgoingMessage as as, type PaymentPayload as at, type PaymentPayloadV1 as au, type PaymentPayloadV2 as av, type PaymentRequiredV2 as aw, type PaymentRequirements as ax, type PaymentRequirementsV1 as ay, type PaymentRequirementsV2 as az, type ArtifactContent as b, type DrainRequestResult as b$, canonicaliseForSigning as b0, canonicalize as b1, chainIdFromCaip2 as b2, computeResultHash as b3, createDefaultReplayStore as b4, decodePayment as b5, decodePaymentRequiredHeader as b6, depleteLots as b7, encodePayment as b8, encodePaymentRequiredHeader as b9, verifyWithFacilitator as bA, wrapMppx as bB, x402NetworkToCaip2 as bC, type CreditDrainEnqueue as bD, type DVMAuthScheme as bE, type CreditLedgerLike as bF, type X402RefundSettlementGate as bG, type CreditSnapshot as bH, type DrawResult as bI, type X402SettlementStatus as bJ, type X402UnresolvedRefund as bK, type GrownDrawResult as bL, type DrawResolution as bM, type FundingRecord as bN, type CreditInvoiceRecord as bO, type InvoiceSettlement as bP, type BlockedInvoiceCursor as bQ, type InvoiceReconciliation as bR, type InvoiceWriteOff as bS, type DrawRecord as bT, type StalePendingDrawCursor as bU, type TempoCreditLossEvidence as bV, type CreditLedgerQuerier as bW, type TempoCreditLoss as bX, type X402CreditLossEvidence as bY, type X402CreditLoss as bZ, type DrainMethod as b_, encodeSettleResponseHeader as ba, exactEvmAuthorization as bb, fifoOrder as bc, inKindDrawMsats as bd, isArtifactMessage as be, isDrainReceipt as bf, isFundingReceipt as bg, isInKindDepletion as bh, isNonChannelBitcoinRail as bi, isPaymentRequestMessage as bj, isSignedJobReceipt as bk, isStaleJobReapable as bl, lotOwedSats as bm, netOwedSats as bn, paymentRequiredV2FromV1 as bo, settleWithFacilitator as bp, signDrainReceipt as bq, signFundingReceipt as br, signReceipt as bs, usdcContractByCaip2 as bt, usdcContractFor as bu, usdcDomainNameFor as bv, usdcDomainVersionFor as bw, verifyDrainReceipt as bx, verifyFundingReceipt as by, verifyReceipt as bz, type CancelContent as c, type X402SettlementReconciliationReason as c$, type BitcoinDepositLiability as c0, type CreditDrainRecord as c1, type ChannelDrainCursor as c2, type DrainWriteOff as c3, type DrainReleaseResult as c4, type DrainFulfilment as c5, type DrainTransitionResult as c6, type StreamableJobStore as c7, type ReceiptIssuingStore as c8, type RequestIdClaim as c9, type RevenueSkippedNoRailPayload as cA, type RevenueSkippedNoRailReason as cB, type Secp256k1AuthOpts as cC, type X402ChannelStorageOpts as cD, type X402RelayLockHolder as cE, type X402RelaySubmissionLock as cF, X402RelaySubmissionLockError as cG, allocateDrawValue as cH, clientCompatibilityAttributes as cI, clientCompatibilityMiddleware as cJ, clientUpgradeRequired as cK, isStreamableJobStore as cL, parseClientCapabilities as cM, parseClientCompatibility as cN, parseDvmClient as cO, parseProtocolVersion as cP, requireClientCompatibility as cQ, secp256k1Auth as cR, signedRequestInput as cS, type CreditDepositEnqueue as cT, type TopUpCapUnenforcedReason as cU, type X402SettlementIntent as cV, type X402SettlementCursor as cW, type X402SettlementWriteOff as cX, type X402FacilitatorAuth as cY, type X402BatchSettlementConfig as cZ, type X402PayoutObserver as c_, type RequestIdClaimResult as ca, type AppendOutgoingOptions as cb, type PaymentCreditDelta as cc, type VerifyAndCreditResult as cd, type JobCounters as ce, CLIENT_COMPATIBILITY_HEADERS as cf, type ClientCompatibility as cg, type ClientCompatibilityEnv as ch, type ClientCompatibilityGate as ci, type ClientCompatibilityRequirement as cj, type ClientSemVer as ck, type CreditFundingBasis as cl, type CreditInvoiceStatus as cm, CreditLedger as cn, CreditLedgerError as co, type CreditLedgerErrorCode as cp, type CreditLedgerErrorDetails as cq, type CreditLedgerPool as cr, type CreditStatus as cs, DRAIN_DELIVERY_RESERVE_SATS as ct, DVM_PROTOCOL_VERSION as cu, type DrainConflictReason as cv, type DrawRailValue as cw, type DrawStatus as cx, PostgresX402ChannelStorage as cy, type ReplayStoreBackend as cz, type CanonicalEnvelope as d, type CreditDrawReleaseEnqueue as d0, type CreateSignedRequestVerifierOpts as e, type CreditConfig as f, type CreditView as g, DEFAULT_CREDIT_MAX as h, DEFAULT_CREDIT_MIN as i, DEFAULT_CREDIT_TTL_SECONDS as j, type DVMRouteContext as k, type InputType as l, InvalidCurrencyError as m, type JobStatus as n, type JobStore as o, type PaymentMethod as p, type PriceValue as q, type ProgressContent as r, type PromptOpts as s, type QuoteContext as t, type QuoteResult as u, type ResponseContent as v, type SDKPaymentRequestOpts as w, SIGNED_REQUEST_AUTH_ID as x, SIGNED_REQUEST_STATEMENT_VERSION as y, type SignedRequestAudience as z };
|