@dvmkit/sdk 0.3.4-rc.10 → 0.4.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-C2AGARLM.js → chunk-2VGWYG4P.js} +5 -3
- package/dist/{chunk-A4CM2BD3.js → chunk-3MHE725Z.js} +1 -1
- package/dist/{chunk-VAMSJRJT.js → chunk-5LR6P7WN.js} +5 -3
- package/dist/{chunk-YYQG4R7T.js → chunk-74PUEZJ4.js} +6 -4
- package/dist/{chunk-MKI6OVW4.js → chunk-CZULM42S.js} +0 -51
- package/dist/{chunk-M4QDXD2A.js → chunk-FYRR2UEZ.js} +1 -1
- package/dist/{chunk-4UZN2GP5.js → chunk-GAOAYHZA.js} +209 -33
- package/dist/{chunk-Z4BNLUZF.js → chunk-IWQHUSMO.js} +3 -105
- package/dist/{chunk-4J32DTLT.js → chunk-IXR5RFK7.js} +82 -4
- package/dist/{chunk-2SD2W2BG.js → chunk-JH3SX3LY.js} +252 -26
- package/dist/{chunk-UZC4A4DM.js → chunk-K2YX2VRA.js} +2480 -206
- package/dist/chunk-KA6VN5FK.js +245 -0
- package/dist/chunk-LIRAHP7B.js +53 -0
- package/dist/{chunk-MG67KXU7.js → chunk-MZRJR2CW.js} +4 -2
- package/dist/{chunk-JDFLRNAZ.js → chunk-PB4FYZET.js} +67 -7
- package/dist/{chunk-BQ2NMWKE.js → chunk-PLG4XER6.js} +1 -1
- package/dist/{chunk-Z3EHBW2G.js → chunk-R4DQZFAL.js} +7 -5
- package/dist/chunk-RLTSCKRI.js +109 -0
- package/dist/{chunk-2KGG5ORW.js → chunk-TXX4GOUB.js} +3 -3
- package/dist/{chunk-YRDA7EWN.js → chunk-W2EQGDF5.js} +14 -10
- package/dist/{chunk-EAMZZ2B4.js → chunk-X7EZ5CLI.js} +59 -0
- package/dist/{chunk-7X6UDVCQ.js → chunk-Z4ZT5LNT.js} +66 -2
- package/dist/{credit-ledger-A35WH2TI.js → credit-ledger-MI2H7NEC.js} +3 -1
- package/dist/{credit-menu-0-UtJ3hh.d.ts → credit-menu-B8_p_vUz.d.ts} +133 -7
- package/dist/{fx-DhyT1Cjh.d.ts → fx-DuaYHvVn.d.ts} +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/internal/caller.d.ts +6 -6
- package/dist/internal/caller.js +28 -24
- package/dist/internal/server.d.ts +11 -85
- package/dist/internal/server.js +18 -16
- package/dist/{job-store-D3CcZp_g.d.ts → job-store-D6Rjj5Oi.d.ts} +1 -1
- package/dist/{lightning-backend-BhsyrBLa.d.ts → lightning-backend-BaeCPmx7.d.ts} +1 -1
- package/dist/{memory-credit-ledger-2LBZVMKJ.js → memory-credit-ledger-W2BAUYJP.js} +4 -2
- package/dist/{mpp-setup-7CGBKNUP.js → mpp-setup-RF5FXGGV.js} +5 -1
- package/dist/{payout-reporter-ZMOA37GN.js → payout-reporter-3Y2U55ZX.js} +1 -1
- package/dist/{postgres-job-store-MSD2HZOI.js → postgres-job-store-VLXLWXUQ.js} +1 -1
- package/dist/{revenue-reporter-EKJOGQYR.js → revenue-reporter-MAERPYGK.js} +1 -1
- package/dist/server/index.d.ts +14 -10
- package/dist/server/index.js +54 -17
- package/dist/{step-cache-v2yhyhSZ.d.ts → step-cache-Bi43A0JN.d.ts} +1769 -1549
- package/dist/{tempo-charge-store-4WX7FPOV.js → tempo-charge-store-Q3D7KYMH.js} +1 -1
- package/dist/tempo-lifecycle-OTHV757Y.js +14 -0
- package/dist/{tempo-session-store-LCSDHB7G.js → tempo-session-store-ZRNCOX7P.js} +1 -1
- package/dist/{tempo-wallet-RCGBBAJH.js → tempo-wallet-32JWQZKQ.js} +4 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/{usd-Cu9G6xv2.d.ts → usd-AZPVq4eU.d.ts} +1 -1
- package/dist/{x402-S47Q77UE.js → x402-CEDHGZDI.js} +17 -14
- package/package.json +1 -1
- package/dist/chunk-YHN3WMMF.js +0 -122
- package/dist/tempo-lifecycle-TJ62A7UX.js +0 -13
|
@@ -2,9 +2,9 @@ import { MiddlewareHandler, Context, Hono } from 'hono';
|
|
|
2
2
|
import { Challenge, Credential, Store, Method, Receipt } from 'mppx';
|
|
3
3
|
import { FacilitatorClient } from '@x402/core/server';
|
|
4
4
|
import { AuthorizerSigner, ChannelStorage, AutoSettlementConfig, Channel, ChannelUpdateResult } from '@x402/evm/batch-settlement/server';
|
|
5
|
-
import { Account, Client, Hex } from 'viem';
|
|
6
5
|
import { Pool, PoolClient } from 'pg';
|
|
7
6
|
import { PaymentPayload as PaymentPayload$1, PaymentRequirements as PaymentRequirements$1, SettleResponse as SettleResponse$1 } from '@x402/core/types';
|
|
7
|
+
import { Account, Client, Hex } from 'viem';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -111,7 +111,7 @@ interface X402FacilitatorAuth {
|
|
|
111
111
|
* Provider-side x402 configuration — where to receive stablecoin payments,
|
|
112
112
|
* how the dvmkit DVM should advertise the rail on its 402 response.
|
|
113
113
|
*/
|
|
114
|
-
interface X402Config {
|
|
114
|
+
interface X402Config$1 {
|
|
115
115
|
payTo: string;
|
|
116
116
|
/** CAIP-2 chain id (`eip155:8453` for Base). */
|
|
117
117
|
network: string;
|
|
@@ -320,7 +320,7 @@ interface PaymentRequiredV2 {
|
|
|
320
320
|
type PaymentRequired = X402ResponseBody | PaymentRequiredV2;
|
|
321
321
|
/** Inputs for `buildPaymentRequirements`. */
|
|
322
322
|
interface BuildPaymentRequirementsOpts {
|
|
323
|
-
x402Config: X402Config;
|
|
323
|
+
x402Config: X402Config$1;
|
|
324
324
|
requiredUsdcMicro: bigint;
|
|
325
325
|
resource: string;
|
|
326
326
|
description?: string;
|
|
@@ -699,1719 +699,1899 @@ declare function clientCompatibilityMiddleware(): MiddlewareHandler<ClientCompat
|
|
|
699
699
|
declare const requireClientCompatibility: ClientCompatibilityGate;
|
|
700
700
|
declare function clientUpgradeRequired(c: Context, compatibility: ClientCompatibility, requirement: ClientCompatibilityRequirement): Response;
|
|
701
701
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
702
|
+
interface HostedIntent {
|
|
703
|
+
admissionId?: string;
|
|
704
|
+
jobId?: string;
|
|
705
|
+
creditId?: string;
|
|
706
|
+
fundId?: string;
|
|
707
|
+
callerPubkey?: string;
|
|
708
|
+
fundingCommitment?: string;
|
|
709
|
+
paymentSeq?: number;
|
|
710
|
+
paymentArtifactHash?: string;
|
|
711
|
+
challengeId?: string;
|
|
712
|
+
invoicePaymentHash?: string;
|
|
713
|
+
invoiceRepairPaymentHash?: string;
|
|
714
|
+
amountMicro?: number;
|
|
715
|
+
}
|
|
716
|
+
interface HostedFundingReservation {
|
|
717
|
+
admissionId: string;
|
|
718
|
+
creditId: string;
|
|
719
|
+
fundId: string;
|
|
720
|
+
callerPubkey: string;
|
|
721
|
+
commitment: string;
|
|
722
|
+
paymentArtifactHash: string;
|
|
723
|
+
}
|
|
724
|
+
type Querier = Pick<PoolClient, "query">;
|
|
725
|
+
/** The control row is the common transaction lock for hosted obligation creation and closure. */
|
|
726
|
+
declare class HostingCleanupFence {
|
|
727
|
+
private readonly pool;
|
|
728
|
+
readonly dvmId: string;
|
|
729
|
+
readonly scope: string;
|
|
730
|
+
constructor(pool: Pool, dvmId: string, scope: string);
|
|
731
|
+
assertNewIntent(tx: Querier, intent?: HostedIntent): Promise<void>;
|
|
732
|
+
withNewIntent<T>(intent: HostedIntent | undefined, operation: (tx: PoolClient) => Promise<T>): Promise<T>;
|
|
733
|
+
assertFundingReservation(tx: Querier, reservation: HostedFundingReservation): Promise<void>;
|
|
734
|
+
fundingAdmission(tx: Querier, creditId: string, fundId: string, callerPubkey: string): Promise<string>;
|
|
735
|
+
withNewIntentOn<T>(db: Pool, intent: HostedIntent | undefined, operation: (tx: PoolClient) => Promise<T>): Promise<T>;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/** Query surface shared by the pool and an open Postgres transaction. */
|
|
739
|
+
type X402ChannelQuerier = Pick<Pool, "query">;
|
|
711
740
|
/**
|
|
712
|
-
*
|
|
741
|
+
* Durable phase of an irreversible x402 facilitator settlement.
|
|
742
|
+
*
|
|
743
|
+
* `written_off` is the operator's terminal: a settlement whose
|
|
744
|
+
* ledger leg cannot be completed, reviewed and closed so it leaves the repair
|
|
745
|
+
* queue without pretending it booked. It is deliberately reversible — a later
|
|
746
|
+
* reconcile still accepts it, because it moved nothing.
|
|
713
747
|
*/
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
748
|
+
type X402SettlementStatus = "prepared" | "submitting" | "settled" | "complete" | "written_off";
|
|
749
|
+
/** Recovery record binding one chain settlement to one credit-ledger effect. */
|
|
750
|
+
interface X402SettlementIntent {
|
|
751
|
+
settlementId: string;
|
|
752
|
+
channelId: string;
|
|
753
|
+
operation: "fund" | "refund";
|
|
754
|
+
effectId: string;
|
|
755
|
+
paymentId: string;
|
|
756
|
+
payload: PaymentPayload$1;
|
|
757
|
+
requirements: PaymentRequirements$1;
|
|
758
|
+
channelBefore: Channel;
|
|
720
759
|
/**
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
*
|
|
760
|
+
* The channel as it stood when the settlement was handed to the facilitator —
|
|
761
|
+
* after the refund's mandatory on-chain refresh, which {@link channelBefore}
|
|
762
|
+
* precedes.
|
|
763
|
+
*
|
|
764
|
+
* The facilitator sizes a cooperative refund from *live* state
|
|
765
|
+
* (`balance − chargedCumulativeAmount`) and signs it against the *live*
|
|
766
|
+
* `refundNonce`, so this is the snapshot chain evidence has to be derived
|
|
767
|
+
* from. `channelBefore` is a pre-verify mirror kept for the voucher restore,
|
|
768
|
+
* and whenever it was stale a refund that settled read as one that never
|
|
769
|
+
* happened. Absent on rows an older build wrote, and on any row that never
|
|
770
|
+
* reached `submitting`.
|
|
724
771
|
*/
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
772
|
+
channelAtSubmission?: Channel;
|
|
773
|
+
submissionBlock?: string;
|
|
774
|
+
pendingId?: string;
|
|
775
|
+
status: X402SettlementStatus;
|
|
776
|
+
response?: SettleResponse$1;
|
|
777
|
+
/** Epoch ms the row was first prepared. */
|
|
778
|
+
createdAt: number;
|
|
779
|
+
/** Epoch ms of the last status write — what the repair queue ages against. */
|
|
780
|
+
updatedAt: number;
|
|
781
|
+
/** Operator's reason, recorded by {@link PostgresX402ChannelStorage.writeOffSettlement}. */
|
|
782
|
+
writeOffNote?: string;
|
|
783
|
+
/** Epoch ms an operator reconciled or wrote this settlement off. */
|
|
784
|
+
resolvedAt?: number;
|
|
733
785
|
}
|
|
786
|
+
/** The caller-supplied half of a settlement row — everything the store does not stamp itself. */
|
|
787
|
+
type X402SettlementIdentity = Omit<X402SettlementIntent, "status" | "response" | "createdAt" | "updatedAt" | "writeOffNote" | "resolvedAt" | "channelAtSubmission">;
|
|
734
788
|
/**
|
|
735
|
-
*
|
|
736
|
-
* or drew the credit
|
|
737
|
-
* ledger — including implicit N=1 per-job payments, which fund and draw a
|
|
738
|
-
* single-use credit in the same request. A caller holding a DVM-signed
|
|
739
|
-
* balance trajectory plus its own caller-signed draw intents can prove
|
|
740
|
-
* misbehaviour from artifacts both sides already possess; that only works
|
|
741
|
-
* because the countersignature rides the receipt.
|
|
789
|
+
* Keyset position in the repair queue's `(created_at, settlement_id)` ordering.
|
|
742
790
|
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
745
|
-
*
|
|
791
|
+
* `createdAt` is Postgres's own rendering of the `TIMESTAMPTZ`, carried as text
|
|
792
|
+
* and handed straight back as one — never a `Date`. `created_at` has microsecond
|
|
793
|
+
* resolution and `Date` has millisecond, so a cursor that round-tripped through
|
|
794
|
+
* epoch ms would land *before* the row it was taken from and re-serve it on
|
|
795
|
+
* every page, which at `limit: 1` never advances at all.
|
|
746
796
|
*/
|
|
747
|
-
interface
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
/**
|
|
755
|
-
|
|
797
|
+
interface X402SettlementCursor {
|
|
798
|
+
createdAt: string;
|
|
799
|
+
settlementId: string;
|
|
800
|
+
}
|
|
801
|
+
/** One page of the repair queue, plus the position to resume from. */
|
|
802
|
+
interface X402SettlementPage {
|
|
803
|
+
settlements: X402SettlementIntent[];
|
|
804
|
+
/** Absent when this page was not full — there is nothing behind it. */
|
|
805
|
+
nextAfter?: X402SettlementCursor;
|
|
756
806
|
}
|
|
757
807
|
/**
|
|
758
|
-
*
|
|
808
|
+
* Fleet-wide serialisation of the self-relay's chain submissions.
|
|
759
809
|
*
|
|
760
|
-
*
|
|
761
|
-
*
|
|
762
|
-
*
|
|
763
|
-
*
|
|
764
|
-
*
|
|
810
|
+
* Narrow on purpose, and implemented by {@link PostgresX402ChannelStorage}: the
|
|
811
|
+
* relay asks for one thing — exclusive use of its gas EOA for the length of one
|
|
812
|
+
* submission — and must not acquire the ability to touch a channel or a
|
|
813
|
+
* settlement row. Handing it this instead of the host `Pool` is what keeps the
|
|
814
|
+
* acquire off the pool the channel transaction is already holding a client from.
|
|
765
815
|
*/
|
|
766
|
-
interface
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
dvm: string;
|
|
773
|
-
capability: string;
|
|
774
|
-
outcome: ReceiptOutcome;
|
|
775
|
-
/** Signed Unix millisecond lifecycle times; absent on legacy receipts. */
|
|
776
|
-
timing?: {
|
|
777
|
-
accepted_at_ms: number;
|
|
778
|
-
terminal_at_ms: number;
|
|
779
|
-
caller_wait_ms: number;
|
|
780
|
-
};
|
|
781
|
-
/** SDK-derived attribution on failure/cancellation; absent on completion and legacy receipts. */
|
|
782
|
-
ended_by?: "caller" | "provider";
|
|
783
|
-
/** Terminal reason on `failed`/`cancelled` (e.g. `worker_died_mid_job`); `null` on success. */
|
|
784
|
-
reason: string | null;
|
|
785
|
-
/** Per-DVM monotonic terminal counter. Gaps expose receipt suppression. */
|
|
786
|
-
seq: number;
|
|
787
|
-
/** Unix seconds at which the receipt was signed. */
|
|
788
|
-
issued_at: number;
|
|
789
|
-
/** Caller's x-only pubkey when signed-request auth was used; `null` otherwise. */
|
|
790
|
-
requester_pubkey: string | null;
|
|
791
|
-
paid: ReceiptPayment;
|
|
816
|
+
interface X402RelaySubmissionLock {
|
|
817
|
+
withRelaySubmissionLock<T>(key: string, operation: () => Promise<T>): Promise<T>;
|
|
818
|
+
}
|
|
819
|
+
interface X402ChannelStorageOpts {
|
|
820
|
+
/** Original hosted chain identity, persisted only on a channel's first write. */
|
|
821
|
+
hostedNetwork?: string;
|
|
792
822
|
/**
|
|
793
|
-
*
|
|
794
|
-
*
|
|
795
|
-
*
|
|
823
|
+
* Override the relay submission lock's wait ceiling, in milliseconds.
|
|
824
|
+
*
|
|
825
|
+
* A test seam: the production ceiling is tens of seconds, which no test can
|
|
826
|
+
* wait out, and the refusal path is exactly what needs pinning.
|
|
796
827
|
*/
|
|
797
|
-
|
|
798
|
-
/** x-only secp256k1 pubkey that signed this receipt (64 hex chars). */
|
|
799
|
-
receipt_pubkey: string;
|
|
800
|
-
/** BIP-340 Schnorr signature over the canonical receipt minus `signature` (128 hex chars). */
|
|
801
|
-
signature: string;
|
|
828
|
+
relaySubmissionLockTimeoutMs?: number;
|
|
802
829
|
/**
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
*
|
|
806
|
-
* settled (completed) job, the restored available balance on a released
|
|
807
|
-
* (failed/cancelled) one.
|
|
830
|
+
* Retire ledger credit when a fresh chain observation proves its channel
|
|
831
|
+
* has no unclaimed backing. Runs under the channel transaction so the
|
|
832
|
+
* channel-first, credit-second lock order stays fleet-wide.
|
|
808
833
|
*/
|
|
809
|
-
|
|
834
|
+
terminalizeUnbackedCredit?: (evidence: {
|
|
835
|
+
channelId: string;
|
|
836
|
+
channelBalanceNative: bigint;
|
|
837
|
+
totalClaimedNative: bigint;
|
|
838
|
+
observedAt: number;
|
|
839
|
+
}, tx: X402ChannelQuerier) => Promise<unknown>;
|
|
840
|
+
}
|
|
841
|
+
/** Best-effort identification of the backend holding the relay submission lock. */
|
|
842
|
+
interface X402RelayLockHolder {
|
|
843
|
+
pid: number;
|
|
844
|
+
applicationName?: string;
|
|
845
|
+
clientAddr?: string;
|
|
846
|
+
state?: string;
|
|
847
|
+
/** Seconds since the holder's current statement started. */
|
|
848
|
+
heldSeconds?: number;
|
|
810
849
|
}
|
|
811
|
-
type UnsignedJobReceipt = Omit<JobReceipt, "signature">;
|
|
812
|
-
type DrainReceiptEvent = "requested" | "parked" | "picked_up" | "sent" | "released";
|
|
813
850
|
/**
|
|
814
|
-
*
|
|
815
|
-
* lifecycle transition — `requested` (the balance debited into the drain
|
|
816
|
-
* liability), `parked` (Cashu notes locked to the caller's refund key,
|
|
817
|
-
* parked for pickup), `picked_up` (the caller collected them), `sent` (a
|
|
818
|
-
* Lightning/x402/mpp send-back settled).
|
|
851
|
+
* The fleet-wide relay submission lock was still held when the wait ceiling expired.
|
|
819
852
|
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
*
|
|
823
|
-
*
|
|
824
|
-
*
|
|
853
|
+
* Typed, and `retryable`, because it is an availability outcome rather than a
|
|
854
|
+
* fault in the payment: a fleet-mate whose RPC is retrying holds the lock, and
|
|
855
|
+
* the same request works once it lets go. It refuses rather than proceeding —
|
|
856
|
+
* two machines submitting from one EOA collide on the nonce — and it refuses
|
|
857
|
+
* rather than blocking, which is the defect: the old acquire had no
|
|
858
|
+
* `lock_timeout`, so a wedged holder pinned a host-pool client and a channel
|
|
859
|
+
* advisory lock for as long as it took.
|
|
860
|
+
*/
|
|
861
|
+
declare class X402RelaySubmissionLockError extends Error {
|
|
862
|
+
readonly lockKey: string;
|
|
863
|
+
readonly timeoutMs: number;
|
|
864
|
+
readonly holder?: X402RelayLockHolder | undefined;
|
|
865
|
+
readonly code = "x402_relay_submission_lock_timeout";
|
|
866
|
+
readonly retryable = true;
|
|
867
|
+
constructor(lockKey: string, timeoutMs: number, holder?: X402RelayLockHolder | undefined);
|
|
868
|
+
}
|
|
869
|
+
/** Outcome of {@link PostgresX402ChannelStorage.writeOffSettlement} — `written` is the transition bit. */
|
|
870
|
+
interface X402SettlementWriteOff {
|
|
871
|
+
intent: X402SettlementIntent;
|
|
872
|
+
written: boolean;
|
|
873
|
+
}
|
|
874
|
+
/** The refund standing between a channel and the credit it backs. */
|
|
875
|
+
interface X402UnresolvedRefund {
|
|
876
|
+
settlementId: string;
|
|
877
|
+
/** `${creditId}:${drainId}` — the drain this settlement belongs to. */
|
|
878
|
+
effectId: string;
|
|
879
|
+
status: X402SettlementStatus;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* The credit ledger's read into durable x402 settlement state.
|
|
825
883
|
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
884
|
+
* Narrow on purpose: the ledger asks one question — is this channel's refund
|
|
885
|
+
* still unresolved — and must not acquire the ability to move a settlement.
|
|
886
|
+
* Implemented by {@link PostgresX402ChannelStorage.pendingRefundSettlement}.
|
|
828
887
|
*/
|
|
829
|
-
interface
|
|
830
|
-
|
|
831
|
-
/** Discriminator separating this from a `JobReceipt` in mixed stores. */
|
|
832
|
-
kind: "credit_drain";
|
|
833
|
-
credit_id: string;
|
|
834
|
-
/** This drain's identifier — matches the caller-signed drain request. */
|
|
835
|
-
drain_id: string;
|
|
836
|
-
event: DrainReceiptEvent;
|
|
837
|
-
/** Payout rail: `cashu` | `x402` | `tempo`. */
|
|
838
|
-
method: string;
|
|
839
|
-
/** Amount reclaimed, micro-units of the credit's currency. */
|
|
840
|
-
amount_micro: number;
|
|
841
|
-
/**
|
|
842
|
-
* Credit balance left after the drain debit, in micro-units. It includes
|
|
843
|
-
* pending job holds at request time and is fixed across this drain's event receipts.
|
|
844
|
-
*/
|
|
845
|
-
balance_after_micro: number;
|
|
846
|
-
/** Per-credit monotonic sequence the drain consumed, shared with draws. */
|
|
847
|
-
ledger_seq: number;
|
|
848
|
-
/** Unix seconds at which this receipt was signed. */
|
|
849
|
-
issued_at: number;
|
|
850
|
-
/** The credit owner's x-only pubkey — who the money is owed to. */
|
|
851
|
-
caller_pubkey: string;
|
|
852
|
-
/** Immutable DVM identity bound into the builder attestation. */
|
|
853
|
-
dvm_id: string;
|
|
854
|
-
/** DVM slug, as bound in the builder attestation. */
|
|
855
|
-
dvm: string;
|
|
856
|
-
/** x-only secp256k1 pubkey that signed this receipt (64 hex chars). */
|
|
857
|
-
receipt_pubkey: string;
|
|
858
|
-
/** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
|
|
859
|
-
signature: string;
|
|
860
|
-
}
|
|
861
|
-
type UnsignedDrainReceipt = Omit<DrainReceipt, "signature">;
|
|
862
|
-
/**
|
|
863
|
-
* A DVM-signed proof that one payment funded a prepaid credit.
|
|
864
|
-
* The balance and sequence are the values fixed when the funding committed;
|
|
865
|
-
* retries return this original artifact even when the credit moved later.
|
|
866
|
-
*/
|
|
867
|
-
interface FundingReceipt {
|
|
868
|
-
v: 1;
|
|
869
|
-
/** Discriminator separating funding evidence from job and drain receipts. */
|
|
870
|
-
kind: "credit_funding";
|
|
871
|
-
credit_id: string;
|
|
872
|
-
/** Caller fund id, or the attached rail payment id for fund-and-draw. */
|
|
873
|
-
fund_id: string;
|
|
874
|
-
/** Amount credited, in micro-units of the credit currency. */
|
|
875
|
-
funded_micro: number;
|
|
876
|
-
/** Available balance immediately after this funding operation committed. */
|
|
877
|
-
balance_after_micro: number;
|
|
878
|
-
/** Per-credit high-water sequence at that same point. */
|
|
879
|
-
ledger_seq: number;
|
|
880
|
-
/** Unix seconds at which the funding artifact was first issued. */
|
|
881
|
-
issued_at: number;
|
|
882
|
-
/** The credit owner's x-only request-signing pubkey. */
|
|
883
|
-
caller_pubkey: string;
|
|
884
|
-
/** Immutable DVM identity bound into the builder attestation. */
|
|
885
|
-
dvm_id: string;
|
|
886
|
-
/** DVM slug, as bound in the builder attestation. */
|
|
887
|
-
dvm: string;
|
|
888
|
-
receipt_pubkey: string;
|
|
889
|
-
/** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
|
|
890
|
-
signature: string;
|
|
891
|
-
}
|
|
892
|
-
type UnsignedFundingReceipt = Omit<FundingReceipt, "signature">;
|
|
893
|
-
/**
|
|
894
|
-
* Hash the delivered result so a receipt binds to *what* was returned, not
|
|
895
|
-
* just that something was.
|
|
896
|
-
*
|
|
897
|
-
* `sha256(canonicalize({ summary, artifact_hashes }))`, where each artifact
|
|
898
|
-
* hash is `sha256(canonicalize(artifactContent))` in emission order (`[]`
|
|
899
|
-
* when the job emitted none). Hashing the artifact *content object* rather
|
|
900
|
-
* than trusting `content.sha256` — which is optional and which `ctx.artifact`
|
|
901
|
-
* never fills in — keeps the hash recomputable by any consumer from exactly
|
|
902
|
-
* the messages it received.
|
|
903
|
-
*
|
|
904
|
-
* **Pass `summary` only for a `completed` job.** On `failed`/`cancelled` the
|
|
905
|
-
* SDK's durable terminal writes the terminal *reason* into the job's summary
|
|
906
|
-
* column, so a verifier recomputing a non-completed receipt must pass
|
|
907
|
-
* `undefined` here and read the reason off `receipt.reason` instead. The
|
|
908
|
-
* issuer does the same (`resultHashFor` in `sdk/server/receipt-issuer.ts`).
|
|
909
|
-
*/
|
|
910
|
-
declare function computeResultHash(summary: string | undefined, artifactContents: JsonValue[]): string;
|
|
911
|
-
declare function signReceipt(unsigned: UnsignedJobReceipt, secretHex: string): JobReceipt;
|
|
912
|
-
/**
|
|
913
|
-
* Verify a receipt's self-consistency: the BIP-340 signature over the
|
|
914
|
-
* canonical payload (minus `signature`) under the receipt's own
|
|
915
|
-
* `receipt_pubkey`. Returns `false` rather than throwing on malformed input.
|
|
916
|
-
*
|
|
917
|
-
* This is only the inner link. A consumer that wants provenance must also
|
|
918
|
-
* check `receipt_pubkey` against `/v1/info#builder`'s attested
|
|
919
|
-
* `receipt_pubkey` and verify that attestation under the builder pubkey
|
|
920
|
-
* (`verifyAttestation` in `builder-identity.ts`).
|
|
921
|
-
*/
|
|
922
|
-
declare function verifyReceipt(receipt: JobReceipt): boolean;
|
|
923
|
-
/**
|
|
924
|
-
* Structural guard on a receipt that arrived from outside this process — off a
|
|
925
|
-
* `/v1/job` response, or back out of the hand-editable `~/.dvm/receipts.jsonl`.
|
|
926
|
-
*
|
|
927
|
-
* Checks only that the load-bearing fields are the right *kind* of thing; it
|
|
928
|
-
* deliberately does not judge the receipt. A well-formed receipt whose
|
|
929
|
-
* signature doesn't hold is `invalid` and the caller must see it said so. A
|
|
930
|
-
* body missing one of these fields is a different animal: consumers dereference
|
|
931
|
-
* them unconditionally (`receiptDisplay`'s `paid.msats`, the attestation link's
|
|
932
|
-
* `receipt_pubkey`), so an unguarded one throws `TypeError` and surfaces as an
|
|
933
|
-
* `internal` exit — and it could never have verified anyway, since the
|
|
934
|
-
* signature covers the whole canonical body. Refusing it costs no proof.
|
|
935
|
-
*
|
|
936
|
-
* One guard for both directions on purpose: what can't come in over the wire
|
|
937
|
-
* must not be able to come back out of the file.
|
|
938
|
-
*
|
|
939
|
-
* Release coupling: adding or requiring a field here changes both the SDK's
|
|
940
|
-
* emitted wire shape and the caller CLI's accepted shape. Name the first-party
|
|
941
|
-
* fleet redeploy as a prerequisite in the release note before publishing the
|
|
942
|
-
* stricter caller; see `public compatibility guide`.
|
|
943
|
-
*/
|
|
944
|
-
declare function isSignedJobReceipt(value: unknown): value is JobReceipt;
|
|
945
|
-
/** Sign an unsigned drain receipt — same canonicalisation as {@link signReceipt}. */
|
|
946
|
-
declare function signDrainReceipt(unsigned: UnsignedDrainReceipt, secretHex: string): DrainReceipt;
|
|
947
|
-
/** Sign an unsigned funding receipt using the common receipt canonicalisation. */
|
|
948
|
-
declare function signFundingReceipt(unsigned: UnsignedFundingReceipt, secretHex: string): FundingReceipt;
|
|
949
|
-
declare function isFundingReceipt(value: unknown): value is FundingReceipt;
|
|
950
|
-
declare function verifyFundingReceipt(receipt: FundingReceipt): boolean;
|
|
951
|
-
/**
|
|
952
|
-
* Structural guard on a drain receipt from outside this process — off a
|
|
953
|
-
* `/v1/credit` drain response, or back out of `~/.dvm/credits.json`.
|
|
954
|
-
*
|
|
955
|
-
* Same contract and same reasoning as {@link isSignedJobReceipt}: both homes
|
|
956
|
-
* type these `unknown[]` because neither the wire nor a hand-editable file is
|
|
957
|
-
* evidence of shape, and the chain walk dereferences `receipt_pubkey`
|
|
958
|
-
* unconditionally. A body missing one of these fields could never have carried
|
|
959
|
-
* a valid signature anyway, so refusing it costs no proof.
|
|
960
|
-
* Required-field changes follow the fleet-before-caller release note linked
|
|
961
|
-
* from {@link isSignedJobReceipt}.
|
|
962
|
-
*/
|
|
963
|
-
declare function isDrainReceipt(value: unknown): value is DrainReceipt;
|
|
964
|
-
/**
|
|
965
|
-
* Verify a drain receipt's self-consistency, mirroring {@link verifyReceipt}
|
|
966
|
-
* the same provenance caveat applies: check `receipt_pubkey` against the
|
|
967
|
-
* DVM's attested `receipt_pubkey` for the outer link.
|
|
968
|
-
*/
|
|
969
|
-
declare function verifyDrainReceipt(receipt: DrainReceipt): boolean;
|
|
970
|
-
|
|
971
|
-
/**
|
|
972
|
-
* Cashu and Lightning credits owe unused sats back at each deposit's funding
|
|
973
|
-
* rate. Keep the sats/micro basis per funding lot: a pooled ratio loses the
|
|
974
|
-
* basis of top-ups made at different rates. Settled draws and reclaims share
|
|
975
|
-
* these lots so their total equals the sats funded.
|
|
976
|
-
*/
|
|
977
|
-
/**
|
|
978
|
-
* The rails whose credits carry a Bitcoin deposit this ledger owes back in
|
|
979
|
-
* kind — Cashu and Lightning funding, both of which leave sats in the
|
|
980
|
-
* provider's own custody.
|
|
981
|
-
*
|
|
982
|
-
* Declared as its own list rather than derived by subtracting the stablecoin
|
|
983
|
-
* rails: a channel-backed credit is in-kind *structurally* (the escrow retains
|
|
984
|
-
* the unused value), so it needs no lots at all, and a rail added to
|
|
985
|
-
* {@link FundingMethod} must be a decision here rather than a default.
|
|
986
|
-
*/
|
|
987
|
-
declare const NON_CHANNEL_BITCOIN_RAILS: readonly ["cashu", "lightning"];
|
|
988
|
-
type NonChannelBitcoinRail = (typeof NON_CHANNEL_BITCOIN_RAILS)[number];
|
|
989
|
-
declare function isNonChannelBitcoinRail(rail: string | null | undefined): rail is NonChannelBitcoinRail;
|
|
990
|
-
/**
|
|
991
|
-
* One funding event's deposit: the sats that arrived and the credit micro they
|
|
992
|
-
* bought, with however much of that micro is still unspent.
|
|
993
|
-
*
|
|
994
|
-
* `remainingMicro` is depleted FIFO by settled draws and by reclaims, so the
|
|
995
|
-
* sum of a credit's lots tracks `credits.balance_micro` exactly — with one
|
|
996
|
-
* deliberate exception: an expiry release zeroes the balance and
|
|
997
|
-
* leaves the lots standing, because a revival restores that balance and its
|
|
998
|
-
* in-kind basis has to still be there. A released credit is excluded from the
|
|
999
|
-
* sweep floor by `BITCOIN_CREDIT_PREDICATE`'s own `balance_micro > 0`, so the
|
|
1000
|
-
* over-coverage is never counted as sats the builder owes back.
|
|
1001
|
-
*/
|
|
1002
|
-
interface FundingLot {
|
|
1003
|
-
lotId: string;
|
|
1004
|
-
creditId: string;
|
|
1005
|
-
rail: NonChannelBitcoinRail;
|
|
1006
|
-
/** Sats this funding delivered. `0` on a credit funded before a rail basis was recorded. */
|
|
1007
|
-
satsFunded: number;
|
|
1008
|
-
/** Credit micro this funding bought — the lot's denominator, never depleted. */
|
|
1009
|
-
creditedMicro: number;
|
|
1010
|
-
remainingMicro: number;
|
|
1011
|
-
/** Rail settlement reference, provenance only. */
|
|
1012
|
-
fundingRef: string | null;
|
|
1013
|
-
createdAt: number;
|
|
1014
|
-
}
|
|
1015
|
-
interface LotDebit {
|
|
1016
|
-
lotId: string;
|
|
1017
|
-
micro: number;
|
|
1018
|
-
/** Decrease in this lot's floored sats obligation after taking the micro debit. */
|
|
1019
|
-
sats: number;
|
|
1020
|
-
}
|
|
1021
|
-
interface LotDepletion {
|
|
1022
|
-
debits: LotDebit[];
|
|
1023
|
-
satsOwed: number;
|
|
1024
|
-
/** Micro the lots could not cover. Non-zero means the credit's lots are short. */
|
|
1025
|
-
uncoveredMicro: number;
|
|
1026
|
-
/** Of the micro taken, how much came out of a lot carrying a real sats basis. */
|
|
1027
|
-
backedMicro: number;
|
|
888
|
+
interface X402RefundSettlementGate {
|
|
889
|
+
pendingRefundSettlement(channelId: string, statuses: X402SettlementStatus[]): Promise<X402UnresolvedRefund | undefined>;
|
|
1028
890
|
}
|
|
1029
891
|
/**
|
|
1030
|
-
*
|
|
1031
|
-
* lotSats(before) - lotSats(after), not the independently floored slice: small
|
|
1032
|
-
* draws then telescope to the full deposit instead of losing up to one sat
|
|
1033
|
-
* per draw. Fractional remaining sats stay with the provider.
|
|
1034
|
-
*/
|
|
1035
|
-
declare function depleteLots(lots: FundingLot[], amountMicro: number): LotDepletion;
|
|
1036
|
-
/**
|
|
1037
|
-
* What a settling draw's fiat debit was worth in kind, in millisatoshis
|
|
1038
|
-
* `null` where the lots cannot price it.
|
|
1039
|
-
*
|
|
1040
|
-
* This is the single sats authority for a non-channel Bitcoin credit. The
|
|
1041
|
-
* settle already depletes the lots the draw consumed, so the figure costs no
|
|
1042
|
-
* extra read: it is `depletion.satsOwed` in the ledger's own msat unit, and
|
|
1043
|
-
* stamping it on `credit_draws.draw_msats` is what makes
|
|
1044
|
-
* `Σ settled draw sats + reclaim sats == Σ sats funded` close per credit.
|
|
1045
|
-
*
|
|
1046
|
-
* `null` follows {@link isInKindDepletion} exactly, so a settle and a reclaim
|
|
1047
|
-
* fall back together: lots short of the balance, or covering lots with no sats
|
|
1048
|
-
* basis, keep the pooled pro-rata figure rather than blending a lot rate with
|
|
1049
|
-
* a blended one. Mixed coverage counts as unpriceable for the same reason it
|
|
1050
|
-
* does on the reclaim.
|
|
1051
|
-
*/
|
|
1052
|
-
declare function inKindDrawMsats(depletion: LotDepletion, amountMicro: number): number | null;
|
|
1053
|
-
/**
|
|
1054
|
-
* What these lots owe in kind if every remaining micro were reclaimed now —
|
|
1055
|
-
* the deposit-liability figure, and the floor the hub must never be swept
|
|
1056
|
-
* below.
|
|
1057
|
-
*/
|
|
1058
|
-
declare function lotOwedSats(lots: FundingLot[]): number;
|
|
1059
|
-
/**
|
|
1060
|
-
* Refund delivery uses a fixed eight-sat reserve so owed_sats cannot shrink
|
|
1061
|
-
* after it is promised. Extra delivery cost is absorbed by the provider;
|
|
1062
|
-
* unused reserve stays with the provider, like fractional-sat dust.
|
|
1063
|
-
*/
|
|
1064
|
-
declare const DRAIN_DELIVERY_RESERVE_SATS = 8;
|
|
1065
|
-
/**
|
|
1066
|
-
* The figure a reclaim publishes: its in-kind gross less
|
|
1067
|
-
* {@link DRAIN_DELIVERY_RESERVE_SATS}, floored at zero.
|
|
892
|
+
* Postgres implementation of x402's server-side {@link ChannelStorage}.
|
|
1068
893
|
*
|
|
1069
|
-
*
|
|
1070
|
-
*
|
|
1071
|
-
*
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
/**
|
|
1075
|
-
* The order lots deplete in: oldest first, `lotId` breaking a tie.
|
|
894
|
+
* `updateChannel` takes an advisory transaction lock before it reads the row,
|
|
895
|
+
* so the read/callback/write sequence is atomic even for a first deposit whose
|
|
896
|
+
* row does not exist yet. {@link withChannelTransaction} exposes the same
|
|
897
|
+
* transaction to the credit ledger: the fixed lock order is channel first,
|
|
898
|
+
* credit second, and no credit-ledger path calls back into this store.
|
|
1076
899
|
*
|
|
1077
|
-
*
|
|
1078
|
-
*
|
|
1079
|
-
*
|
|
1080
|
-
*
|
|
1081
|
-
*
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
* would claim a value neither basis supports.
|
|
1088
|
-
*/
|
|
1089
|
-
declare function isInKindDepletion(depletion: LotDepletion, amountMicro: number): boolean;
|
|
1090
|
-
|
|
1091
|
-
/** The rails a payout lands on. Lightning never pays out: credits fund straight into the receive wallet. */
|
|
1092
|
-
type PayoutRail = "cashu" | "x402" | "tempo";
|
|
1093
|
-
/**
|
|
1094
|
-
* Transfers aggregate payments: a Cashu melt, x402 batch to pay_to, or Tempo recipient
|
|
1095
|
-
* settlement/close.
|
|
1096
|
-
*/
|
|
1097
|
-
type PayoutKind = "melt" | "batch" | "close" | "settle";
|
|
1098
|
-
type PayoutRepairKind = "x402_settlement_reconciled" | "lightning_invoice_reconciled" | "tempo_close_reconciled";
|
|
1099
|
-
/**
|
|
1100
|
-
* One landed movement into builder custody. Platform retries deduplicate by (dvmId,
|
|
1101
|
-
* payoutId); repaired redelivery upgrades that movement rather than adding another.
|
|
900
|
+
* Two pools, and the split is load-bearing. The settlement markers
|
|
901
|
+
* must write *outside* the ledger transaction, which means a second connection
|
|
902
|
+
* taken while the transaction's client is still checked out — on the shared
|
|
903
|
+
* host pool that is a self-deadlock the moment concurrent deposits reach its
|
|
904
|
+
* `max`, with no error and no timeout because every waiter is also a holder.
|
|
905
|
+
* They therefore run on a small dedicated pool that acquires nothing itself,
|
|
906
|
+
* so nothing can ever queue behind them. The rule this rests on: a client held
|
|
907
|
+
* from one pool may only ever be used to acquire from a pool that is terminal.
|
|
908
|
+
* {@link withFleetSettlementLock} and {@link withRelaySubmissionLock} are the
|
|
909
|
+
* other two nesting sites and are bounded a different way — see their docs.
|
|
1102
910
|
*/
|
|
1103
|
-
|
|
911
|
+
declare class PostgresX402ChannelStorage implements ChannelStorage {
|
|
912
|
+
private readonly pool;
|
|
913
|
+
private hostingCleanupFence?;
|
|
914
|
+
useHostingCleanupFence(fence: HostingCleanupFence): void;
|
|
915
|
+
assertHostingFundingReservation(reservation: HostedFundingReservation | undefined): Promise<void>;
|
|
916
|
+
private readonly transaction;
|
|
917
|
+
private readonly hostedFunding;
|
|
918
|
+
private readonly refundBase;
|
|
919
|
+
private settlementPool?;
|
|
920
|
+
private fleetLock;
|
|
921
|
+
private fleetLockQueueDepth;
|
|
922
|
+
private readonly relayLockTimeoutMs;
|
|
923
|
+
private readonly terminalizeUnbackedCredit?;
|
|
924
|
+
private readonly hostedNetwork?;
|
|
925
|
+
constructor(pool: Pool, opts?: X402ChannelStorageOpts);
|
|
926
|
+
init(): Promise<void>;
|
|
927
|
+
/**
|
|
928
|
+
* Read the fleet's durable "claimed funds are awaiting transfer" marker.
|
|
929
|
+
*
|
|
930
|
+
* Upstream keeps this in process memory, which a multi-machine fleet cannot
|
|
931
|
+
* share: the machine that won a claim tick is rarely the one that wins the
|
|
932
|
+
* next settle tick. An unset scope reads false, so a receiver that has never
|
|
933
|
+
* claimed never submits a settlement.
|
|
934
|
+
*
|
|
935
|
+
* Both accessors run on the settlement pool: every caller reads or writes the
|
|
936
|
+
* marker while {@link withFleetSettlementLock} holds a host-pool client, which
|
|
937
|
+
* is the nesting the split exists to keep off a non-terminal pool.
|
|
938
|
+
*/
|
|
939
|
+
isSettlePending(scope: string): Promise<boolean>;
|
|
940
|
+
/** Record whether a claim's funds still need a `settle(receiver, token)` transfer. */
|
|
941
|
+
setSettlePending(scope: string, pending: boolean): Promise<void>;
|
|
942
|
+
/**
|
|
943
|
+
* Look up the durable settlement state for one credit-ledger effect.
|
|
944
|
+
*
|
|
945
|
+
* On the dedicated settlement pool like every other marker, and for a sharper
|
|
946
|
+
* reason than they had: this and {@link prepareSettlement} now run *inside*
|
|
947
|
+
* the channel transaction, which holds a host-pool client for its
|
|
948
|
+
* whole duration. Taking a second client from that same pool is the
|
|
949
|
+
* self-deadlock fixed — at pool max every client is a holder and a
|
|
950
|
+
* waiter — and moving these calls under the transaction is exactly what would
|
|
951
|
+
* reintroduce it.
|
|
952
|
+
*/
|
|
953
|
+
getSettlement(operation: "fund" | "refund", effectId: string): Promise<X402SettlementIntent | undefined>;
|
|
1104
954
|
/**
|
|
1105
|
-
*
|
|
955
|
+
* The refund on this channel that is neither finished nor abandoned, if there
|
|
956
|
+
* is one — the credit ledger's spend gate.
|
|
957
|
+
*
|
|
958
|
+
* `statuses` is the caller's, because the two gates disagree by design: a
|
|
959
|
+
* draw is blocked by {@link X402_SPEND_BLOCKING_SETTLEMENT_STATUSES}, a fresh
|
|
960
|
+
* drain only by {@link X402_WEDGED_SETTLEMENT_STATUSES}.
|
|
961
|
+
*
|
|
962
|
+
* On the dedicated settlement pool, and that is what makes the gate callable
|
|
963
|
+
* at all: a draw routinely runs inside an open channel transaction holding a
|
|
964
|
+
* host-pool client, and taking a second one from that pool is the
|
|
965
|
+
* self-deadlock. The oldest blocking row wins, so a channel that somehow
|
|
966
|
+
* accumulated two names the one an operator should reach for first.
|
|
967
|
+
*
|
|
968
|
+
* Projects three columns rather than decoding a whole {@link
|
|
969
|
+
* X402SettlementIntent}: the decoder refuses a legacy row that carries
|
|
970
|
+
* no reconciliation snapshot, and a gate that throws on one would take every
|
|
971
|
+
* draw on that channel down with it — when the row is exactly the kind that
|
|
972
|
+
* ought to block them.
|
|
1106
973
|
*/
|
|
1107
|
-
|
|
1108
|
-
/** Required. The DVM's own id for the movement — `cashu:melt:<quote>`, `x402:batch:<uuid>`, `tempo:<tx>`. */
|
|
1109
|
-
payoutId: string;
|
|
1110
|
-
rail: PayoutRail;
|
|
1111
|
-
kind: PayoutKind;
|
|
974
|
+
pendingRefundSettlement(channelId: string, statuses: X402SettlementStatus[]): Promise<X402UnresolvedRefund | undefined>;
|
|
1112
975
|
/**
|
|
1113
|
-
*
|
|
976
|
+
* Read one settlement by its own identifier — the handle an operator holds
|
|
977
|
+
* Same dedicated pool as {@link getSettlement}, for the same
|
|
978
|
+
* reason: the repair reads this while a channel transaction may be open.
|
|
1114
979
|
*/
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
recipient: string | null;
|
|
980
|
+
getSettlementById(settlementId: string): Promise<X402SettlementIntent | undefined>;
|
|
981
|
+
/** Lazily move a legacy settlement row onto its authorization/effect-scoped replay key. */
|
|
982
|
+
normalizeLegacyPaymentId(settlementId: string, operation: "fund" | "refund", legacyPaymentId: string, paymentId: string): Promise<X402SettlementIntent>;
|
|
1119
983
|
/**
|
|
1120
|
-
*
|
|
984
|
+
* The operator's repair queue: settlements stuck between "chain paid" and
|
|
985
|
+
* "ledger booked", oldest write first.
|
|
986
|
+
*
|
|
987
|
+
* Keyset-paginated on `(created_at, settlement_id)` for the reason the
|
|
988
|
+
* blocked-invoice queue is: a wedged row never self-clears, so a fixed first
|
|
989
|
+
* page of rows the operator has declined to act on would starve everything
|
|
990
|
+
* behind it on every run, forever.
|
|
991
|
+
*
|
|
992
|
+
* The keyset is on `created_at`, which never moves, while `updatedBeforeMs`
|
|
993
|
+
* filters on `updated_at`, which does — so a row an operator touches mid-walk
|
|
994
|
+
* can leave the queue but can never jump the cursor and be skipped. The cursor
|
|
995
|
+
* itself is Postgres's text rendering of that timestamp, handed back verbatim;
|
|
996
|
+
* see {@link X402SettlementCursor} for why a `Date` cannot carry it.
|
|
997
|
+
*
|
|
998
|
+
* `updatedBeforeMs` is the staleness floor. A healthy settlement passes
|
|
999
|
+
* through `submitting` and `settled` inside one request, so anything younger
|
|
1000
|
+
* than the floor is in flight rather than wedged and must not be offered as
|
|
1001
|
+
* repairable work.
|
|
1121
1002
|
*/
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
}
|
|
1129
|
-
interface PayoutWedge {
|
|
1130
|
-
id: string;
|
|
1003
|
+
listSettlements(args: {
|
|
1004
|
+
statuses: X402SettlementStatus[];
|
|
1005
|
+
updatedBeforeMs: number;
|
|
1006
|
+
limit: number;
|
|
1007
|
+
after?: X402SettlementCursor;
|
|
1008
|
+
}): Promise<X402SettlementPage>;
|
|
1131
1009
|
/**
|
|
1132
|
-
*
|
|
1133
|
-
*
|
|
1134
|
-
*
|
|
1135
|
-
*
|
|
1010
|
+
* Close a wedged settlement without booking anything.
|
|
1011
|
+
*
|
|
1012
|
+
* A single-statement CAS on the wedged statuses, on the settlement pool —
|
|
1013
|
+
* there is no ledger effect to be atomic with, which is the whole point:
|
|
1014
|
+
* the write-off records that an operator reviewed this settlement and is not
|
|
1015
|
+
* completing it, and no dvmkit table moves. Idempotent, and the first note
|
|
1016
|
+
* wins; `written` is the transition bit the caller logs on, so the one place
|
|
1017
|
+
* the amount is ever recorded gets exactly one entry.
|
|
1018
|
+
*
|
|
1019
|
+
* Reversible by construction: a later reconcile calls
|
|
1020
|
+
* {@link recordSettlementResponse}, whose `status <> 'complete'` CAS lifts a
|
|
1021
|
+
* written-off row back to `settled`.
|
|
1136
1022
|
*/
|
|
1137
|
-
|
|
1138
|
-
/**
|
|
1139
|
-
|
|
1140
|
-
/** Retry count where the retry is automatic, else null. */
|
|
1141
|
-
attempts: number | null;
|
|
1142
|
-
/** Epoch ms of the next automatic retry, else null. */
|
|
1143
|
-
nextRetry: number | null;
|
|
1144
|
-
lastError: string | null;
|
|
1145
|
-
/** Rail-native amount held up, when known. */
|
|
1146
|
-
native?: number;
|
|
1147
|
-
}
|
|
1148
|
-
interface PayoutPool {
|
|
1149
|
-
id: string;
|
|
1150
|
-
native: number;
|
|
1151
|
-
label?: string;
|
|
1152
|
-
}
|
|
1153
|
-
/**
|
|
1154
|
-
* A replaceable snapshot of one rail’s pending builder funds. Best-effort delivery uses no
|
|
1155
|
-
* durable queue; a later tick supersedes a lost report.
|
|
1156
|
-
*/
|
|
1157
|
-
interface PayoutPendingPayload {
|
|
1158
|
-
dvmId: string;
|
|
1159
|
-
rail: PayoutRail;
|
|
1160
|
-
/** Everything on this rail still moving toward the builder, in atomic units. */
|
|
1161
|
-
poolNative: number;
|
|
1162
|
-
nativeAsset: "sats" | "usdc";
|
|
1163
|
-
items: number;
|
|
1164
|
-
pools: PayoutPool[];
|
|
1165
|
-
wedged: PayoutWedge[];
|
|
1166
|
-
/** This DVM's clock when the snapshot was taken (epoch ms). */
|
|
1167
|
-
snapshotAt: number;
|
|
1168
|
-
}
|
|
1169
|
-
interface PayoutTransport {
|
|
1023
|
+
writeOffSettlement(settlementId: string, note: string | undefined, nowMs: number): Promise<X402SettlementWriteOff | undefined>;
|
|
1024
|
+
/** Persist the reconciliation identity, refreshing a prepared retry's transient reservation. */
|
|
1025
|
+
prepareSettlement(intent: X402SettlementIdentity, hostingFunding?: HostedFundingReservation): Promise<X402SettlementIntent>;
|
|
1170
1026
|
/**
|
|
1171
|
-
*
|
|
1172
|
-
*
|
|
1173
|
-
*
|
|
1174
|
-
*
|
|
1027
|
+
* Mark that the next operation is the ambiguous external-call boundary.
|
|
1028
|
+
*
|
|
1029
|
+
* `channelAtSubmission` is the live channel the facilitator is about to size
|
|
1030
|
+
* and sign the settlement against — bookmarked here, with the
|
|
1031
|
+
* block height, because these two are the only description of that instant
|
|
1032
|
+
* that survives the call.
|
|
1175
1033
|
*/
|
|
1176
|
-
|
|
1177
|
-
/**
|
|
1178
|
-
|
|
1179
|
-
/**
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1034
|
+
markSettlementSubmitting(settlementId: string, submissionBlock: string, channelAtSubmission?: Channel): Promise<void>;
|
|
1035
|
+
/** Return an explicitly failed facilitator submission to the safe retry phase. */
|
|
1036
|
+
markSettlementRetryable(settlementId: string): Promise<void>;
|
|
1037
|
+
/**
|
|
1038
|
+
* Record a successful facilitator response independently of the enclosing
|
|
1039
|
+
* ledger transaction.
|
|
1040
|
+
*
|
|
1041
|
+
* The write-off columns are cleared, not carried: this CAS is what lifts a
|
|
1042
|
+
* `written_off` row back to `settled` (which is what makes the write-off
|
|
1043
|
+
* reversible), and a row that is no longer written off must not still be
|
|
1044
|
+
* carrying the note and resolution timestamp of a decision that has been
|
|
1045
|
+
* reversed. On the live path the columns are already `NULL` and this is a
|
|
1046
|
+
* no-op.
|
|
1047
|
+
*/
|
|
1048
|
+
recordSettlementResponse(settlementId: string, response: SettleResponse$1): Promise<void>;
|
|
1049
|
+
/**
|
|
1050
|
+
* Mark the external settlement and ledger effect complete in the active transaction.
|
|
1051
|
+
*
|
|
1052
|
+
* `resolvedAtMs` is set only by the operator repair, so an audit
|
|
1053
|
+
* read can tell a settlement that completed on its own from one a human had
|
|
1054
|
+
* to finish.
|
|
1055
|
+
*/
|
|
1056
|
+
completeSettlement(settlementId: string, resolvedAtMs?: number): Promise<void>;
|
|
1057
|
+
get(channelId: string): Promise<Channel | undefined>;
|
|
1058
|
+
/** List all channel snapshots in stable channel-id order. */
|
|
1059
|
+
list(): Promise<Channel[]>;
|
|
1060
|
+
/** Atomic backend-level read/modify/write for one channel. */
|
|
1061
|
+
updateChannel(channelId: string, update: (current: Channel | undefined) => Channel | undefined): Promise<ChannelUpdateResult>;
|
|
1062
|
+
/**
|
|
1063
|
+
* Run channel state and its ledger effect under the channel lock.
|
|
1064
|
+
*
|
|
1065
|
+
* Upstream's voucher-only settle updates `chargedCumulativeAmount` through
|
|
1066
|
+
* `updateChannel`. Keeping that update and `commitMarkedFunding` in this one
|
|
1067
|
+
* transaction means a ledger refusal rolls the voucher state back. External
|
|
1068
|
+
* deposit/refund responses are recorded separately before this transaction
|
|
1069
|
+
* commits, leaving a durable reconciliation row if COMMIT fails.
|
|
1070
|
+
*/
|
|
1071
|
+
/**
|
|
1072
|
+
* Sanction one cooperative-refund base for the updates inside `operation`.
|
|
1073
|
+
*
|
|
1074
|
+
* The store cannot derive the base — it comes from the credit ledger — so
|
|
1075
|
+
* this is how the settlement layer says "this exact decrease is the one the
|
|
1076
|
+
* payer's verified refund voucher authorises". Outside this scope a decrease
|
|
1077
|
+
* of `chargedCumulativeAmount` is refused flat, which is what keeps the
|
|
1078
|
+
* arbitrary-decrease guard meaningful.
|
|
1079
|
+
*/
|
|
1080
|
+
withSanctionedRefundBase<T>(base: string, operation: () => Promise<T>): Promise<T>;
|
|
1081
|
+
withChannelTransaction<T>(channelId: string, operation: (tx: PoolClient) => Promise<T>, hostingFunding?: HostedFundingReservation): Promise<T>;
|
|
1082
|
+
/** Durable effect bookkeeping reached while a caller already holds a host-pool client. */
|
|
1083
|
+
withSettlementTransaction<T>(operation: (tx: PoolClient) => Promise<T>): Promise<T>;
|
|
1084
|
+
/**
|
|
1085
|
+
* Run one fleet-wide settlement operation, optionally waiting for the current owner.
|
|
1086
|
+
*
|
|
1087
|
+
* The lock is session-scoped, so its client stays checked out for the whole
|
|
1088
|
+
* operation — and `operation()` (upstream's claim/settle) re-enters the pool
|
|
1089
|
+
* through {@link updateChannel}. Two bounds keep that from wedging the pool
|
|
1090
|
+
* local callers are serialized in-process, so this holds at most
|
|
1091
|
+
* one client per process no matter how many requests arrive, and a waiting
|
|
1092
|
+
* acquire carries a `lock_timeout` so a wedged fleet-mate can't pin that
|
|
1093
|
+
* client — and the caller's request — indefinitely. A timeout reports the
|
|
1094
|
+
* lock as not acquired, which is what every caller already handles.
|
|
1095
|
+
*
|
|
1096
|
+
* The queue depth is reserved synchronously, before the promise chain can
|
|
1097
|
+
* yield. That makes the non-waiting form a true local try-lock even while the
|
|
1098
|
+
* first caller is still acquiring its connection: an overlapping request
|
|
1099
|
+
* returns `false` instead of queuing behind the facilitator operation.
|
|
1100
|
+
*/
|
|
1101
|
+
withFleetSettlementLock(operation: () => Promise<unknown>, wait?: boolean): Promise<boolean>;
|
|
1102
|
+
/**
|
|
1103
|
+
* Hold the fleet-wide self-relay submission lock for one chain submission.
|
|
1104
|
+
*
|
|
1105
|
+
* The session lock keeps one per-DVM gas EOA to one in-flight submission
|
|
1106
|
+
* across the whole fleet — two machines submitting at once collide on the
|
|
1107
|
+
* nonce. It is reached from `resource.settlePayment` inside
|
|
1108
|
+
* {@link withChannelTransaction} and from upstream's claim/settle inside
|
|
1109
|
+
* {@link withFleetSettlementLock}, so on both paths a host-pool client is
|
|
1110
|
+
* already checked out: it therefore acquires from the terminal settlement
|
|
1111
|
+
* pool, which is the rule in this class's doc, and never from the host pool
|
|
1112
|
+
* the caller is holding.
|
|
1113
|
+
*
|
|
1114
|
+
* The wait is bounded by `lock_timeout` and a timeout **refuses** rather than
|
|
1115
|
+
* proceeding unlocked, which is where this differs from
|
|
1116
|
+
* {@link withFleetSettlementLock}: a fleet lock nobody got means the claim is
|
|
1117
|
+
* someone else's turn, while a submission lock nobody got means the caller
|
|
1118
|
+
* must not submit at all. The refusal is typed and retryable, and names the
|
|
1119
|
+
* holder when Postgres will tell us who it is.
|
|
1120
|
+
*
|
|
1121
|
+
* The relay's own in-process tail serialises callers before they reach here,
|
|
1122
|
+
* so one process pins at most one settlement-pool client no matter how many
|
|
1123
|
+
* deposits arrive.
|
|
1124
|
+
*/
|
|
1125
|
+
withRelaySubmissionLock<T>(key: string, operation: () => Promise<T>): Promise<T>;
|
|
1126
|
+
/** Release the dedicated settlement pool; the host-owned pool is left alone. */
|
|
1127
|
+
close(): Promise<void>;
|
|
1128
|
+
private settlementWriter;
|
|
1129
|
+
private settlementPoolRef;
|
|
1130
|
+
private runUnderFleetLock;
|
|
1131
|
+
private updateInTransaction;
|
|
1191
1132
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1133
|
+
|
|
1134
|
+
type Query = Pick<Pool | PoolClient, "query">;
|
|
1135
|
+
type Rail = "x402" | "tempo";
|
|
1136
|
+
type Operation = "claim" | "refund" | "settle" | "close";
|
|
1137
|
+
type State = "reserved" | "observed" | "report_queued" | "delivered" | "no_report";
|
|
1138
|
+
interface ObligationRow {
|
|
1139
|
+
id: string;
|
|
1140
|
+
source_key: string;
|
|
1141
|
+
dvm_id: string;
|
|
1142
|
+
scope: string;
|
|
1143
|
+
instance_id: string;
|
|
1144
|
+
boot_epoch: string;
|
|
1145
|
+
rail: Rail;
|
|
1146
|
+
operation: Operation;
|
|
1147
|
+
network: string;
|
|
1148
|
+
contract: string;
|
|
1149
|
+
channel_id: string;
|
|
1150
|
+
original: Record<string, unknown>;
|
|
1151
|
+
state: State;
|
|
1152
|
+
tx_hash: string | null;
|
|
1153
|
+
block_number: string | null;
|
|
1154
|
+
block_hash: string | null;
|
|
1155
|
+
delta_native: string | null;
|
|
1156
|
+
payout_id: string | null;
|
|
1157
|
+
report_id: string | null;
|
|
1158
|
+
report_payload: Record<string, unknown> | null;
|
|
1159
|
+
}
|
|
1160
|
+
interface HostedEffectReservation {
|
|
1161
|
+
id: string;
|
|
1162
|
+
replayed: boolean;
|
|
1200
1163
|
}
|
|
1201
|
-
interface
|
|
1164
|
+
interface HostedObservedEffect {
|
|
1165
|
+
id: string;
|
|
1202
1166
|
txHash: string;
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
amount: bigint;
|
|
1207
|
-
/** Newly settled to the payee by this transaction, atomic units. */
|
|
1208
|
-
delta: bigint;
|
|
1167
|
+
blockNumber: string;
|
|
1168
|
+
blockHash: string;
|
|
1169
|
+
deltaNative: string;
|
|
1209
1170
|
}
|
|
1210
|
-
interface
|
|
1171
|
+
interface HostedRecoverableEffect {
|
|
1172
|
+
id: string;
|
|
1173
|
+
rail: Rail;
|
|
1174
|
+
operation: Operation;
|
|
1211
1175
|
channelId: string;
|
|
1212
1176
|
txHash: string;
|
|
1213
|
-
|
|
1214
|
-
|
|
1177
|
+
batchId?: string;
|
|
1178
|
+
reportId: string | null;
|
|
1179
|
+
blockHash: string | null;
|
|
1180
|
+
deltaNative: string | null;
|
|
1215
1181
|
}
|
|
1216
|
-
interface
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
interface X402WedgedRefund {
|
|
1222
|
-
settlementId: string;
|
|
1223
|
-
/** Epoch ms the settlement was first prepared. */
|
|
1224
|
-
createdAt: number;
|
|
1225
|
-
native?: number;
|
|
1182
|
+
interface X402Config {
|
|
1183
|
+
network: string;
|
|
1184
|
+
rpcUrl?: string;
|
|
1185
|
+
receiver: string;
|
|
1186
|
+
token: string;
|
|
1226
1187
|
}
|
|
1227
|
-
interface
|
|
1228
|
-
/** `${network}|${payTo}|${token}` — the scope the settle-pending marker is keyed on. */
|
|
1229
|
-
scope: string;
|
|
1230
|
-
payTo: string;
|
|
1188
|
+
interface TempoConfig {
|
|
1231
1189
|
network: string;
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
settleIntervalMs: number;
|
|
1237
|
-
listWedgedRefunds?: () => Promise<X402WedgedRefund[]>;
|
|
1190
|
+
rpcUrl?: string;
|
|
1191
|
+
escrow: string;
|
|
1192
|
+
payee: string;
|
|
1193
|
+
token: string;
|
|
1238
1194
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1195
|
+
/** Private, server-installed record of economic effects whose report may outlive the rail call. */
|
|
1196
|
+
declare class HostedSettlementReportObligations {
|
|
1197
|
+
private readonly pool;
|
|
1198
|
+
private readonly dvmId;
|
|
1199
|
+
private readonly scope;
|
|
1200
|
+
private x402?;
|
|
1201
|
+
private tempo?;
|
|
1202
|
+
private x402SettlementStore?;
|
|
1203
|
+
constructor(pool: Pool, dvmId: string, scope: string);
|
|
1204
|
+
configureX402(config: X402Config): void;
|
|
1205
|
+
attachX402SettlementStore(store: PostgresX402ChannelStorage): void;
|
|
1206
|
+
private x402Transaction;
|
|
1207
|
+
configureTempo(config: TempoConfig): void;
|
|
1208
|
+
init(): Promise<void>;
|
|
1209
|
+
reserveX402(channelId: string, operation: "claim" | "refund", expected: {
|
|
1210
|
+
charged?: string;
|
|
1211
|
+
signature?: string;
|
|
1212
|
+
refundNonce?: string;
|
|
1213
|
+
refundAmount?: string;
|
|
1214
|
+
voucherSignature?: string;
|
|
1215
|
+
signedMax?: string;
|
|
1216
|
+
authorizationPayload: Record<string, unknown>;
|
|
1217
|
+
authorizationRequirements: {
|
|
1218
|
+
scheme: string;
|
|
1219
|
+
network: string;
|
|
1220
|
+
asset: string;
|
|
1221
|
+
payTo: string;
|
|
1222
|
+
amount: string;
|
|
1223
|
+
};
|
|
1224
|
+
}): Promise<HostedEffectReservation | null>;
|
|
1225
|
+
reserveTempo(channelId: string, operation: "settle" | "close", drainId?: string): Promise<HostedEffectReservation>;
|
|
1226
|
+
findPendingTempo(channelId: string, operation: "settle" | "close"): Promise<ObligationRow | undefined>;
|
|
1227
|
+
submittedIds(rail: Rail, operation: Operation, txHash: string, batchId?: string): Promise<string[]>;
|
|
1228
|
+
refundClaimDelta(txHash: string, channelId: string): Promise<bigint>;
|
|
1229
|
+
assertReservedTempoClose(id: string, channelId: string): Promise<void>;
|
|
1230
|
+
originalTempoCloseSettled(id: string, channelId: string, txHash: string): Promise<bigint>;
|
|
1231
|
+
settlementId(batchId: string, txHash: string): Promise<string>;
|
|
1232
|
+
reserveOpenX402Settlement(): Promise<HostedEffectReservation>;
|
|
1233
|
+
private ensureOpenX402Batch;
|
|
1234
|
+
recordSubmitted(id: string, txHash: string): Promise<void>;
|
|
1235
|
+
recoveryCandidates(): Promise<HostedRecoverableEffect[]>;
|
|
1236
|
+
recoverOriginalX402Refunds(): Promise<Array<{
|
|
1237
|
+
id: string;
|
|
1238
|
+
error: unknown;
|
|
1239
|
+
}>>;
|
|
1240
|
+
reconcileX402Batches(): Promise<Array<{
|
|
1241
|
+
id: string;
|
|
1242
|
+
error: unknown;
|
|
1243
|
+
}>>;
|
|
1244
|
+
queueReport(ids: string[], payoutId: string, reportId: string, payload: Record<string, unknown>, tx: Query): Promise<void>;
|
|
1245
|
+
hasOriginalReport(ids: string[], payoutId: string, payload: Record<string, unknown>, tx: Query): Promise<boolean>;
|
|
1246
|
+
assertReportReady(reportId: string, payload: Record<string, unknown>, query?: Query): Promise<void>;
|
|
1247
|
+
acknowledgeDelivered(reportId: string, tx: Query, requireLinked?: boolean): Promise<void>;
|
|
1248
|
+
observeCanonical(id: string): Promise<HostedObservedEffect>;
|
|
1249
|
+
private lockRow;
|
|
1250
|
+
unresolvedCount(query?: Query): Promise<number>;
|
|
1251
|
+
hasCompleteHistory(query?: Query): Promise<boolean>;
|
|
1252
|
+
private lockIdentity;
|
|
1253
|
+
private insert;
|
|
1247
1254
|
}
|
|
1255
|
+
|
|
1256
|
+
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
1257
|
+
[key: string]: JsonValue;
|
|
1258
|
+
};
|
|
1248
1259
|
/**
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1260
|
+
* Object keys sort recursively in UTF-16 code-unit order; arrays preserve order.
|
|
1261
|
+
* Undefined object properties are omitted. Unsupported scalars, including
|
|
1262
|
+
* non-finite numbers, throw TypeError. Callers strip unsigned fields themselves.
|
|
1252
1263
|
*/
|
|
1253
|
-
|
|
1254
|
-
attach(ctx: X402PayoutContext): void;
|
|
1255
|
-
trackManager<M extends X402TrackedManager>(manager: M): M;
|
|
1256
|
-
/** A settle that landed outside the tracked manager — the manual claim-and-settle's own retry loop. */
|
|
1257
|
-
recordSettle(transaction: string): Promise<void>;
|
|
1258
|
-
/** A settle attempt that failed outside the tracked manager. One call per attempt a builder would count as one. */
|
|
1259
|
-
recordSettleFailure(error: unknown): Promise<void>;
|
|
1260
|
-
}
|
|
1261
|
-
interface TempoPayoutReader {
|
|
1262
|
-
recipient?: string;
|
|
1263
|
-
/** CAIP-2 network for the refs, when known. */
|
|
1264
|
-
network?: string;
|
|
1265
|
-
listActive?: (limit: number, cursor?: {
|
|
1266
|
-
updatedAt: number;
|
|
1267
|
-
key: string;
|
|
1268
|
-
}) => Promise<{
|
|
1269
|
-
key: string;
|
|
1270
|
-
state: Record<string, unknown>;
|
|
1271
|
-
updatedAt: number;
|
|
1272
|
-
}[]>;
|
|
1273
|
-
listChannelDrains?: (args: {
|
|
1274
|
-
limit: number;
|
|
1275
|
-
after?: ChannelDrainCursor;
|
|
1276
|
-
createdBeforeMs: number;
|
|
1277
|
-
rail: "tempo";
|
|
1278
|
-
}) => Promise<CreditDrainRecord[]>;
|
|
1279
|
-
}
|
|
1264
|
+
declare function canonicalize(value: JsonValue): string;
|
|
1280
1265
|
/**
|
|
1281
|
-
*
|
|
1282
|
-
* eventual settlement can run on different hosts under the fleet lock.
|
|
1266
|
+
* UTF-8 encoding of the canonical JSON form.
|
|
1283
1267
|
*/
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
openedAt: number;
|
|
1291
|
-
attempts: number;
|
|
1292
|
-
firstFailedAt: number | null;
|
|
1293
|
-
lastError: string | null;
|
|
1294
|
-
nextRetryAt: number | null;
|
|
1295
|
-
}
|
|
1296
|
-
interface X402BatchStore {
|
|
1297
|
-
get(scope: string): Promise<X402OpenBatch | undefined>;
|
|
1298
|
-
put(batch: X402OpenBatch): Promise<void>;
|
|
1299
|
-
delete(scope: string): Promise<void>;
|
|
1268
|
+
declare function canonicaliseForSigning(value: JsonValue): Uint8Array;
|
|
1269
|
+
|
|
1270
|
+
type ReceiptOutcome = "completed" | "failed" | "cancelled";
|
|
1271
|
+
interface ReceiptPayment {
|
|
1272
|
+
/** Total credited millisats. `0` on free jobs — they still get receipts. */
|
|
1273
|
+
msats: number;
|
|
1300
1274
|
/**
|
|
1301
|
-
*
|
|
1302
|
-
*
|
|
1275
|
+
* Rail of the most recent successful credit; `null` when nothing was paid.
|
|
1276
|
+
* `lightning` appears on a draw against a credit the caller funded by
|
|
1277
|
+
* invoice — it is a funding rail, never an attached-proof one.
|
|
1303
1278
|
*/
|
|
1304
|
-
|
|
1279
|
+
rail: "cashu" | "x402" | "tempo" | "lightning" | null;
|
|
1280
|
+
/** Rail-native upfront amount (sats / USDC microunits), `null` when absent. */
|
|
1281
|
+
native_amount: number | null;
|
|
1282
|
+
native_asset: string | null;
|
|
1283
|
+
/** Settlement reference: EVM tx hash (x402), mppx challenge id, or the Cashu request id. */
|
|
1284
|
+
tx_hash: string | null;
|
|
1285
|
+
/** Cashu mint the proofs were drawn on, `null` off the Cashu rail. */
|
|
1286
|
+
mint: string | null;
|
|
1305
1287
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
/**
|
|
1322
|
-
|
|
1323
|
-
|
|
1288
|
+
/**
|
|
1289
|
+
* Credit-draw countersignature block. Present on every job whose payment funded
|
|
1290
|
+
* or drew the credit
|
|
1291
|
+
* ledger — including implicit N=1 per-job payments, which fund and draw a
|
|
1292
|
+
* single-use credit in the same request. A caller holding a DVM-signed
|
|
1293
|
+
* balance trajectory plus its own caller-signed draw intents can prove
|
|
1294
|
+
* misbehaviour from artifacts both sides already possess; that only works
|
|
1295
|
+
* because the countersignature rides the receipt.
|
|
1296
|
+
*
|
|
1297
|
+
* Absence is signature-compatible: `canonicalize` omits absent keys, so a
|
|
1298
|
+
* ledger-less (or free) job's receipt signs identically to a legacy
|
|
1299
|
+
* receipt.
|
|
1300
|
+
*/
|
|
1301
|
+
interface ReceiptCredit {
|
|
1302
|
+
credit_id: string;
|
|
1303
|
+
/** This draw's identifier — matches the caller-signed draw intent. */
|
|
1304
|
+
draw_id: string;
|
|
1305
|
+
/** Amount drawn, in the credit's denomination. */
|
|
1306
|
+
amount: number;
|
|
1307
|
+
balance_after: number;
|
|
1308
|
+
/** Per-credit monotonic draw counter. */
|
|
1309
|
+
ledger_seq: number;
|
|
1324
1310
|
}
|
|
1325
1311
|
/**
|
|
1326
|
-
*
|
|
1327
|
-
*
|
|
1328
|
-
*
|
|
1312
|
+
* A DVM-signed proof of what a job did and what it cost.
|
|
1313
|
+
*
|
|
1314
|
+
* Signed once at the terminal transition by the per-DVM receipt key, whose
|
|
1315
|
+
* authority chains to the builder identity key through the deploy-time
|
|
1316
|
+
* attestation (`/v1/info#builder`, `attestation.receipt_pubkey`). Served
|
|
1317
|
+
* identically on the synchronous `POST /v1/job` 200, on `GET /v1/job/:id`,
|
|
1318
|
+
* and on every later re-read — the bytes are persisted, not recomputed.
|
|
1329
1319
|
*/
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
/** Deliver what is queued now; a transport without the seam waits for its retry loop. */
|
|
1357
|
-
flush(): Promise<void>;
|
|
1358
|
-
meltHook(): TransactionalPayoutHook<CashuMeltCompleted>;
|
|
1359
|
-
/**
|
|
1360
|
-
* Report melted face value per mint, not the Lightning amount received after fees. Hash the
|
|
1361
|
-
* payment preimage; do not publish it. With tx, propagate enqueue failures and defer
|
|
1362
|
-
* delivery until commit. Without tx, enqueue independently and start delivery.
|
|
1363
|
-
*/
|
|
1364
|
-
cashuMelted(event: CashuMeltCompleted, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1365
|
-
attachTempo(reader: TempoPayoutReader): void;
|
|
1366
|
-
/**
|
|
1367
|
-
* A Tempo channel settled or closed on chain. `delta` is what this
|
|
1368
|
-
* transaction newly paid the recipient; a transaction that paid nothing new
|
|
1369
|
-
* (a close of a fully settled channel) is no payout.
|
|
1370
|
-
*/
|
|
1371
|
-
tempoSettled(event: TempoSessionSettled): Promise<void>;
|
|
1372
|
-
tempoRepairHook(): TransactionalPayoutHook<TempoCloseReconciled>;
|
|
1373
|
-
/**
|
|
1374
|
-
* Report the receipt’s payee total under the live close’s transaction ID, flagged repaired.
|
|
1375
|
-
* The platform upgrades a live row if one exists. Join tx and propagate enqueue failures;
|
|
1376
|
-
* this does not report the caller refund.
|
|
1377
|
-
*/
|
|
1378
|
-
tempoCloseReconciled(event: TempoCloseReconciled, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1379
|
-
x402Observer(): X402PayoutObserver;
|
|
1380
|
-
/**
|
|
1381
|
-
* Read claim deltas from storage because upstream returns voucher counts. Under the fleet
|
|
1382
|
-
* lock, before/after reads are consistent. A deleted channel contributes its pre-claim
|
|
1383
|
-
* unclaimed value, including refund claim-then-delete.
|
|
1384
|
-
*/
|
|
1385
|
-
private trackX402Manager;
|
|
1386
|
-
private recordX402Claim;
|
|
1387
|
-
private recordX402Settle;
|
|
1388
|
-
private recordX402SettleFailure;
|
|
1389
|
-
/** Every rail this reporter can read, each on its own failure boundary. */
|
|
1390
|
-
snapshot(): Promise<void>;
|
|
1320
|
+
interface JobReceipt {
|
|
1321
|
+
v: 1;
|
|
1322
|
+
job_id: string;
|
|
1323
|
+
/** Immutable DVM identity bound into the builder attestation. */
|
|
1324
|
+
dvm_id: string;
|
|
1325
|
+
/** DVM slug, as bound in the builder attestation. */
|
|
1326
|
+
dvm: string;
|
|
1327
|
+
capability: string;
|
|
1328
|
+
outcome: ReceiptOutcome;
|
|
1329
|
+
/** Signed Unix millisecond lifecycle times; absent on legacy receipts. */
|
|
1330
|
+
timing?: {
|
|
1331
|
+
accepted_at_ms: number;
|
|
1332
|
+
terminal_at_ms: number;
|
|
1333
|
+
caller_wait_ms: number;
|
|
1334
|
+
};
|
|
1335
|
+
/** SDK-derived attribution on failure/cancellation; absent on completion and legacy receipts. */
|
|
1336
|
+
ended_by?: "caller" | "provider";
|
|
1337
|
+
/** Terminal reason on `failed`/`cancelled` (e.g. `worker_died_mid_job`); `null` on success. */
|
|
1338
|
+
reason: string | null;
|
|
1339
|
+
/** Per-DVM monotonic terminal counter. Gaps expose receipt suppression. */
|
|
1340
|
+
seq: number;
|
|
1341
|
+
/** Unix seconds at which the receipt was signed. */
|
|
1342
|
+
issued_at: number;
|
|
1343
|
+
/** Caller's x-only pubkey when signed-request auth was used; `null` otherwise. */
|
|
1344
|
+
requester_pubkey: string | null;
|
|
1345
|
+
paid: ReceiptPayment;
|
|
1391
1346
|
/**
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1394
|
-
*
|
|
1347
|
+
* {@link computeResultHash} over the delivered result; `null` when nothing
|
|
1348
|
+
* was delivered. The summary is part of the hash only on `completed` — see
|
|
1349
|
+
* {@link computeResultHash}.
|
|
1395
1350
|
*/
|
|
1396
|
-
|
|
1351
|
+
result_hash: string | null;
|
|
1352
|
+
/** x-only secp256k1 pubkey that signed this receipt (64 hex chars). */
|
|
1353
|
+
receipt_pubkey: string;
|
|
1354
|
+
/** BIP-340 Schnorr signature over the canonical receipt minus `signature` (128 hex chars). */
|
|
1355
|
+
signature: string;
|
|
1397
1356
|
/**
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1400
|
-
*
|
|
1357
|
+
* Credit-draw countersignature — present when the job's payment
|
|
1358
|
+
* funded/drew the credit ledger. See {@link ReceiptCredit}. `balance_after`
|
|
1359
|
+
* reflects the terminal resolution: the recorded draw trajectory on a
|
|
1360
|
+
* settled (completed) job, the restored available balance on a released
|
|
1361
|
+
* (failed/cancelled) one.
|
|
1401
1362
|
*/
|
|
1402
|
-
|
|
1363
|
+
credit?: ReceiptCredit;
|
|
1364
|
+
}
|
|
1365
|
+
type UnsignedJobReceipt = Omit<JobReceipt, "signature">;
|
|
1366
|
+
type DrainReceiptEvent = "requested" | "parked" | "picked_up" | "sent" | "released";
|
|
1367
|
+
/**
|
|
1368
|
+
* A DVM-signed proof of one reclaim event. One receipt per
|
|
1369
|
+
* lifecycle transition — `requested` (the balance debited into the drain
|
|
1370
|
+
* liability), `parked` (Cashu notes locked to the caller's refund key,
|
|
1371
|
+
* parked for pickup), `picked_up` (the caller collected them), `sent` (a
|
|
1372
|
+
* Lightning/x402/mpp send-back settled).
|
|
1373
|
+
*
|
|
1374
|
+
* The evidence pairing: the caller holds the drain *request* it signed (the
|
|
1375
|
+
* `/v1/credit` envelope), the DVM countersigns each state the drain reaches.
|
|
1376
|
+
* A `requested` receipt with no later `parked`/`sent` is a signed IOU whose
|
|
1377
|
+
* refusal is provable from artifacts both sides already hold — reclaim is
|
|
1378
|
+
* enforced by reputation with cryptographic evidence, not new crypto.
|
|
1379
|
+
*
|
|
1380
|
+
* Signed by the same per-DVM receipt key as {@link JobReceipt}, over the
|
|
1381
|
+
* same canonicalisation, so one attestation chain covers both.
|
|
1382
|
+
*/
|
|
1383
|
+
interface DrainReceipt {
|
|
1384
|
+
v: 1;
|
|
1385
|
+
/** Discriminator separating this from a `JobReceipt` in mixed stores. */
|
|
1386
|
+
kind: "credit_drain";
|
|
1387
|
+
credit_id: string;
|
|
1388
|
+
/** This drain's identifier — matches the caller-signed drain request. */
|
|
1389
|
+
drain_id: string;
|
|
1390
|
+
event: DrainReceiptEvent;
|
|
1391
|
+
/** Payout rail: `cashu` | `x402` | `tempo`. */
|
|
1392
|
+
method: string;
|
|
1393
|
+
/** Amount reclaimed, micro-units of the credit's currency. */
|
|
1394
|
+
amount_micro: number;
|
|
1403
1395
|
/**
|
|
1404
|
-
*
|
|
1405
|
-
*
|
|
1406
|
-
* chain and ledger, aged past the same floor the repair queue
|
|
1407
|
-
* uses so an in-flight close is not reported as stuck.
|
|
1396
|
+
* Credit balance left after the drain debit, in micro-units. It includes
|
|
1397
|
+
* pending job holds at request time and is fixed across this drain's event receipts.
|
|
1408
1398
|
*/
|
|
1409
|
-
|
|
1399
|
+
balance_after_micro: number;
|
|
1400
|
+
/** Per-credit monotonic sequence the drain consumed, shared with draws. */
|
|
1401
|
+
ledger_seq: number;
|
|
1402
|
+
/** Unix seconds at which this receipt was signed. */
|
|
1403
|
+
issued_at: number;
|
|
1404
|
+
/** The credit owner's x-only pubkey — who the money is owed to. */
|
|
1405
|
+
caller_pubkey: string;
|
|
1406
|
+
/** Immutable DVM identity bound into the builder attestation. */
|
|
1407
|
+
dvm_id: string;
|
|
1408
|
+
/** DVM slug, as bound in the builder attestation. */
|
|
1409
|
+
dvm: string;
|
|
1410
|
+
/** x-only secp256k1 pubkey that signed this receipt (64 hex chars). */
|
|
1411
|
+
receipt_pubkey: string;
|
|
1412
|
+
/** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
|
|
1413
|
+
signature: string;
|
|
1414
|
+
}
|
|
1415
|
+
type UnsignedDrainReceipt = Omit<DrainReceipt, "signature">;
|
|
1416
|
+
/**
|
|
1417
|
+
* A DVM-signed proof that one payment funded a prepaid credit.
|
|
1418
|
+
* The balance and sequence are the values fixed when the funding committed;
|
|
1419
|
+
* retries return this original artifact even when the credit moved later.
|
|
1420
|
+
*/
|
|
1421
|
+
interface FundingReceipt {
|
|
1422
|
+
v: 1;
|
|
1423
|
+
/** Discriminator separating funding evidence from job and drain receipts. */
|
|
1424
|
+
kind: "credit_funding";
|
|
1425
|
+
credit_id: string;
|
|
1426
|
+
/** Caller fund id, or the attached rail payment id for fund-and-draw. */
|
|
1427
|
+
fund_id: string;
|
|
1428
|
+
/** Amount credited, in micro-units of the credit currency. */
|
|
1429
|
+
funded_micro: number;
|
|
1430
|
+
/** Available balance immediately after this funding operation committed. */
|
|
1431
|
+
balance_after_micro: number;
|
|
1432
|
+
/** Per-credit high-water sequence at that same point. */
|
|
1433
|
+
ledger_seq: number;
|
|
1434
|
+
/** Unix seconds at which the funding artifact was first issued. */
|
|
1435
|
+
issued_at: number;
|
|
1436
|
+
/** The credit owner's x-only request-signing pubkey. */
|
|
1437
|
+
caller_pubkey: string;
|
|
1438
|
+
/** Immutable DVM identity bound into the builder attestation. */
|
|
1439
|
+
dvm_id: string;
|
|
1440
|
+
/** DVM slug, as bound in the builder attestation. */
|
|
1441
|
+
dvm: string;
|
|
1442
|
+
receipt_pubkey: string;
|
|
1443
|
+
/** BIP-340 Schnorr signature over the canonical receipt minus `signature`. */
|
|
1444
|
+
signature: string;
|
|
1445
|
+
}
|
|
1446
|
+
type UnsignedFundingReceipt = Omit<FundingReceipt, "signature">;
|
|
1447
|
+
/**
|
|
1448
|
+
* Hash the delivered result so a receipt binds to *what* was returned, not
|
|
1449
|
+
* just that something was.
|
|
1450
|
+
*
|
|
1451
|
+
* `sha256(canonicalize({ summary, artifact_hashes }))`, where each artifact
|
|
1452
|
+
* hash is `sha256(canonicalize(artifactContent))` in emission order (`[]`
|
|
1453
|
+
* when the job emitted none). Hashing the artifact *content object* rather
|
|
1454
|
+
* than trusting `content.sha256` — which is optional and which `ctx.artifact`
|
|
1455
|
+
* never fills in — keeps the hash recomputable by any consumer from exactly
|
|
1456
|
+
* the messages it received.
|
|
1457
|
+
*
|
|
1458
|
+
* **Pass `summary` only for a `completed` job.** On `failed`/`cancelled` the
|
|
1459
|
+
* SDK's durable terminal writes the terminal *reason* into the job's summary
|
|
1460
|
+
* column, so a verifier recomputing a non-completed receipt must pass
|
|
1461
|
+
* `undefined` here and read the reason off `receipt.reason` instead. The
|
|
1462
|
+
* issuer does the same (`resultHashFor` in `sdk/server/receipt-issuer.ts`).
|
|
1463
|
+
*/
|
|
1464
|
+
declare function computeResultHash(summary: string | undefined, artifactContents: JsonValue[]): string;
|
|
1465
|
+
declare function signReceipt(unsigned: UnsignedJobReceipt, secretHex: string): JobReceipt;
|
|
1466
|
+
/**
|
|
1467
|
+
* Verify a receipt's self-consistency: the BIP-340 signature over the
|
|
1468
|
+
* canonical payload (minus `signature`) under the receipt's own
|
|
1469
|
+
* `receipt_pubkey`. Returns `false` rather than throwing on malformed input.
|
|
1470
|
+
*
|
|
1471
|
+
* This is only the inner link. A consumer that wants provenance must also
|
|
1472
|
+
* check `receipt_pubkey` against `/v1/info#builder`'s attested
|
|
1473
|
+
* `receipt_pubkey` and verify that attestation under the builder pubkey
|
|
1474
|
+
* (`verifyAttestation` in `builder-identity.ts`).
|
|
1475
|
+
*/
|
|
1476
|
+
declare function verifyReceipt(receipt: JobReceipt): boolean;
|
|
1477
|
+
/**
|
|
1478
|
+
* Structural guard on a receipt that arrived from outside this process — off a
|
|
1479
|
+
* `/v1/job` response, or back out of the hand-editable `~/.dvm/receipts.jsonl`.
|
|
1480
|
+
*
|
|
1481
|
+
* Checks only that the load-bearing fields are the right *kind* of thing; it
|
|
1482
|
+
* deliberately does not judge the receipt. A well-formed receipt whose
|
|
1483
|
+
* signature doesn't hold is `invalid` and the caller must see it said so. A
|
|
1484
|
+
* body missing one of these fields is a different animal: consumers dereference
|
|
1485
|
+
* them unconditionally (`receiptDisplay`'s `paid.msats`, the attestation link's
|
|
1486
|
+
* `receipt_pubkey`), so an unguarded one throws `TypeError` and surfaces as an
|
|
1487
|
+
* `internal` exit — and it could never have verified anyway, since the
|
|
1488
|
+
* signature covers the whole canonical body. Refusing it costs no proof.
|
|
1489
|
+
*
|
|
1490
|
+
* One guard for both directions on purpose: what can't come in over the wire
|
|
1491
|
+
* must not be able to come back out of the file.
|
|
1492
|
+
*
|
|
1493
|
+
* Release coupling: adding or requiring a field here changes both the SDK's
|
|
1494
|
+
* emitted wire shape and the caller CLI's accepted shape. Name the first-party
|
|
1495
|
+
* fleet redeploy as a prerequisite in the release note before publishing the
|
|
1496
|
+
* stricter caller; see `public compatibility guide`.
|
|
1497
|
+
*/
|
|
1498
|
+
declare function isSignedJobReceipt(value: unknown): value is JobReceipt;
|
|
1499
|
+
/** Sign an unsigned drain receipt — same canonicalisation as {@link signReceipt}. */
|
|
1500
|
+
declare function signDrainReceipt(unsigned: UnsignedDrainReceipt, secretHex: string): DrainReceipt;
|
|
1501
|
+
/** Sign an unsigned funding receipt using the common receipt canonicalisation. */
|
|
1502
|
+
declare function signFundingReceipt(unsigned: UnsignedFundingReceipt, secretHex: string): FundingReceipt;
|
|
1503
|
+
declare function isFundingReceipt(value: unknown): value is FundingReceipt;
|
|
1504
|
+
declare function verifyFundingReceipt(receipt: FundingReceipt): boolean;
|
|
1505
|
+
/**
|
|
1506
|
+
* Structural guard on a drain receipt from outside this process — off a
|
|
1507
|
+
* `/v1/credit` drain response, or back out of `~/.dvm/credits.json`.
|
|
1508
|
+
*
|
|
1509
|
+
* Same contract and same reasoning as {@link isSignedJobReceipt}: both homes
|
|
1510
|
+
* type these `unknown[]` because neither the wire nor a hand-editable file is
|
|
1511
|
+
* evidence of shape, and the chain walk dereferences `receipt_pubkey`
|
|
1512
|
+
* unconditionally. A body missing one of these fields could never have carried
|
|
1513
|
+
* a valid signature anyway, so refusing it costs no proof.
|
|
1514
|
+
* Required-field changes follow the fleet-before-caller release note linked
|
|
1515
|
+
* from {@link isSignedJobReceipt}.
|
|
1516
|
+
*/
|
|
1517
|
+
declare function isDrainReceipt(value: unknown): value is DrainReceipt;
|
|
1518
|
+
/**
|
|
1519
|
+
* Verify a drain receipt's self-consistency, mirroring {@link verifyReceipt}
|
|
1520
|
+
* the same provenance caveat applies: check `receipt_pubkey` against the
|
|
1521
|
+
* DVM's attested `receipt_pubkey` for the outer link.
|
|
1522
|
+
*/
|
|
1523
|
+
declare function verifyDrainReceipt(receipt: DrainReceipt): boolean;
|
|
1524
|
+
|
|
1525
|
+
/**
|
|
1526
|
+
* Cashu and Lightning credits owe unused sats back at each deposit's funding
|
|
1527
|
+
* rate. Keep the sats/micro basis per funding lot: a pooled ratio loses the
|
|
1528
|
+
* basis of top-ups made at different rates. Settled draws and reclaims share
|
|
1529
|
+
* these lots so their total equals the sats funded.
|
|
1530
|
+
*/
|
|
1531
|
+
/**
|
|
1532
|
+
* The rails whose credits carry a Bitcoin deposit this ledger owes back in
|
|
1533
|
+
* kind — Cashu and Lightning funding, both of which leave sats in the
|
|
1534
|
+
* provider's own custody.
|
|
1535
|
+
*
|
|
1536
|
+
* Declared as its own list rather than derived by subtracting the stablecoin
|
|
1537
|
+
* rails: a channel-backed credit is in-kind *structurally* (the escrow retains
|
|
1538
|
+
* the unused value), so it needs no lots at all, and a rail added to
|
|
1539
|
+
* {@link FundingMethod} must be a decision here rather than a default.
|
|
1540
|
+
*/
|
|
1541
|
+
declare const NON_CHANNEL_BITCOIN_RAILS: readonly ["cashu", "lightning"];
|
|
1542
|
+
type NonChannelBitcoinRail = (typeof NON_CHANNEL_BITCOIN_RAILS)[number];
|
|
1543
|
+
declare function isNonChannelBitcoinRail(rail: string | null | undefined): rail is NonChannelBitcoinRail;
|
|
1544
|
+
/**
|
|
1545
|
+
* One funding event's deposit: the sats that arrived and the credit micro they
|
|
1546
|
+
* bought, with however much of that micro is still unspent.
|
|
1547
|
+
*
|
|
1548
|
+
* `remainingMicro` is depleted FIFO by settled draws and by reclaims, so the
|
|
1549
|
+
* sum of a credit's lots tracks `credits.balance_micro` exactly — with one
|
|
1550
|
+
* deliberate exception: an expiry release zeroes the balance and
|
|
1551
|
+
* leaves the lots standing, because a revival restores that balance and its
|
|
1552
|
+
* in-kind basis has to still be there. A released credit is excluded from the
|
|
1553
|
+
* sweep floor by `BITCOIN_CREDIT_PREDICATE`'s own `balance_micro > 0`, so the
|
|
1554
|
+
* over-coverage is never counted as sats the builder owes back.
|
|
1555
|
+
*/
|
|
1556
|
+
interface FundingLot {
|
|
1557
|
+
lotId: string;
|
|
1558
|
+
creditId: string;
|
|
1559
|
+
rail: NonChannelBitcoinRail;
|
|
1560
|
+
/** Sats this funding delivered. `0` on a credit funded before a rail basis was recorded. */
|
|
1561
|
+
satsFunded: number;
|
|
1562
|
+
/** Credit micro this funding bought — the lot's denominator, never depleted. */
|
|
1563
|
+
creditedMicro: number;
|
|
1564
|
+
remainingMicro: number;
|
|
1565
|
+
/** Rail settlement reference, provenance only. */
|
|
1566
|
+
fundingRef: string | null;
|
|
1567
|
+
createdAt: number;
|
|
1568
|
+
}
|
|
1569
|
+
interface LotDebit {
|
|
1570
|
+
lotId: string;
|
|
1571
|
+
micro: number;
|
|
1572
|
+
/** Decrease in this lot's floored sats obligation after taking the micro debit. */
|
|
1573
|
+
sats: number;
|
|
1574
|
+
}
|
|
1575
|
+
interface LotDepletion {
|
|
1576
|
+
debits: LotDebit[];
|
|
1577
|
+
satsOwed: number;
|
|
1578
|
+
/** Micro the lots could not cover. Non-zero means the credit's lots are short. */
|
|
1579
|
+
uncoveredMicro: number;
|
|
1580
|
+
/** Of the micro taken, how much came out of a lot carrying a real sats basis. */
|
|
1581
|
+
backedMicro: number;
|
|
1410
1582
|
}
|
|
1411
|
-
|
|
1412
1583
|
/**
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1584
|
+
* Deplete lots in FIFO order, supplied by fifoOrder. Price each debit as
|
|
1585
|
+
* lotSats(before) - lotSats(after), not the independently floored slice: small
|
|
1586
|
+
* draws then telescope to the full deposit instead of losing up to one sat
|
|
1587
|
+
* per draw. Fractional remaining sats stay with the provider.
|
|
1415
1588
|
*/
|
|
1416
|
-
|
|
1417
|
-
/** Platform DVM record ID (`dvms.id`). Identifies which DVM earned the revenue. */
|
|
1418
|
-
dvmId: string;
|
|
1419
|
-
/** Job identifier used by the platform to de-duplicate revenue rows and link to the job record. */
|
|
1420
|
-
jobId: string;
|
|
1421
|
-
/**
|
|
1422
|
-
* Capability selected by dispatch for this job. Omitted for
|
|
1423
|
-
* reports from older SDKs and legacy persisted jobs that predate capability
|
|
1424
|
-
* dispatch; absence must not be rewritten as a capability name.
|
|
1425
|
-
*/
|
|
1426
|
-
capability?: string;
|
|
1427
|
-
/** Total amount paid by the caller in millisatoshis, across all credits that satisfied the job. */
|
|
1428
|
-
paidMsats: number;
|
|
1429
|
-
/** Cashu mint URL from which the payment tokens were issued. Set only for the `cashu` rail; omitted for all other rails. */
|
|
1430
|
-
paymentMint?: string;
|
|
1431
|
-
/**
|
|
1432
|
-
* Payment rail identifier (`"cashu"`, `"tempo"`, `"x402"`, `"stripe"`).
|
|
1433
|
-
* Mirrors `PaymentMethod` on the platform; carried as a plain string over the wire
|
|
1434
|
-
* so the SDK has no platform-type dependency.
|
|
1435
|
-
*/
|
|
1436
|
-
rail: string;
|
|
1437
|
-
/**
|
|
1438
|
-
* Settlement reference for the credit: EVM tx hash for `x402`, credential
|
|
1439
|
-
* challenge ID for `tempo`, `X-Cashu-Request-Id` UUID for `cashu`. Required by the
|
|
1440
|
-
* platform revenue ledger for `tempo`/`x402` rails (recordJobRevenue throws without it);
|
|
1441
|
-
* nullable for `cashu`.
|
|
1442
|
-
*/
|
|
1443
|
-
paymentTxHash?: string;
|
|
1444
|
-
/**
|
|
1445
|
-
* Rail-native payment amount in atomic units: USDC microunits for `tempo` and
|
|
1446
|
-
* `x402`, USD cents for `stripe`. Omitted for `cashu` (platform derives
|
|
1447
|
-
* `paidMsats / 1000`). Must be set together with `nativeAsset`.
|
|
1448
|
-
*/
|
|
1449
|
-
nativeAmount?: number;
|
|
1450
|
-
/**
|
|
1451
|
-
* Asset tag paired with `nativeAmount`: `"sats"`, `"usdc"`, `"usdc.e"`, or
|
|
1452
|
-
* `"usd-cents"`. Both fields must be present for the platform to write a non-cashu
|
|
1453
|
-
* revenue row.
|
|
1454
|
-
*/
|
|
1455
|
-
nativeAsset?: string;
|
|
1456
|
-
/**
|
|
1457
|
-
* Cashu flow discriminator. Set to `"p2pk_accumulator"` when the cashu
|
|
1458
|
-
* accumulator path satisfied the job; omitted for legacy cashu. Written into
|
|
1459
|
-
* `revenue_events.metadata.cashu_flow` by the platform so ops can split per-call cashu
|
|
1460
|
-
* rows by source.
|
|
1461
|
-
*/
|
|
1462
|
-
cashuFlow?: string;
|
|
1463
|
-
/**
|
|
1464
|
-
* What the job cost the **builder** to serve, as declared by the
|
|
1465
|
-
* handler through `ctx.cost()` — the other half of the
|
|
1466
|
-
* margin the dashboard states. `amountMicro` is 1e-6 of `currency`, which
|
|
1467
|
-
* is the currency the builder incurred the cost in and need not be the
|
|
1468
|
-
* credit's.
|
|
1469
|
-
*
|
|
1470
|
-
* Never netted against `paidMsats` or `drawAmountMicro`: what the caller
|
|
1471
|
-
* paid and what serving them cost are two questions, so they are two fields.
|
|
1472
|
-
*
|
|
1473
|
-
* **Absent means unreported, not zero.** A handler that declares nothing
|
|
1474
|
-
* sends no `cost` key at all, and the platform shows the margin as unknown
|
|
1475
|
-
* rather than as 100%. A handler that declares `{ amount: 0 }` is making the
|
|
1476
|
-
* opposite, positive claim and sends `amountMicro: 0`.
|
|
1477
|
-
*
|
|
1478
|
-
* Additive on the wire: a platform build that does not read this key keeps
|
|
1479
|
-
* accepting the report unchanged.
|
|
1480
|
-
*/
|
|
1481
|
-
cost?: {
|
|
1482
|
-
amountMicro: number;
|
|
1483
|
-
currency: string;
|
|
1484
|
-
};
|
|
1485
|
-
/**
|
|
1486
|
-
* Credit this job's payment drew against. Present on
|
|
1487
|
-
* every job a ledger-backed payment satisfied; its presence is what tells
|
|
1488
|
-
* the platform the row is a **draw-keyed** revenue event rather than a
|
|
1489
|
-
* pre-credits per-call one.
|
|
1490
|
-
*/
|
|
1491
|
-
creditId?: string;
|
|
1492
|
-
/** The draw this job settled — one draw, one revenue row. */
|
|
1493
|
-
drawId?: string;
|
|
1494
|
-
/** The draw's fiat amount, 1e-6 of `creditCurrency`. The exact liability offset. */
|
|
1495
|
-
drawAmountMicro?: number;
|
|
1496
|
-
/** Currency the credit is denominated in (lowercase ISO, e.g. `usd`). */
|
|
1497
|
-
creditCurrency?: string;
|
|
1498
|
-
/**
|
|
1499
|
-
* Settlement instant (epoch ms) — draws are revenue *when they
|
|
1500
|
-
* settle*, so this dates the row. Omitted for pre-credits rows, which the
|
|
1501
|
-
* platform dates at record time as before.
|
|
1502
|
-
*/
|
|
1503
|
-
settledAt?: number;
|
|
1504
|
-
/** The funding's own rail reference, for reconciling a draw back to its deposit. */
|
|
1505
|
-
fundingRef?: string;
|
|
1506
|
-
/**
|
|
1507
|
-
* Revenue class when this row is not ordinary service revenue.
|
|
1508
|
-
* `short_pay_forfeit` marks money kept from an underpayment that bought no
|
|
1509
|
-
* job so analytics can exclude it.
|
|
1510
|
-
*/
|
|
1511
|
-
kind?: string;
|
|
1512
|
-
}
|
|
1513
|
-
/** Privacy-preserving terminal evidence; contains no caller or payment references. */
|
|
1514
|
-
interface JobTerminalReportPayload {
|
|
1515
|
-
dvmId: string;
|
|
1516
|
-
jobId: string;
|
|
1517
|
-
capability: string;
|
|
1518
|
-
outcome: "completed" | "failed" | "cancelled";
|
|
1519
|
-
endedBy: "caller" | "provider" | null;
|
|
1520
|
-
paid: boolean;
|
|
1521
|
-
acceptedAtMs: number;
|
|
1522
|
-
terminalAtMs: number;
|
|
1523
|
-
callerWaitMs: number;
|
|
1524
|
-
receiptSeq: number | null;
|
|
1525
|
-
}
|
|
1589
|
+
declare function depleteLots(lots: FundingLot[], amountMicro: number): LotDepletion;
|
|
1526
1590
|
/**
|
|
1527
|
-
*
|
|
1528
|
-
*
|
|
1529
|
-
* revenue report.
|
|
1591
|
+
* What a settling draw's fiat debit was worth in kind, in millisatoshis
|
|
1592
|
+
* `null` where the lots cannot price it.
|
|
1530
1593
|
*
|
|
1531
|
-
*
|
|
1532
|
-
*
|
|
1533
|
-
*
|
|
1534
|
-
*
|
|
1535
|
-
*
|
|
1536
|
-
*
|
|
1537
|
-
*
|
|
1594
|
+
* This is the single sats authority for a non-channel Bitcoin credit. The
|
|
1595
|
+
* settle already depletes the lots the draw consumed, so the figure costs no
|
|
1596
|
+
* extra read: it is `depletion.satsOwed` in the ledger's own msat unit, and
|
|
1597
|
+
* stamping it on `credit_draws.draw_msats` is what makes
|
|
1598
|
+
* `Σ settled draw sats + reclaim sats == Σ sats funded` close per credit.
|
|
1599
|
+
*
|
|
1600
|
+
* `null` follows {@link isInKindDepletion} exactly, so a settle and a reclaim
|
|
1601
|
+
* fall back together: lots short of the balance, or covering lots with no sats
|
|
1602
|
+
* basis, keep the pooled pro-rata figure rather than blending a lot rate with
|
|
1603
|
+
* a blended one. Mixed coverage counts as unpriceable for the same reason it
|
|
1604
|
+
* does on the reclaim.
|
|
1538
1605
|
*/
|
|
1539
|
-
|
|
1540
|
-
/** Platform DVM record ID (`dvms.id`); bound to the authenticating token. */
|
|
1541
|
-
dvmId: string;
|
|
1542
|
-
/** Job identifier and the report's platform-side idempotency key. */
|
|
1543
|
-
jobId: string;
|
|
1544
|
-
/** Capability selected by dispatch; absent only on legacy persisted jobs. */
|
|
1545
|
-
capability?: string;
|
|
1546
|
-
/** Terminal outcome that explains why this event carries no revenue. */
|
|
1547
|
-
outcome: "completed" | "failed" | "cancelled";
|
|
1548
|
-
/**
|
|
1549
|
-
* Number of declarations incorporated into `cost`; the
|
|
1550
|
-
* platform keeps the payload with the highest revision for this job.
|
|
1551
|
-
*/
|
|
1552
|
-
costRevision: number;
|
|
1553
|
-
/**
|
|
1554
|
-
* The builder's cumulative declared cost, in millionths of its currency.
|
|
1555
|
-
* `null` at a higher revision invalidates an earlier prefix when the handler
|
|
1556
|
-
* switched currencies and the final total is therefore unreportable.
|
|
1557
|
-
*/
|
|
1558
|
-
cost: {
|
|
1559
|
-
amountMicro: number;
|
|
1560
|
-
currency: string;
|
|
1561
|
-
} | null;
|
|
1562
|
-
}
|
|
1606
|
+
declare function inKindDrawMsats(depletion: LotDepletion, amountMicro: number): number | null;
|
|
1563
1607
|
/**
|
|
1564
|
-
*
|
|
1565
|
-
*
|
|
1566
|
-
*
|
|
1608
|
+
* What these lots owe in kind if every remaining micro were reclaimed now —
|
|
1609
|
+
* the deposit-liability figure, and the floor the hub must never be swept
|
|
1610
|
+
* below.
|
|
1611
|
+
*/
|
|
1612
|
+
declare function lotOwedSats(lots: FundingLot[]): number;
|
|
1613
|
+
/**
|
|
1614
|
+
* Refund delivery uses a fixed eight-sat reserve so owed_sats cannot shrink
|
|
1615
|
+
* after it is promised. Extra delivery cost is absorbed by the provider;
|
|
1616
|
+
* unused reserve stays with the provider, like fractional-sat dust.
|
|
1617
|
+
*/
|
|
1618
|
+
declare const DRAIN_DELIVERY_RESERVE_SATS = 8;
|
|
1619
|
+
/**
|
|
1620
|
+
* The figure a reclaim publishes: its in-kind gross less
|
|
1621
|
+
* {@link DRAIN_DELIVERY_RESERVE_SATS}, floored at zero.
|
|
1567
1622
|
*
|
|
1568
|
-
*
|
|
1569
|
-
*
|
|
1623
|
+
* Zero is a refusal rather than a payout — a balance worth no more than what
|
|
1624
|
+
* it costs to send is `drain_below_dust`, since parking a zero-value token
|
|
1625
|
+
* would hold the servicer's queue open forever.
|
|
1570
1626
|
*/
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
*
|
|
1604
|
-
* `CloseRequested` event back to the DVM that owns the channel.
|
|
1605
|
-
*
|
|
1606
|
-
* Rides the deposit report rather than a side channel on purpose — accepting
|
|
1607
|
-
* the voucher and booking the registration then share the one durable commit
|
|
1608
|
-
* boundary, so a registered channel can never be a channel whose funding
|
|
1609
|
-
* rolled back, and the existing retry drain delivers it after a crash. The
|
|
1610
|
-
* registration is therefore asynchronous relative to the close: an event can
|
|
1611
|
-
* legitimately arrive first, which is what the platform's payee fallback is
|
|
1612
|
-
* for.
|
|
1627
|
+
declare function netOwedSats(grossSats: number): number;
|
|
1628
|
+
/**
|
|
1629
|
+
* The order lots deplete in: oldest first, `lotId` breaking a tie.
|
|
1630
|
+
*
|
|
1631
|
+
* A tiebreak is not decoration — lot ids are surrogates rather than a
|
|
1632
|
+
* per-credit sequence (`fund` takes no credit lock, so two concurrent
|
|
1633
|
+
* fundings cannot cooperate on a counter), and two fundings can land on the
|
|
1634
|
+
* same millisecond. Without it the FIFO order is whatever the planner
|
|
1635
|
+
* returned, and a reclaim's figure would depend on it.
|
|
1636
|
+
*/
|
|
1637
|
+
declare function fifoOrder(left: FundingLot, right: FundingLot): number;
|
|
1638
|
+
/**
|
|
1639
|
+
* Missing lot coverage or a zero legacy sats basis requires pooled pricing.
|
|
1640
|
+
* Mixed backed/unbacked lots also fall back: combining lot and live rates
|
|
1641
|
+
* would claim a value neither basis supports.
|
|
1642
|
+
*/
|
|
1643
|
+
declare function isInKindDepletion(depletion: LotDepletion, amountMicro: number): boolean;
|
|
1644
|
+
|
|
1645
|
+
/** The rails a payout lands on. Lightning never pays out: credits fund straight into the receive wallet. */
|
|
1646
|
+
type PayoutRail = "cashu" | "x402" | "tempo";
|
|
1647
|
+
/**
|
|
1648
|
+
* Transfers aggregate payments: a Cashu melt, x402 batch to pay_to, or Tempo recipient
|
|
1649
|
+
* settlement/close.
|
|
1650
|
+
*/
|
|
1651
|
+
type PayoutKind = "melt" | "batch" | "close" | "settle";
|
|
1652
|
+
type PayoutRepairKind = "x402_settlement_reconciled" | "lightning_invoice_reconciled" | "tempo_close_reconciled";
|
|
1653
|
+
/**
|
|
1654
|
+
* One landed movement into builder custody. Platform retries deduplicate by (dvmId,
|
|
1655
|
+
* payoutId); repaired redelivery upgrades that movement rather than adding another.
|
|
1656
|
+
*/
|
|
1657
|
+
interface PayoutReportPayload {
|
|
1658
|
+
/**
|
|
1659
|
+
* Platform record ID; must match the bearer token’s DVM.
|
|
1613
1660
|
*/
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
escrow: string;
|
|
1620
|
-
/** The DVM's on-chain recipient — the fallback routing key. */
|
|
1621
|
-
payee?: string;
|
|
1622
|
-
};
|
|
1661
|
+
dvmId: string;
|
|
1662
|
+
/** Required. The DVM's own id for the movement — `cashu:melt:<quote>`, `x402:batch:<uuid>`, `tempo:<tx>`. */
|
|
1663
|
+
payoutId: string;
|
|
1664
|
+
rail: PayoutRail;
|
|
1665
|
+
kind: PayoutKind;
|
|
1623
1666
|
/**
|
|
1624
|
-
*
|
|
1625
|
-
* was accepted on, emitted **whenever the funding was session-based** —
|
|
1626
|
-
* independently of whether {@link CreditDepositPayload.tempoChannel} could be
|
|
1627
|
-
* read. That independence is the entire point: `rail: "tempo"` covers both
|
|
1628
|
-
* `tempo/session` and the `tempo/charge` one-shot, and a charge legitimately
|
|
1629
|
-
* carries no channel at all, so without this the platform cannot tell a
|
|
1630
|
-
* routine charge deposit from a session whose chain binding was lost — the
|
|
1631
|
-
* shape, where the close observer silently never watches the escrow.
|
|
1632
|
-
*
|
|
1633
|
-
* Carries only the channel id, not the chain binding: a DVM that could read
|
|
1634
|
-
* the binding sends `tempoChannel` too, and one that could not still names
|
|
1635
|
-
* the channel an operator has to go and look at.
|
|
1667
|
+
* Positive rail-native atomic units: sats or USDC micro.
|
|
1636
1668
|
*/
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1669
|
+
nativeAmount: number;
|
|
1670
|
+
nativeAsset: "sats" | "usdc";
|
|
1671
|
+
/** Where it landed; `null` for a melt, whose Lightning destination the platform never records. */
|
|
1672
|
+
recipient: string | null;
|
|
1641
1673
|
/**
|
|
1642
|
-
*
|
|
1643
|
-
* Lightning invoice the builder's agent reconciled onto a credit. The money
|
|
1644
|
-
* landed in the receive wallet at payment time and this deposit is the one
|
|
1645
|
-
* row it gets, so the settlements table types it `repair` off this flag
|
|
1646
|
-
* rather than off a second row.
|
|
1674
|
+
* Descriptive mechanism references, never an arithmetic basis.
|
|
1647
1675
|
*/
|
|
1676
|
+
refs: Record<string, unknown>;
|
|
1677
|
+
/** Required. When the money landed (epoch ms). */
|
|
1678
|
+
landedAt: number;
|
|
1648
1679
|
repaired?: boolean;
|
|
1680
|
+
/** Required iff `repaired`. */
|
|
1681
|
+
repairKind?: PayoutRepairKind;
|
|
1649
1682
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
/**
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1683
|
+
interface PayoutWedge {
|
|
1684
|
+
id: string;
|
|
1685
|
+
/**
|
|
1686
|
+
* `settle`: an x402 batch whose transfer keeps failing (auto-retried).
|
|
1687
|
+
* `refund`: an x402 cooperative refund wedged between chain and ledger —
|
|
1688
|
+
* caller money, not in the pool. `close`: a Tempo close wedged the same way.
|
|
1689
|
+
* `melt`: cashu rows whose builder-side melt failed.
|
|
1690
|
+
*/
|
|
1691
|
+
kind: "settle" | "refund" | "close" | "melt";
|
|
1692
|
+
/** Epoch ms the wedge was first observed. */
|
|
1693
|
+
since: number;
|
|
1694
|
+
/** Retry count where the retry is automatic, else null. */
|
|
1695
|
+
attempts: number | null;
|
|
1696
|
+
/** Epoch ms of the next automatic retry, else null. */
|
|
1697
|
+
nextRetry: number | null;
|
|
1698
|
+
lastError: string | null;
|
|
1699
|
+
/** Rail-native amount held up, when known. */
|
|
1700
|
+
native?: number;
|
|
1701
|
+
}
|
|
1702
|
+
interface PayoutPool {
|
|
1703
|
+
id: string;
|
|
1704
|
+
native: number;
|
|
1705
|
+
label?: string;
|
|
1673
1706
|
}
|
|
1674
|
-
type CreditDrainRepairKind = "x402_settlement_reconciled" | "tempo_close_reconciled";
|
|
1675
1707
|
/**
|
|
1676
|
-
*
|
|
1677
|
-
*
|
|
1678
|
-
* have not paid the caller. Idempotency key: (dvmId, creditId, drainId).
|
|
1708
|
+
* A replaceable snapshot of one rail’s pending builder funds. Best-effort delivery uses no
|
|
1709
|
+
* durable queue; a later tick supersedes a lost report.
|
|
1679
1710
|
*/
|
|
1680
|
-
interface
|
|
1681
|
-
/** Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
|
|
1711
|
+
interface PayoutPendingPayload {
|
|
1682
1712
|
dvmId: string;
|
|
1683
|
-
|
|
1684
|
-
/**
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
/**
|
|
1691
|
-
|
|
1692
|
-
/** Currency `amountMicro` is 1e-6 of. */
|
|
1693
|
-
creditCurrency: string;
|
|
1694
|
-
/** Terminal state: `"picked_up"` for cashu, `"sent"` for other rails. */
|
|
1695
|
-
status: string;
|
|
1696
|
-
/** When the money left (epoch ms). */
|
|
1697
|
-
drainedAt: number;
|
|
1698
|
-
/** Per-credit gap-free ledger sequence, for reconciling against the DVM's ledger. */
|
|
1699
|
-
ledgerSeq?: number;
|
|
1700
|
-
/** Set when an operator repair verb completed this reclaim. */
|
|
1701
|
-
repaired?: boolean;
|
|
1702
|
-
/** Required iff `repaired`; identifies the repair path that completed the reclaim. */
|
|
1703
|
-
repairKind?: CreditDrainRepairKind;
|
|
1713
|
+
rail: PayoutRail;
|
|
1714
|
+
/** Everything on this rail still moving toward the builder, in atomic units. */
|
|
1715
|
+
poolNative: number;
|
|
1716
|
+
nativeAsset: "sats" | "usdc";
|
|
1717
|
+
items: number;
|
|
1718
|
+
pools: PayoutPool[];
|
|
1719
|
+
wedged: PayoutWedge[];
|
|
1720
|
+
/** This DVM's clock when the snapshot was taken (epoch ms). */
|
|
1721
|
+
snapshotAt: number;
|
|
1704
1722
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1723
|
+
interface PayoutTransport {
|
|
1724
|
+
/**
|
|
1725
|
+
* Queue a payout through the caller's open transaction, so the row and the
|
|
1726
|
+
* fact it reports commit or roll back together; without `tx` it is queued on
|
|
1727
|
+
* its own. Durable once committed — the retry loop delivers it, or
|
|
1728
|
+
* {@link PayoutTransport.drainPending} when a caller wants it now.
|
|
1729
|
+
*/
|
|
1730
|
+
enqueuePayout(payload: PayoutReportPayload, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1731
|
+
enqueueHostedPayout?(payload: PayoutReportPayload, obligationIds: string[], tx?: RevenueReporterQuerier): Promise<void>;
|
|
1732
|
+
/** Queue a payout on its own and try to deliver it at once. */
|
|
1733
|
+
reportPayout(payload: PayoutReportPayload): Promise<void>;
|
|
1734
|
+
/** Best-effort: one POST, replaced by the next snapshot. */
|
|
1735
|
+
reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
|
|
1736
|
+
/** Deliver what is queued now rather than on the next retry tick. */
|
|
1737
|
+
drainPending?(): Promise<void>;
|
|
1711
1738
|
}
|
|
1712
1739
|
/**
|
|
1713
|
-
*
|
|
1714
|
-
*
|
|
1715
|
-
* Implemented by {@link RevenueReporter.enqueueDeposit}; callers must await it
|
|
1716
|
-
* before committing the rail receive and ledger funding.
|
|
1717
|
-
*/
|
|
1718
|
-
type CreditDepositEnqueue = (tx: RevenueReporterQuerier, payload: CreditDepositPayload) => Promise<void>;
|
|
1719
|
-
/** Transactional draw-release outbox seam used by the credit ledger. */
|
|
1720
|
-
type CreditDrawReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditDrawReleasePayload) => Promise<void>;
|
|
1721
|
-
/**
|
|
1722
|
-
* Transactional drain-outbox seam threaded through each terminal drain
|
|
1723
|
-
* transition.
|
|
1724
|
-
*
|
|
1725
|
-
* Implemented by {@link RevenueReporter.enqueueCreditDrain}; callers must await
|
|
1726
|
-
* it before committing the status CAS that moved the money.
|
|
1727
|
-
*/
|
|
1728
|
-
type CreditDrainEnqueue = (tx: RevenueReporterQuerier, payload: CreditDrainPayload) => Promise<void>;
|
|
1729
|
-
/**
|
|
1730
|
-
* JSON wire shape POSTed to the platform's `/_internal/credit-expiry-release`
|
|
1731
|
-
* endpoint. A prepaid credit that reaches its TTL with an undrawn
|
|
1732
|
-
* remainder RELEASES that remainder to the builder: on cashu, Lightning and
|
|
1733
|
-
* x402 the funding value reached builder custody at funding time or on the
|
|
1734
|
-
* settlement cycle, so expiry extinguishes the caller's remaining claim.
|
|
1735
|
-
* Reported so committed value nets to
|
|
1736
|
-
* `deposited − drawn − drained − released`; without it an expired credit is
|
|
1737
|
-
* owed forever on paper, exactly like an unreported drain.
|
|
1738
|
-
*
|
|
1739
|
-
* Never sent for a Tempo credit. Tempo balances are channel-backed and undrawn
|
|
1740
|
-
* channel value returns to the *caller* at exit, so no release exists on that
|
|
1741
|
-
* rail — the sweep skips them and the platform refuses one outright.
|
|
1742
|
-
*
|
|
1743
|
-
* Two reports share this shape, distinguished only by `reversedAt`:
|
|
1744
|
-
*
|
|
1745
|
-
* - **The release**, written by the expiry sweep with `reversedAt` absent.
|
|
1746
|
-
* - **The reversal**, written when a later funding revives the credit
|
|
1747
|
-
* (`CreditLedger.fund` overwrites `expiry_ms`, so the recorded remainder is
|
|
1748
|
-
* no longer final). It repeats the whole payload rather than sending a
|
|
1749
|
-
* reference, because the retry loop can deliver the two out of order and the
|
|
1750
|
-
* platform's upsert must be able to land either one first.
|
|
1751
|
-
*
|
|
1752
|
-
* Idempotent on `(dvmId, creditId, releaseId)`, so the shared retry loop can
|
|
1753
|
-
* redeliver freely.
|
|
1740
|
+
* enqueue joins the money transaction and must throw on failure so its fact rolls back.
|
|
1741
|
+
* committed runs after commit, when delivery and snapshots can safely read the new state.
|
|
1754
1742
|
*/
|
|
1755
|
-
interface
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
/**
|
|
1773
|
-
|
|
1743
|
+
interface TransactionalPayoutHook<E> {
|
|
1744
|
+
enqueue(event: E, tx: RevenueReporterQuerier): Promise<void>;
|
|
1745
|
+
committed(): void;
|
|
1746
|
+
}
|
|
1747
|
+
interface CashuMeltCompleted {
|
|
1748
|
+
rows: {
|
|
1749
|
+
id: string;
|
|
1750
|
+
mintUrl: string;
|
|
1751
|
+
proofAmount: number;
|
|
1752
|
+
}[];
|
|
1753
|
+
meltQuoteId: string;
|
|
1754
|
+
paymentPreimage: string;
|
|
1755
|
+
}
|
|
1756
|
+
interface TempoSessionSettled {
|
|
1757
|
+
txHash: string;
|
|
1758
|
+
channelId: string;
|
|
1759
|
+
trigger: "settle" | "close" | "scheduled";
|
|
1760
|
+
/** Cumulative amount settled to the payee on this channel, atomic units. */
|
|
1761
|
+
amount: bigint;
|
|
1762
|
+
/** Newly settled to the payee by this transaction, atomic units. */
|
|
1763
|
+
delta: bigint;
|
|
1764
|
+
}
|
|
1765
|
+
interface TempoCloseReconciled {
|
|
1766
|
+
channelId: string;
|
|
1767
|
+
txHash: string;
|
|
1768
|
+
/** Captured by the payee — this DVM — per the receipt, atomic units as a decimal string. */
|
|
1769
|
+
settledToPayee: string;
|
|
1770
|
+
}
|
|
1771
|
+
interface X402TrackedChannel {
|
|
1772
|
+
channelId: string;
|
|
1773
|
+
chargedCumulativeAmount: string;
|
|
1774
|
+
totalClaimed: string;
|
|
1775
|
+
}
|
|
1776
|
+
interface X402WedgedRefund {
|
|
1777
|
+
settlementId: string;
|
|
1778
|
+
/** Epoch ms the settlement was first prepared. */
|
|
1779
|
+
createdAt: number;
|
|
1780
|
+
native?: number;
|
|
1781
|
+
}
|
|
1782
|
+
interface X402PayoutContext {
|
|
1783
|
+
/** `${network}|${payTo}|${token}` — the scope the settle-pending marker is keyed on. */
|
|
1784
|
+
scope: string;
|
|
1785
|
+
payTo: string;
|
|
1786
|
+
network: string;
|
|
1787
|
+
storage: {
|
|
1788
|
+
list(): Promise<X402TrackedChannel[]>;
|
|
1789
|
+
};
|
|
1790
|
+
/** The scheduler's settle cadence — what a wedge's `nextRetry` is derived from. */
|
|
1791
|
+
settleIntervalMs: number;
|
|
1792
|
+
listWedgedRefunds?: () => Promise<X402WedgedRefund[]>;
|
|
1793
|
+
}
|
|
1794
|
+
interface X402TrackedManager {
|
|
1795
|
+
claim(...args: never[]): Promise<{
|
|
1796
|
+
vouchers: number;
|
|
1797
|
+
transaction: string;
|
|
1798
|
+
}[]>;
|
|
1799
|
+
settle(): Promise<{
|
|
1800
|
+
transaction: string;
|
|
1801
|
+
}>;
|
|
1802
|
+
refund?(channelIds?: string[]): Promise<{
|
|
1803
|
+
channel: string;
|
|
1804
|
+
transaction: string;
|
|
1805
|
+
}[]>;
|
|
1774
1806
|
}
|
|
1775
1807
|
/**
|
|
1776
|
-
*
|
|
1777
|
-
* and through the
|
|
1778
|
-
*
|
|
1779
|
-
* Implemented by {@link RevenueReporter.enqueueCreditExpiryRelease}; callers
|
|
1780
|
-
* must await it before committing the write that made the release (or its
|
|
1781
|
-
* reversal) true, for the same reason the drain seam does — a report lost
|
|
1782
|
-
* between the two overstates or understates committed value forever.
|
|
1783
|
-
*/
|
|
1784
|
-
type CreditExpiryReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditExpiryReleasePayload) => Promise<void>;
|
|
1785
|
-
/**
|
|
1786
|
-
* JSON wire shape POSTed to the platform's `/_internal/paid-job-death` endpoint
|
|
1787
|
-
* when the stale-job reaper force-fails a *paid* job — its pending
|
|
1788
|
-
* credit draw is released rather than settled, so the caller's payment is back
|
|
1789
|
-
* on their credit balance and reclaimable by drain. The platform resolves fiat
|
|
1790
|
-
* + owner and sends an ambient notice via the shared alert sink. Unlike
|
|
1791
|
-
* {@link RevenueReportPayload} this carries no durable-retry semantics: the
|
|
1792
|
-
* reaper's `stale_job_failed` structured log is the durable backstop, so a lost
|
|
1793
|
-
* report degrades to log-only rather than blocking the sweep.
|
|
1808
|
+
* The batch-settlement server's view of the reporter: attach once with the
|
|
1809
|
+
* scope, then route every claim and settle through the tracked manager so
|
|
1810
|
+
* the open batch is kept and the settle emits the payout.
|
|
1794
1811
|
*/
|
|
1795
|
-
interface
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
/**
|
|
1801
|
-
|
|
1802
|
-
/**
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
+
interface X402PayoutObserver {
|
|
1813
|
+
attach(ctx: X402PayoutContext): void;
|
|
1814
|
+
trackManager<M extends X402TrackedManager>(manager: M): M;
|
|
1815
|
+
/** A settle that landed outside the tracked manager — the manual claim-and-settle's own retry loop. */
|
|
1816
|
+
recordSettle(transaction: string): Promise<void>;
|
|
1817
|
+
/** A settle attempt that failed outside the tracked manager. One call per attempt a builder would count as one. */
|
|
1818
|
+
recordSettleFailure(error: unknown): Promise<void>;
|
|
1819
|
+
/** A credit-drain refund's bundled claim, recorded after its exact pre-effect reservation. */
|
|
1820
|
+
recordRefundClaim?(transaction: string, delta: bigint): Promise<void>;
|
|
1821
|
+
}
|
|
1822
|
+
interface TempoPayoutReader {
|
|
1823
|
+
recipient?: string;
|
|
1824
|
+
/** CAIP-2 network for the refs, when known. */
|
|
1825
|
+
network?: string;
|
|
1826
|
+
listActive?: (limit: number, cursor?: {
|
|
1827
|
+
updatedAt: number;
|
|
1828
|
+
key: string;
|
|
1829
|
+
}) => Promise<{
|
|
1830
|
+
key: string;
|
|
1831
|
+
state: Record<string, unknown>;
|
|
1832
|
+
updatedAt: number;
|
|
1833
|
+
}[]>;
|
|
1834
|
+
listChannelDrains?: (args: {
|
|
1835
|
+
limit: number;
|
|
1836
|
+
after?: ChannelDrainCursor;
|
|
1837
|
+
createdBeforeMs: number;
|
|
1838
|
+
rail: "tempo";
|
|
1839
|
+
}) => Promise<CreditDrainRecord[]>;
|
|
1812
1840
|
}
|
|
1813
|
-
type RevenueSkippedNoRailReason = "credit_rail_null" | "credit_rail_unrecognized" | "job_rail_missing" | "ledger_not_configured";
|
|
1814
1841
|
/**
|
|
1815
|
-
*
|
|
1816
|
-
*
|
|
1817
|
-
* for its revenue event. This is an operator notice rather than a durable
|
|
1818
|
-
* accounting report: the SDK's structured log remains the durable backstop,
|
|
1819
|
-
* and a failed alert must never block terminal handling or reconciliation.
|
|
1842
|
+
* Claims accumulated since the last settle. The batch is durable across machines: claims and
|
|
1843
|
+
* eventual settlement can run on different hosts under the fleet lock.
|
|
1820
1844
|
*/
|
|
1821
|
-
interface
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
/** Credit funding rail as stored on the draw, or null for legacy rows. */
|
|
1834
|
-
creditRail: string | null;
|
|
1835
|
-
reason: RevenueSkippedNoRailReason;
|
|
1845
|
+
interface X402OpenBatch {
|
|
1846
|
+
scope: string;
|
|
1847
|
+
batchId: string;
|
|
1848
|
+
claimedNative: bigint;
|
|
1849
|
+
voucherCount: number;
|
|
1850
|
+
claims: number;
|
|
1851
|
+
openedAt: number;
|
|
1852
|
+
attempts: number;
|
|
1853
|
+
firstFailedAt: number | null;
|
|
1854
|
+
lastError: string | null;
|
|
1855
|
+
nextRetryAt: number | null;
|
|
1856
|
+
obligationIds?: string[];
|
|
1836
1857
|
}
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
/**
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
balanceWei: string;
|
|
1847
|
-
/** Configured paging floor as a decimal wei string. */
|
|
1848
|
-
lowBalanceWei: string;
|
|
1849
|
-
/** Millisecond timestamp at which the DVM read the balance. */
|
|
1850
|
-
checkedAt: number;
|
|
1858
|
+
interface X402BatchStore {
|
|
1859
|
+
get(scope: string): Promise<X402OpenBatch | undefined>;
|
|
1860
|
+
put(batch: X402OpenBatch): Promise<void>;
|
|
1861
|
+
delete(scope: string): Promise<void>;
|
|
1862
|
+
/**
|
|
1863
|
+
* Atomically delete the matching batch and enqueue its payout. Failed enqueue preserves the
|
|
1864
|
+
* batch for retry; false means the scope no longer holds batchId and nothing was queued.
|
|
1865
|
+
*/
|
|
1866
|
+
settle(scope: string, batchId: string, enqueue: (tx?: RevenueReporterQuerier) => Promise<void>): Promise<boolean>;
|
|
1851
1867
|
}
|
|
1852
|
-
|
|
1853
|
-
type X402SelfRelayRpcHealthPayload = {
|
|
1854
|
-
/** Platform DVM record ID, bound to the authenticating platform token. */
|
|
1855
|
-
dvmId: string;
|
|
1856
|
-
/** Dedicated relay EOA identifying the affected self-relay. */
|
|
1857
|
-
address: `0x${string}`;
|
|
1858
|
-
/** CAIP-2 Base network identifier. */
|
|
1859
|
-
network: string;
|
|
1860
|
-
/** Healthy transition clears an open RPC page. */
|
|
1861
|
-
status: "healthy";
|
|
1862
|
-
/** Millisecond timestamp at which the DVM observed the transition. */
|
|
1863
|
-
checkedAt: number;
|
|
1864
|
-
} | {
|
|
1865
|
-
/** Platform DVM record ID, bound to the authenticating platform token. */
|
|
1866
|
-
dvmId: string;
|
|
1867
|
-
/** Dedicated relay EOA identifying the affected self-relay. */
|
|
1868
|
-
address: `0x${string}`;
|
|
1869
|
-
/** CAIP-2 Base network identifier. */
|
|
1870
|
-
network: string;
|
|
1871
|
-
/** Failed transition opens or refreshes an RPC page. */
|
|
1872
|
-
status: "failed";
|
|
1873
|
-
/** Redacted failure class; never the provider error or endpoint URL. */
|
|
1874
|
-
reason: X402SelfRelayRpcFailureReason;
|
|
1875
|
-
/** Millisecond timestamp at which the DVM observed the transition. */
|
|
1876
|
-
checkedAt: number;
|
|
1877
|
-
};
|
|
1878
|
-
/** Configured Tempo operator fee-token balance POSTed by a hosted DVM. */
|
|
1879
|
-
interface TempoSettlementBalancePayload {
|
|
1868
|
+
interface PayoutReporterOpts {
|
|
1880
1869
|
dvmId: string;
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1870
|
+
transport: PayoutTransport;
|
|
1871
|
+
/**
|
|
1872
|
+
* Postgres holds durable x402 batches and supplies Cashu snapshots. Without it, the default
|
|
1873
|
+
* batch store is memory and Cashu snapshots are omitted.
|
|
1874
|
+
*/
|
|
1875
|
+
db?: Pool;
|
|
1876
|
+
/**
|
|
1877
|
+
* Only accumulator hosts create wallet_accumulator; enabling this on another host makes
|
|
1878
|
+
* each snapshot query fail.
|
|
1879
|
+
*/
|
|
1880
|
+
cashu?: boolean;
|
|
1881
|
+
/** Override the batch store (tests); defaults to Postgres on `db`, memory without. */
|
|
1882
|
+
batchStore?: X402BatchStore;
|
|
1883
|
+
hostedObligations?: HostedSettlementReportObligations;
|
|
1884
|
+
/** Snapshot cadence, ms. Default 5 minutes. */
|
|
1885
|
+
snapshotIntervalMs?: number;
|
|
1886
|
+
now?: () => number;
|
|
1888
1887
|
}
|
|
1889
|
-
/**
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1888
|
+
/**
|
|
1889
|
+
* Cashu melts and repaired Tempo closes enqueue inside their caller’s transaction and
|
|
1890
|
+
* propagate failures. x402 reporting and pending snapshots instead log failures; failed x402
|
|
1891
|
+
* enqueue preserves the open batch for retry.
|
|
1892
|
+
*/
|
|
1893
|
+
declare class PayoutReporter implements PayoutTransport {
|
|
1894
|
+
private readonly dvmId;
|
|
1895
|
+
private readonly transport;
|
|
1896
|
+
private readonly db?;
|
|
1897
|
+
private readonly cashu;
|
|
1898
|
+
private readonly batches;
|
|
1899
|
+
private readonly ownsBatchStore;
|
|
1900
|
+
private readonly intervalMs;
|
|
1901
|
+
private readonly now;
|
|
1902
|
+
private readonly hostedObligations?;
|
|
1903
|
+
private lastX402SnapshotAt;
|
|
1904
|
+
private x402?;
|
|
1905
|
+
private tempo?;
|
|
1906
|
+
private timer;
|
|
1907
|
+
private warmup;
|
|
1908
|
+
constructor(opts: PayoutReporterOpts);
|
|
1909
|
+
init(): Promise<void>;
|
|
1910
|
+
/**
|
|
1911
|
+
* Start the snapshot loop. Unref'd, so it never keeps a scale-to-zero
|
|
1912
|
+
* machine awake; a first snapshot runs shortly after boot so a fresh process
|
|
1913
|
+
* reports without waiting a whole interval.
|
|
1914
|
+
*/
|
|
1915
|
+
start(): void;
|
|
1916
|
+
stop(): void;
|
|
1917
|
+
private reconcileHosted;
|
|
1918
|
+
enqueuePayout(payload: PayoutReportPayload, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1919
|
+
reportPayout(payload: PayoutReportPayload): Promise<void>;
|
|
1920
|
+
reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
|
|
1921
|
+
/** Deliver what is queued now; a transport without the seam waits for its retry loop. */
|
|
1922
|
+
flush(): Promise<void>;
|
|
1923
|
+
meltHook(): TransactionalPayoutHook<CashuMeltCompleted>;
|
|
1924
|
+
/**
|
|
1925
|
+
* Report melted face value per mint, not the Lightning amount received after fees. Hash the
|
|
1926
|
+
* payment preimage; do not publish it. With tx, propagate enqueue failures and defer
|
|
1927
|
+
* delivery until commit. Without tx, enqueue independently and start delivery.
|
|
1928
|
+
*/
|
|
1929
|
+
cashuMelted(event: CashuMeltCompleted, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1930
|
+
attachTempo(reader: TempoPayoutReader): void;
|
|
1931
|
+
/**
|
|
1932
|
+
* A Tempo channel settled or closed on chain. `delta` is what this
|
|
1933
|
+
* transaction newly paid the recipient; a transaction that paid nothing new
|
|
1934
|
+
* (a close of a fully settled channel) is no payout.
|
|
1935
|
+
*/
|
|
1936
|
+
tempoSettled(event: TempoSessionSettled): Promise<void>;
|
|
1937
|
+
tempoRepairHook(): TransactionalPayoutHook<TempoCloseReconciled>;
|
|
1938
|
+
/**
|
|
1939
|
+
* Report the receipt’s payee total under the live close’s transaction ID, flagged repaired.
|
|
1940
|
+
* The platform upgrades a live row if one exists. Join tx and propagate enqueue failures;
|
|
1941
|
+
* this does not report the caller refund.
|
|
1942
|
+
*/
|
|
1943
|
+
tempoCloseReconciled(event: TempoCloseReconciled, tx?: RevenueReporterQuerier): Promise<void>;
|
|
1944
|
+
x402Observer(): X402PayoutObserver;
|
|
1945
|
+
/**
|
|
1946
|
+
* Read claim deltas from storage because upstream returns voucher counts. Under the fleet
|
|
1947
|
+
* lock, before/after reads are consistent. A deleted channel contributes its pre-claim
|
|
1948
|
+
* unclaimed value, including refund claim-then-delete.
|
|
1949
|
+
*/
|
|
1950
|
+
private trackX402Manager;
|
|
1951
|
+
private recordX402Claim;
|
|
1952
|
+
private recordX402Settle;
|
|
1953
|
+
private recordX402SettleFailure;
|
|
1954
|
+
/** Every rail this reporter can read, each on its own failure boundary. */
|
|
1955
|
+
snapshot(): Promise<void>;
|
|
1956
|
+
/**
|
|
1957
|
+
* Cashu: every unmelted proof in the accumulator, per mint. Rows whose
|
|
1958
|
+
* builder-side melt failed are still in the pool (they are still at the
|
|
1959
|
+
* mint) and listed as wedged until `restart-failed` clears them.
|
|
1960
|
+
*/
|
|
1961
|
+
snapshotCashu(): Promise<void>;
|
|
1962
|
+
/**
|
|
1963
|
+
* x402: the open batch (claimed, not yet transferred) plus every channel's
|
|
1964
|
+
* unclaimed voucher value; wedged when the settle keeps failing, and the
|
|
1965
|
+
* refund settlements stuck between chain and ledger beside it.
|
|
1966
|
+
*/
|
|
1967
|
+
snapshotX402(): Promise<void>;
|
|
1968
|
+
/**
|
|
1969
|
+
* Tempo: each active channel's spent-but-unsettled balance — earned, not
|
|
1970
|
+
* yet paid to the recipient — and the cooperative closes wedged between
|
|
1971
|
+
* chain and ledger, aged past the same floor the repair queue
|
|
1972
|
+
* uses so an in-flight close is not reported as stuck.
|
|
1973
|
+
*/
|
|
1974
|
+
snapshotTempo(): Promise<void>;
|
|
1902
1975
|
}
|
|
1976
|
+
|
|
1903
1977
|
/**
|
|
1904
|
-
*
|
|
1905
|
-
*
|
|
1906
|
-
* On job completion the SDK calls `report()`, which persists the payload to
|
|
1907
|
-
* a local Postgres table and attempts an immediate POST to the platform.
|
|
1908
|
-
* A background retry loop picks up un-acked rows with exponential backoff.
|
|
1909
|
-
* Gauge and health notifications are best-effort: failed delivery is logged
|
|
1910
|
-
* and dropped rather than queued for retry.
|
|
1978
|
+
* JSON wire shape POSTed to the platform's `/_internal/job-revenue` endpoint
|
|
1979
|
+
* and stored in `pending_dvm_reports.payload` for durable retry.
|
|
1911
1980
|
*/
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
/** Create the pending-reports table and migrate the legacy SDK outbox (idempotent). */
|
|
1922
|
-
init(): Promise<void>;
|
|
1923
|
-
private createTables;
|
|
1924
|
-
private migrateLegacySdkOutbox;
|
|
1925
|
-
private assertSdkTableShape;
|
|
1926
|
-
private assertSdkColumns;
|
|
1927
|
-
private tableColumns;
|
|
1928
|
-
/** Persist a revenue report and attempt immediate delivery. */
|
|
1929
|
-
report(payload: RevenueReportPayload): Promise<void>;
|
|
1930
|
-
/** The permanent deduplication claim and outbox insert commit together.
|
|
1931
|
-
* The claim survives successful delivery, including for custom job stores.
|
|
1981
|
+
interface RevenueReportPayload {
|
|
1982
|
+
/** Platform DVM record ID (`dvms.id`). Identifies which DVM earned the revenue. */
|
|
1983
|
+
dvmId: string;
|
|
1984
|
+
/** Job identifier used by the platform to de-duplicate revenue rows and link to the job record. */
|
|
1985
|
+
jobId: string;
|
|
1986
|
+
/**
|
|
1987
|
+
* Capability selected by dispatch for this job. Omitted for
|
|
1988
|
+
* reports from older SDKs and legacy persisted jobs that predate capability
|
|
1989
|
+
* dispatch; absence must not be rewritten as a capability name.
|
|
1932
1990
|
*/
|
|
1933
|
-
|
|
1934
|
-
/**
|
|
1935
|
-
|
|
1991
|
+
capability?: string;
|
|
1992
|
+
/** Total amount paid by the caller in millisatoshis, across all credits that satisfied the job. */
|
|
1993
|
+
paidMsats: number;
|
|
1994
|
+
/** Cashu mint URL from which the payment tokens were issued. Set only for the `cashu` rail; omitted for all other rails. */
|
|
1995
|
+
paymentMint?: string;
|
|
1936
1996
|
/**
|
|
1937
|
-
*
|
|
1938
|
-
*
|
|
1939
|
-
*
|
|
1940
|
-
* durable queue row through `tx`. The retry drain delivers the row after the
|
|
1941
|
-
* rail transaction commits, including after a process restart.
|
|
1997
|
+
* Payment rail identifier (`"cashu"`, `"tempo"`, `"x402"`, `"stripe"`).
|
|
1998
|
+
* Mirrors `PaymentMethod` on the platform; carried as a plain string over the wire
|
|
1999
|
+
* so the SDK has no platform-type dependency.
|
|
1942
2000
|
*/
|
|
1943
|
-
|
|
1944
|
-
/** Join a released draw to the transaction that made the hold terminal. */
|
|
1945
|
-
enqueueCreditDrawRelease(tx: RevenueReporterQuerier, payload: CreditDrawReleasePayload): Promise<void>;
|
|
2001
|
+
rail: string;
|
|
1946
2002
|
/**
|
|
1947
|
-
*
|
|
1948
|
-
*
|
|
2003
|
+
* Settlement reference for the credit: EVM tx hash for `x402`, credential
|
|
2004
|
+
* challenge ID for `tempo`, `X-Cashu-Request-Id` UUID for `cashu`. Required by the
|
|
2005
|
+
* platform revenue ledger for `tempo`/`x402` rails (recordJobRevenue throws without it);
|
|
2006
|
+
* nullable for `cashu`.
|
|
1949
2007
|
*/
|
|
1950
|
-
|
|
2008
|
+
paymentTxHash?: string;
|
|
1951
2009
|
/**
|
|
1952
|
-
*
|
|
1953
|
-
*
|
|
1954
|
-
*
|
|
1955
|
-
* {@link enqueueCreditDrain} set and for the same reason: the money has
|
|
1956
|
-
* already moved on a rail this ledger does not own, so the platform row is
|
|
1957
|
-
* the only record the builder's dashboard has of it, and a row lost between
|
|
1958
|
-
* the fact and the queue would understate "paid out" forever. Without `tx`
|
|
1959
|
-
* the row is queued on the pool, for callers with no fact of their own to
|
|
1960
|
-
* commit. Idempotent on `(dvmId, payoutId)` platform-side, so the retry
|
|
1961
|
-
* loop redelivers freely.
|
|
2010
|
+
* Rail-native payment amount in atomic units: USDC microunits for `tempo` and
|
|
2011
|
+
* `x402`, USD cents for `stripe`. Omitted for `cashu` (platform derives
|
|
2012
|
+
* `paidMsats / 1000`). Must be set together with `nativeAsset`.
|
|
1962
2013
|
*/
|
|
1963
|
-
|
|
2014
|
+
nativeAmount?: number;
|
|
1964
2015
|
/**
|
|
1965
|
-
*
|
|
1966
|
-
*
|
|
1967
|
-
*
|
|
2016
|
+
* Asset tag paired with `nativeAmount`: `"sats"`, `"usdc"`, `"usdc.e"`, or
|
|
2017
|
+
* `"usd-cents"`. Both fields must be present for the platform to write a non-cashu
|
|
2018
|
+
* revenue row.
|
|
1968
2019
|
*/
|
|
1969
|
-
|
|
2020
|
+
nativeAsset?: string;
|
|
1970
2021
|
/**
|
|
1971
|
-
*
|
|
1972
|
-
*
|
|
1973
|
-
*
|
|
1974
|
-
*
|
|
2022
|
+
* Cashu flow discriminator. Set to `"p2pk_accumulator"` when the cashu
|
|
2023
|
+
* accumulator path satisfied the job; omitted for legacy cashu. Written into
|
|
2024
|
+
* `revenue_events.metadata.cashu_flow` by the platform so ops can split per-call cashu
|
|
2025
|
+
* rows by source.
|
|
1975
2026
|
*/
|
|
1976
|
-
|
|
2027
|
+
cashuFlow?: string;
|
|
1977
2028
|
/**
|
|
1978
|
-
*
|
|
1979
|
-
*
|
|
2029
|
+
* What the job cost the **builder** to serve, as declared by the
|
|
2030
|
+
* handler through `ctx.cost()` — the other half of the
|
|
2031
|
+
* margin the dashboard states. `amountMicro` is 1e-6 of `currency`, which
|
|
2032
|
+
* is the currency the builder incurred the cost in and need not be the
|
|
2033
|
+
* credit's.
|
|
2034
|
+
*
|
|
2035
|
+
* Never netted against `paidMsats` or `drawAmountMicro`: what the caller
|
|
2036
|
+
* paid and what serving them cost are two questions, so they are two fields.
|
|
2037
|
+
*
|
|
2038
|
+
* **Absent means unreported, not zero.** A handler that declares nothing
|
|
2039
|
+
* sends no `cost` key at all, and the platform shows the margin as unknown
|
|
2040
|
+
* rather than as 100%. A handler that declares `{ amount: 0 }` is making the
|
|
2041
|
+
* opposite, positive claim and sends `amountMicro: 0`.
|
|
2042
|
+
*
|
|
2043
|
+
* Additive on the wire: a platform build that does not read this key keeps
|
|
2044
|
+
* accepting the report unchanged.
|
|
1980
2045
|
*/
|
|
1981
|
-
|
|
2046
|
+
cost?: {
|
|
2047
|
+
amountMicro: number;
|
|
2048
|
+
currency: string;
|
|
2049
|
+
};
|
|
1982
2050
|
/**
|
|
1983
|
-
*
|
|
1984
|
-
*
|
|
1985
|
-
*
|
|
1986
|
-
*
|
|
1987
|
-
* (`paid_job_death_report_failed`) and dropped, since the reaper's own
|
|
1988
|
-
* `stale_job_failed` log is the durable record and a failed *alert* must never
|
|
1989
|
-
* wedge the sweep.
|
|
2051
|
+
* Credit this job's payment drew against. Present on
|
|
2052
|
+
* every job a ledger-backed payment satisfied; its presence is what tells
|
|
2053
|
+
* the platform the row is a **draw-keyed** revenue event rather than a
|
|
2054
|
+
* pre-credits per-call one.
|
|
1990
2055
|
*/
|
|
1991
|
-
|
|
2056
|
+
creditId?: string;
|
|
2057
|
+
/** The draw this job settled — one draw, one revenue row. */
|
|
2058
|
+
drawId?: string;
|
|
2059
|
+
/** The draw's fiat amount, 1e-6 of `creditCurrency`. The exact liability offset. */
|
|
2060
|
+
drawAmountMicro?: number;
|
|
2061
|
+
/** Currency the credit is denominated in (lowercase ISO, e.g. `usd`). */
|
|
2062
|
+
creditCurrency?: string;
|
|
1992
2063
|
/**
|
|
1993
|
-
*
|
|
1994
|
-
*
|
|
1995
|
-
*
|
|
1996
|
-
* must never wedge terminal handling or the orphan-draw reconciler.
|
|
2064
|
+
* Settlement instant (epoch ms) — draws are revenue *when they
|
|
2065
|
+
* settle*, so this dates the row. Omitted for pre-credits rows, which the
|
|
2066
|
+
* platform dates at record time as before.
|
|
1997
2067
|
*/
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
reportTempoSettlementBalance(payload: TempoSettlementBalancePayload): Promise<void>;
|
|
2002
|
-
reportTempoSettlementFailure(payload: TempoSettlementFailurePayload): Promise<void>;
|
|
2003
|
-
private postTempoSettlementReadiness;
|
|
2004
|
-
startRetryLoop(): void;
|
|
2005
|
-
stop(): void;
|
|
2068
|
+
settledAt?: number;
|
|
2069
|
+
/** The funding's own rail reference, for reconciling a draw back to its deposit. */
|
|
2070
|
+
fundingRef?: string;
|
|
2006
2071
|
/**
|
|
2007
|
-
*
|
|
2008
|
-
*
|
|
2009
|
-
*
|
|
2010
|
-
* transient backlog apart from permanently-failed rows.
|
|
2072
|
+
* Revenue class when this row is not ordinary service revenue.
|
|
2073
|
+
* `short_pay_forfeit` marks money kept from an underpayment that bought no
|
|
2074
|
+
* job so analytics can exclude it.
|
|
2011
2075
|
*/
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2076
|
+
kind?: string;
|
|
2077
|
+
}
|
|
2078
|
+
/** Privacy-preserving terminal evidence; contains no caller or payment references. */
|
|
2079
|
+
interface JobTerminalReportPayload {
|
|
2080
|
+
dvmId: string;
|
|
2081
|
+
jobId: string;
|
|
2082
|
+
capability: string;
|
|
2083
|
+
outcome: "completed" | "failed" | "cancelled";
|
|
2084
|
+
endedBy: "caller" | "provider" | null;
|
|
2085
|
+
paid: boolean;
|
|
2086
|
+
acceptedAtMs: number;
|
|
2087
|
+
terminalAtMs: number;
|
|
2088
|
+
callerWaitMs: number;
|
|
2089
|
+
receiptSeq: number | null;
|
|
2090
|
+
}
|
|
2091
|
+
/**
|
|
2092
|
+
* JSON wire shape POSTed to the platform's `/_internal/job-cost` endpoint
|
|
2093
|
+
* when a terminal job incurred a declared builder cost but produced no
|
|
2094
|
+
* revenue report.
|
|
2095
|
+
*
|
|
2096
|
+
* Unlike {@link RevenueReportPayload}, this event has no payment rail or
|
|
2097
|
+
* revenue amount: a failed, cancelled, or free job is not a sale. The
|
|
2098
|
+
* platform upserts it by `(dvmId, jobId)`, independently of the revenue
|
|
2099
|
+
* ledger's rail-keyed constraint. It replaces the stored value only when
|
|
2100
|
+
* `costRevision` is higher; equal revisions must be byte-identical. That rule
|
|
2101
|
+
* makes a persisted prefix and a later in-memory total converge regardless of
|
|
2102
|
+
* delivery order.
|
|
2103
|
+
*/
|
|
2104
|
+
interface JobCostReportPayload {
|
|
2105
|
+
/** Platform DVM record ID (`dvms.id`); bound to the authenticating token. */
|
|
2106
|
+
dvmId: string;
|
|
2107
|
+
/** Job identifier and the report's platform-side idempotency key. */
|
|
2108
|
+
jobId: string;
|
|
2109
|
+
/** Capability selected by dispatch; absent only on legacy persisted jobs. */
|
|
2110
|
+
capability?: string;
|
|
2111
|
+
/** Terminal outcome that explains why this event carries no revenue. */
|
|
2112
|
+
outcome: "completed" | "failed" | "cancelled";
|
|
2113
|
+
/**
|
|
2114
|
+
* Number of declarations incorporated into `cost`; the
|
|
2115
|
+
* platform keeps the payload with the highest revision for this job.
|
|
2116
|
+
*/
|
|
2117
|
+
costRevision: number;
|
|
2017
2118
|
/**
|
|
2018
|
-
*
|
|
2019
|
-
*
|
|
2020
|
-
*
|
|
2021
|
-
* become terminal after 24 hours. Successful deliveries remove their rows.
|
|
2119
|
+
* The builder's cumulative declared cost, in millionths of its currency.
|
|
2120
|
+
* `null` at a higher revision invalidates an earlier prefix when the handler
|
|
2121
|
+
* switched currencies and the final total is therefore unreportable.
|
|
2022
2122
|
*/
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
private markStale;
|
|
2123
|
+
cost: {
|
|
2124
|
+
amountMicro: number;
|
|
2125
|
+
currency: string;
|
|
2126
|
+
} | null;
|
|
2028
2127
|
}
|
|
2029
|
-
|
|
2030
|
-
/** Query surface shared by the pool and an open Postgres transaction. */
|
|
2031
|
-
type X402ChannelQuerier = Pick<Pool, "query">;
|
|
2032
2128
|
/**
|
|
2033
|
-
*
|
|
2129
|
+
* JSON wire shape POSTed to the platform's `/_internal/credit-deposit`
|
|
2130
|
+
* endpoint. A funding event is a **deposit** — a liability until
|
|
2131
|
+
* drawn — reported separately and never summed into revenue (the deposit-accounting rule).
|
|
2034
2132
|
*
|
|
2035
|
-
* `
|
|
2036
|
-
*
|
|
2037
|
-
* queue without pretending it booked. It is deliberately reversible — a later
|
|
2038
|
-
* reconcile still accepts it, because it moved nothing.
|
|
2133
|
+
* Idempotent on `(dvmId, creditId, fundingId)`, so the shared retry loop can
|
|
2134
|
+
* redeliver freely.
|
|
2039
2135
|
*/
|
|
2040
|
-
|
|
2041
|
-
/**
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2136
|
+
interface CreditDepositPayload {
|
|
2137
|
+
/** Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
|
|
2138
|
+
dvmId: string;
|
|
2139
|
+
creditId: string;
|
|
2140
|
+
/** Canonical owner of the funded credit. */
|
|
2141
|
+
callerPubkey: string;
|
|
2142
|
+
/** The rail's settlement reference — the deposit's idempotency key. */
|
|
2143
|
+
fundingId: string;
|
|
2144
|
+
/** Payment rail (`"cashu"`, `"tempo"`, `"x402"`). */
|
|
2145
|
+
rail: string;
|
|
2146
|
+
/** Rail value received, in millisatoshis. */
|
|
2147
|
+
paidMsats: number;
|
|
2148
|
+
/** Cashu only. Full received proof face before its receiver input fee. */
|
|
2149
|
+
grossPaidMsats?: number;
|
|
2150
|
+
/** Cashu only. Standalone NUT-02 fee reserved by the received proofs. */
|
|
2151
|
+
cashuInputFeeMsats?: number;
|
|
2152
|
+
/** Rail-native atomic units; omitted for cashu (derived from `paidMsats`). */
|
|
2153
|
+
nativeAmount?: number;
|
|
2154
|
+
/** Asset tag paired with `nativeAmount`. */
|
|
2155
|
+
nativeAsset?: string;
|
|
2156
|
+
/** Cashu mint URL the funding proofs were issued from. */
|
|
2157
|
+
paymentMint?: string;
|
|
2158
|
+
/** Fiat micro credited to the ledger — the liability this deposit creates. */
|
|
2159
|
+
amountMicro: number;
|
|
2160
|
+
/** Currency `amountMicro` is 1e-6 of. */
|
|
2161
|
+
creditCurrency: string;
|
|
2162
|
+
/** When the funding committed (epoch ms). */
|
|
2163
|
+
fundedAt: number;
|
|
2164
|
+
/** Current credit expiry after this funding committed (epoch ms). */
|
|
2165
|
+
expiryMs: number;
|
|
2051
2166
|
/**
|
|
2052
|
-
*
|
|
2053
|
-
*
|
|
2054
|
-
*
|
|
2167
|
+
* Tempo session funding only: the TIP-1034 channel this deposit
|
|
2168
|
+
* arrived on, registered so the platform's close observer can route a
|
|
2169
|
+
* `CloseRequested` event back to the DVM that owns the channel.
|
|
2055
2170
|
*
|
|
2056
|
-
*
|
|
2057
|
-
*
|
|
2058
|
-
*
|
|
2059
|
-
*
|
|
2060
|
-
*
|
|
2061
|
-
*
|
|
2062
|
-
*
|
|
2171
|
+
* Rides the deposit report rather than a side channel on purpose — accepting
|
|
2172
|
+
* the voucher and booking the registration then share the one durable commit
|
|
2173
|
+
* boundary, so a registered channel can never be a channel whose funding
|
|
2174
|
+
* rolled back, and the existing retry drain delivers it after a crash. The
|
|
2175
|
+
* registration is therefore asynchronous relative to the close: an event can
|
|
2176
|
+
* legitimately arrive first, which is what the platform's payee fallback is
|
|
2177
|
+
* for.
|
|
2063
2178
|
*/
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
/**
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2179
|
+
tempoChannel?: {
|
|
2180
|
+
chainId: number;
|
|
2181
|
+
/** 32-byte channel id, `0x`-prefixed and lowercased. */
|
|
2182
|
+
channelId: string;
|
|
2183
|
+
/** TIP-1034 escrow the channel was opened against. */
|
|
2184
|
+
escrow: string;
|
|
2185
|
+
/** The DVM's on-chain recipient — the fallback routing key. */
|
|
2186
|
+
payee?: string;
|
|
2187
|
+
};
|
|
2188
|
+
/**
|
|
2189
|
+
* Tempo session funding only: which channel this deposit's voucher
|
|
2190
|
+
* was accepted on, emitted **whenever the funding was session-based** —
|
|
2191
|
+
* independently of whether {@link CreditDepositPayload.tempoChannel} could be
|
|
2192
|
+
* read. That independence is the entire point: `rail: "tempo"` covers both
|
|
2193
|
+
* `tempo/session` and the `tempo/charge` one-shot, and a charge legitimately
|
|
2194
|
+
* carries no channel at all, so without this the platform cannot tell a
|
|
2195
|
+
* routine charge deposit from a session whose chain binding was lost — the
|
|
2196
|
+
* shape, where the close observer silently never watches the escrow.
|
|
2197
|
+
*
|
|
2198
|
+
* Carries only the channel id, not the chain binding: a DVM that could read
|
|
2199
|
+
* the binding sends `tempoChannel` too, and one that could not still names
|
|
2200
|
+
* the channel an operator has to go and look at.
|
|
2201
|
+
*/
|
|
2202
|
+
tempoSession?: {
|
|
2203
|
+
/** 32-byte channel id, `0x`-prefixed and lowercased. */
|
|
2204
|
+
channelId: string;
|
|
2205
|
+
};
|
|
2206
|
+
/**
|
|
2207
|
+
* Set by the blocked-invoice repair: this deposit is a paid
|
|
2208
|
+
* Lightning invoice the builder's agent reconciled onto a credit. The money
|
|
2209
|
+
* landed in the receive wallet at payment time and this deposit is the one
|
|
2210
|
+
* row it gets, so the settlements table types it `repair` off this flag
|
|
2211
|
+
* rather than off a second row.
|
|
2212
|
+
*/
|
|
2213
|
+
repaired?: boolean;
|
|
2077
2214
|
}
|
|
2078
|
-
/** The caller-supplied half of a settlement row — everything the store does not stamp itself. */
|
|
2079
|
-
type X402SettlementIdentity = Omit<X402SettlementIntent, "status" | "response" | "createdAt" | "updatedAt" | "writeOffNote" | "resolvedAt" | "channelAtSubmission">;
|
|
2080
2215
|
/**
|
|
2081
|
-
*
|
|
2216
|
+
* JSON wire shape POSTed to the platform's `/_internal/credit-draw-release`
|
|
2217
|
+
* endpoint. A released hold leaves the credit balance intact, but
|
|
2218
|
+
* marks that slice of the outstanding liability as failure churn rather than
|
|
2219
|
+
* untouched prefunding.
|
|
2082
2220
|
*
|
|
2083
|
-
*
|
|
2084
|
-
*
|
|
2085
|
-
* resolution and `Date` has millisecond, so a cursor that round-tripped through
|
|
2086
|
-
* epoch ms would land *before* the row it was taken from and re-serve it on
|
|
2087
|
-
* every page, which at `limit: 1` never advances at all.
|
|
2221
|
+
* Idempotent on `(dvmId, creditId, drawId)`, so the reporter queue can
|
|
2222
|
+
* redeliver freely.
|
|
2088
2223
|
*/
|
|
2089
|
-
interface
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
/**
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2224
|
+
interface CreditDrawReleasePayload {
|
|
2225
|
+
/** Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
|
|
2226
|
+
dvmId: string;
|
|
2227
|
+
creditId: string;
|
|
2228
|
+
/** Released draw; the platform idempotency key with DVM and credit. */
|
|
2229
|
+
drawId: string;
|
|
2230
|
+
/** Fiat micro restored to availability. */
|
|
2231
|
+
amountMicro: number;
|
|
2232
|
+
/** Currency `amountMicro` is 1e-6 of. */
|
|
2233
|
+
creditCurrency: string;
|
|
2234
|
+
/** Per-credit gap-free sequence assigned when the draw was placed. */
|
|
2235
|
+
ledgerSeq: number;
|
|
2236
|
+
/** When the hold reached `released` (epoch ms). */
|
|
2237
|
+
releasedAt: number;
|
|
2098
2238
|
}
|
|
2239
|
+
type CreditDrainRepairKind = "x402_settlement_reconciled" | "tempo_close_reconciled";
|
|
2099
2240
|
/**
|
|
2100
|
-
*
|
|
2101
|
-
*
|
|
2102
|
-
*
|
|
2103
|
-
* relay asks for one thing — exclusive use of its gas EOA for the length of one
|
|
2104
|
-
* submission — and must not acquire the ability to touch a channel or a
|
|
2105
|
-
* settlement row. Handing it this instead of the host `Pool` is what keeps the
|
|
2106
|
-
* acquire off the pool the channel transaction is already holding a client from.
|
|
2241
|
+
* Terminal picked_up/sent drains report reclaimed fiat micro-units, reducing
|
|
2242
|
+
* outstanding liability rather than adding revenue. Requested or parked drains
|
|
2243
|
+
* have not paid the caller. Idempotency key: (dvmId, creditId, drainId).
|
|
2107
2244
|
*/
|
|
2108
|
-
interface
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
/**
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2245
|
+
interface CreditDrainPayload {
|
|
2246
|
+
/** Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
|
|
2247
|
+
dvmId: string;
|
|
2248
|
+
creditId: string;
|
|
2249
|
+
/** Canonical owner of the reclaimed credit. */
|
|
2250
|
+
callerPubkey: string;
|
|
2251
|
+
/** The drain's own idempotency key. */
|
|
2252
|
+
drainId: string;
|
|
2253
|
+
/** Payout rail (`"cashu"`, `"x402"`, `"tempo"`). */
|
|
2254
|
+
method: string;
|
|
2255
|
+
/** Fiat micro reclaimed — the liability this drain extinguishes. */
|
|
2256
|
+
amountMicro: number;
|
|
2257
|
+
/** Currency `amountMicro` is 1e-6 of. */
|
|
2258
|
+
creditCurrency: string;
|
|
2259
|
+
/** Terminal state: `"picked_up"` for cashu, `"sent"` for other rails. */
|
|
2260
|
+
status: string;
|
|
2261
|
+
/** When the money left (epoch ms). */
|
|
2262
|
+
drainedAt: number;
|
|
2263
|
+
/** Per-credit gap-free ledger sequence, for reconciling against the DVM's ledger. */
|
|
2264
|
+
ledgerSeq?: number;
|
|
2265
|
+
/** Set when an operator repair verb completed this reclaim. */
|
|
2266
|
+
repaired?: boolean;
|
|
2267
|
+
/** Required iff `repaired`; identifies the repair path that completed the reclaim. */
|
|
2268
|
+
repairKind?: CreditDrainRepairKind;
|
|
2130
2269
|
}
|
|
2131
|
-
/**
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
/** Seconds since the holder's current statement started. */
|
|
2138
|
-
heldSeconds?: number;
|
|
2270
|
+
/**
|
|
2271
|
+
* Open Postgres transaction handle accepted by the reporter-owned outbox
|
|
2272
|
+
* enqueue. The payment rail owns BEGIN/COMMIT; the reporter owns its table.
|
|
2273
|
+
*/
|
|
2274
|
+
interface RevenueReporterQuerier {
|
|
2275
|
+
query: Pool["query"];
|
|
2139
2276
|
}
|
|
2140
2277
|
/**
|
|
2141
|
-
*
|
|
2278
|
+
* Transactional deposit-outbox seam threaded through each rail commit.
|
|
2279
|
+
*
|
|
2280
|
+
* Implemented by {@link RevenueReporter.enqueueDeposit}; callers must await it
|
|
2281
|
+
* before committing the rail receive and ledger funding.
|
|
2282
|
+
*/
|
|
2283
|
+
type CreditDepositEnqueue = (tx: RevenueReporterQuerier, payload: CreditDepositPayload) => Promise<void>;
|
|
2284
|
+
/** Transactional draw-release outbox seam used by the credit ledger. */
|
|
2285
|
+
type CreditDrawReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditDrawReleasePayload) => Promise<void>;
|
|
2286
|
+
/**
|
|
2287
|
+
* Transactional drain-outbox seam threaded through each terminal drain
|
|
2288
|
+
* transition.
|
|
2289
|
+
*
|
|
2290
|
+
* Implemented by {@link RevenueReporter.enqueueCreditDrain}; callers must await
|
|
2291
|
+
* it before committing the status CAS that moved the money.
|
|
2292
|
+
*/
|
|
2293
|
+
type CreditDrainEnqueue = (tx: RevenueReporterQuerier, payload: CreditDrainPayload) => Promise<void>;
|
|
2294
|
+
/**
|
|
2295
|
+
* JSON wire shape POSTed to the platform's `/_internal/credit-expiry-release`
|
|
2296
|
+
* endpoint. A prepaid credit that reaches its TTL with an undrawn
|
|
2297
|
+
* remainder RELEASES that remainder to the builder: on cashu, Lightning and
|
|
2298
|
+
* x402 the funding value reached builder custody at funding time or on the
|
|
2299
|
+
* settlement cycle, so expiry extinguishes the caller's remaining claim.
|
|
2300
|
+
* Reported so committed value nets to
|
|
2301
|
+
* `deposited − drawn − drained − released`; without it an expired credit is
|
|
2302
|
+
* owed forever on paper, exactly like an unreported drain.
|
|
2142
2303
|
*
|
|
2143
|
-
*
|
|
2144
|
-
*
|
|
2145
|
-
*
|
|
2146
|
-
*
|
|
2147
|
-
*
|
|
2148
|
-
*
|
|
2149
|
-
*
|
|
2304
|
+
* Never sent for a Tempo credit. Tempo balances are channel-backed and undrawn
|
|
2305
|
+
* channel value returns to the *caller* at exit, so no release exists on that
|
|
2306
|
+
* rail — the sweep skips them and the platform refuses one outright.
|
|
2307
|
+
*
|
|
2308
|
+
* Two reports share this shape, distinguished only by `reversedAt`:
|
|
2309
|
+
*
|
|
2310
|
+
* - **The release**, written by the expiry sweep with `reversedAt` absent.
|
|
2311
|
+
* - **The reversal**, written when a later funding revives the credit
|
|
2312
|
+
* (`CreditLedger.fund` overwrites `expiry_ms`, so the recorded remainder is
|
|
2313
|
+
* no longer final). It repeats the whole payload rather than sending a
|
|
2314
|
+
* reference, because the retry loop can deliver the two out of order and the
|
|
2315
|
+
* platform's upsert must be able to land either one first.
|
|
2316
|
+
*
|
|
2317
|
+
* Idempotent on `(dvmId, creditId, releaseId)`, so the shared retry loop can
|
|
2318
|
+
* redeliver freely.
|
|
2150
2319
|
*/
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
/**
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2320
|
+
interface CreditExpiryReleasePayload {
|
|
2321
|
+
/** Platform DVM record ID (`dvms.id`); must match the bearer token's DVM. */
|
|
2322
|
+
dvmId: string;
|
|
2323
|
+
creditId: string;
|
|
2324
|
+
/** Per-expiry-episode key; the platform idempotency key with DVM and credit. */
|
|
2325
|
+
releaseId: string;
|
|
2326
|
+
callerPubkey: string;
|
|
2327
|
+
/** Funding rail. `null` on a credit funded before the basis became mandatory. */
|
|
2328
|
+
rail: string | null;
|
|
2329
|
+
/** Fiat micro released — the liability this expiry extinguishes. */
|
|
2330
|
+
amountMicro: number;
|
|
2331
|
+
/** Currency `amountMicro` is 1e-6 of. */
|
|
2332
|
+
creditCurrency: string;
|
|
2333
|
+
/** When the credit's TTL ran out (epoch ms) — when the liability ended. */
|
|
2334
|
+
expiryMs: number;
|
|
2335
|
+
/** When the sweep observed the expiry (epoch ms). */
|
|
2336
|
+
releasedAt: number;
|
|
2337
|
+
/** Set only on the reversal report: when the revival funding landed (epoch ms). */
|
|
2338
|
+
reversedAt?: number | null;
|
|
2170
2339
|
}
|
|
2171
2340
|
/**
|
|
2172
|
-
*
|
|
2341
|
+
* Transactional expiry-release outbox seam, threaded through the sweep's insert
|
|
2342
|
+
* and through the funding that reverses a release.
|
|
2173
2343
|
*
|
|
2174
|
-
*
|
|
2175
|
-
*
|
|
2176
|
-
*
|
|
2344
|
+
* Implemented by {@link RevenueReporter.enqueueCreditExpiryRelease}; callers
|
|
2345
|
+
* must await it before committing the write that made the release (or its
|
|
2346
|
+
* reversal) true, for the same reason the drain seam does — a report lost
|
|
2347
|
+
* between the two overstates or understates committed value forever.
|
|
2177
2348
|
*/
|
|
2178
|
-
|
|
2179
|
-
|
|
2349
|
+
type CreditExpiryReleaseEnqueue = (tx: RevenueReporterQuerier, payload: CreditExpiryReleasePayload) => Promise<void>;
|
|
2350
|
+
/**
|
|
2351
|
+
* JSON wire shape POSTed to the platform's `/_internal/paid-job-death` endpoint
|
|
2352
|
+
* when the stale-job reaper force-fails a *paid* job — its pending
|
|
2353
|
+
* credit draw is released rather than settled, so the caller's payment is back
|
|
2354
|
+
* on their credit balance and reclaimable by drain. The platform resolves fiat
|
|
2355
|
+
* + owner and sends an ambient notice via the shared alert sink. Unlike
|
|
2356
|
+
* {@link RevenueReportPayload} this carries no durable-retry semantics: the
|
|
2357
|
+
* reaper's `stale_job_failed` structured log is the durable backstop, so a lost
|
|
2358
|
+
* report degrades to log-only rather than blocking the sweep.
|
|
2359
|
+
*/
|
|
2360
|
+
interface PaidJobDeathPayload {
|
|
2361
|
+
/** Platform DVM record ID (`dvms.id`). Bound to the authenticating token. */
|
|
2362
|
+
dvmId: string;
|
|
2363
|
+
jobId: string;
|
|
2364
|
+
capability: string;
|
|
2365
|
+
/** Amount of the released credit draw in millisatoshis. */
|
|
2366
|
+
paidMsats: number;
|
|
2367
|
+
/** Payment rail of the released draw (`"cashu"`, `"tempo"`, `"x402"`). */
|
|
2368
|
+
rail: string;
|
|
2369
|
+
/** Reaper reason: dead worker or never-terminal idle job. */
|
|
2370
|
+
reason: string;
|
|
2371
|
+
/** Rail-native released amount, paired with `nativeAsset`. */
|
|
2372
|
+
nativeAmount?: number;
|
|
2373
|
+
/** Asset tag for `nativeAmount` (`"sats"` / `"usdc"` / …). */
|
|
2374
|
+
nativeAsset?: string;
|
|
2375
|
+
/** Cashu mint URL the credit draw's tokens were issued from. */
|
|
2376
|
+
paymentMint?: string;
|
|
2180
2377
|
}
|
|
2378
|
+
type RevenueSkippedNoRailReason = "credit_rail_null" | "credit_rail_unrecognized" | "job_rail_missing" | "ledger_not_configured";
|
|
2181
2379
|
/**
|
|
2182
|
-
*
|
|
2183
|
-
*
|
|
2184
|
-
*
|
|
2185
|
-
*
|
|
2186
|
-
*
|
|
2187
|
-
* transaction to the credit ledger: the fixed lock order is channel first,
|
|
2188
|
-
* credit second, and no credit-ledger path calls back into this store.
|
|
2189
|
-
*
|
|
2190
|
-
* Two pools, and the split is load-bearing. The settlement markers
|
|
2191
|
-
* must write *outside* the ledger transaction, which means a second connection
|
|
2192
|
-
* taken while the transaction's client is still checked out — on the shared
|
|
2193
|
-
* host pool that is a self-deadlock the moment concurrent deposits reach its
|
|
2194
|
-
* `max`, with no error and no timeout because every waiter is also a holder.
|
|
2195
|
-
* They therefore run on a small dedicated pool that acquires nothing itself,
|
|
2196
|
-
* so nothing can ever queue behind them. The rule this rests on: a client held
|
|
2197
|
-
* from one pool may only ever be used to acquire from a pool that is terminal.
|
|
2198
|
-
* {@link withFleetSettlementLock} and {@link withRelaySubmissionLock} are the
|
|
2199
|
-
* other two nesting sites and are bounded a different way — see their docs.
|
|
2380
|
+
* JSON wire shape POSTed to the platform's `/_internal/revenue-skipped-no-rail`
|
|
2381
|
+
* endpoint when a settled credit draw has no usable payment rail
|
|
2382
|
+
* for its revenue event. This is an operator notice rather than a durable
|
|
2383
|
+
* accounting report: the SDK's structured log remains the durable backstop,
|
|
2384
|
+
* and a failed alert must never block terminal handling or reconciliation.
|
|
2200
2385
|
*/
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
/**
|
|
2226
|
-
|
|
2227
|
-
/**
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2386
|
+
interface RevenueSkippedNoRailPayload {
|
|
2387
|
+
/** Platform DVM record ID (`dvms.id`). Bound to the authenticating token. */
|
|
2388
|
+
dvmId: string;
|
|
2389
|
+
jobId: string;
|
|
2390
|
+
creditId: string;
|
|
2391
|
+
drawId: string;
|
|
2392
|
+
/** Caller payment captured for the job, in millisatoshis. */
|
|
2393
|
+
paidMsats: number;
|
|
2394
|
+
/** Settled draw amount in 1e-6 of `creditCurrency`. */
|
|
2395
|
+
drawAmountMicro?: number;
|
|
2396
|
+
/** Currency the draw amount is denominated in. */
|
|
2397
|
+
creditCurrency?: string;
|
|
2398
|
+
/** Credit funding rail as stored on the draw, or null for legacy rows. */
|
|
2399
|
+
creditRail: string | null;
|
|
2400
|
+
reason: RevenueSkippedNoRailReason;
|
|
2401
|
+
}
|
|
2402
|
+
/** Gas-balance observation POSTed by a platform-hosted x402 self-relay. */
|
|
2403
|
+
interface X402SelfRelayGasBalancePayload {
|
|
2404
|
+
/** Platform DVM record ID, bound to the authenticating platform token. */
|
|
2405
|
+
dvmId: string;
|
|
2406
|
+
/** Dedicated relay EOA whose Base ETH balance was read. */
|
|
2407
|
+
address: `0x${string}`;
|
|
2408
|
+
/** CAIP-2 Base network identifier. */
|
|
2409
|
+
network: string;
|
|
2410
|
+
/** Current native balance as a decimal wei string. */
|
|
2411
|
+
balanceWei: string;
|
|
2412
|
+
/** Configured paging floor as a decimal wei string. */
|
|
2413
|
+
lowBalanceWei: string;
|
|
2414
|
+
/** Millisecond timestamp at which the DVM read the balance. */
|
|
2415
|
+
checkedAt: number;
|
|
2416
|
+
}
|
|
2417
|
+
/** RPC-health transition POSTed by a platform-hosted x402 self-relay. */
|
|
2418
|
+
type X402SelfRelayRpcHealthPayload = {
|
|
2419
|
+
/** Platform DVM record ID, bound to the authenticating platform token. */
|
|
2420
|
+
dvmId: string;
|
|
2421
|
+
/** Dedicated relay EOA identifying the affected self-relay. */
|
|
2422
|
+
address: `0x${string}`;
|
|
2423
|
+
/** CAIP-2 Base network identifier. */
|
|
2424
|
+
network: string;
|
|
2425
|
+
/** Healthy transition clears an open RPC page. */
|
|
2426
|
+
status: "healthy";
|
|
2427
|
+
/** Millisecond timestamp at which the DVM observed the transition. */
|
|
2428
|
+
checkedAt: number;
|
|
2429
|
+
} | {
|
|
2430
|
+
/** Platform DVM record ID, bound to the authenticating platform token. */
|
|
2431
|
+
dvmId: string;
|
|
2432
|
+
/** Dedicated relay EOA identifying the affected self-relay. */
|
|
2433
|
+
address: `0x${string}`;
|
|
2434
|
+
/** CAIP-2 Base network identifier. */
|
|
2435
|
+
network: string;
|
|
2436
|
+
/** Failed transition opens or refreshes an RPC page. */
|
|
2437
|
+
status: "failed";
|
|
2438
|
+
/** Redacted failure class; never the provider error or endpoint URL. */
|
|
2439
|
+
reason: X402SelfRelayRpcFailureReason;
|
|
2440
|
+
/** Millisecond timestamp at which the DVM observed the transition. */
|
|
2441
|
+
checkedAt: number;
|
|
2442
|
+
};
|
|
2443
|
+
/** Configured Tempo operator fee-token balance POSTed by a hosted DVM. */
|
|
2444
|
+
interface TempoSettlementBalancePayload {
|
|
2445
|
+
dvmId: string;
|
|
2446
|
+
address: `0x${string}`;
|
|
2447
|
+
network: string;
|
|
2448
|
+
token: `0x${string}`;
|
|
2449
|
+
balanceMicro: string;
|
|
2450
|
+
lowBalanceMicro: string;
|
|
2451
|
+
checkedAt: number;
|
|
2452
|
+
ready: boolean;
|
|
2453
|
+
}
|
|
2454
|
+
/** Redacted Tempo operator fee-funding rejection POSTed by a hosted DVM. */
|
|
2455
|
+
interface TempoSettlementFailurePayload {
|
|
2456
|
+
dvmId: string;
|
|
2457
|
+
address: `0x${string}`;
|
|
2458
|
+
network: string;
|
|
2459
|
+
token: `0x${string}`;
|
|
2460
|
+
operation: "settle" | "close";
|
|
2461
|
+
errorClass: "InsufficientFundsError";
|
|
2462
|
+
checkedAt: number;
|
|
2463
|
+
balanceMicro?: string;
|
|
2464
|
+
channelId?: string;
|
|
2465
|
+
creditId?: string;
|
|
2466
|
+
drainId?: string;
|
|
2467
|
+
}
|
|
2468
|
+
/**
|
|
2469
|
+
* Durable revenue reporter for container-runtime DVMs.
|
|
2470
|
+
*
|
|
2471
|
+
* On job completion the SDK calls `report()`, which persists the payload to
|
|
2472
|
+
* a local Postgres table and attempts an immediate POST to the platform.
|
|
2473
|
+
* A background retry loop picks up un-acked rows with exponential backoff.
|
|
2474
|
+
* Gauge and health notifications are best-effort: failed delivery is logged
|
|
2475
|
+
* and dropped rather than queued for retry.
|
|
2476
|
+
*/
|
|
2477
|
+
declare class RevenueReporter {
|
|
2478
|
+
private readonly db;
|
|
2479
|
+
private readonly platformUrl;
|
|
2480
|
+
private readonly platformToken;
|
|
2481
|
+
private readonly hostedObligations?;
|
|
2482
|
+
private retryTimer;
|
|
2483
|
+
private static readonly RETRY_INTERVAL_MS;
|
|
2484
|
+
private static readonly MAX_BACKOFF_MS;
|
|
2485
|
+
private static readonly ABANDON_AFTER_MS;
|
|
2486
|
+
constructor(db: Pool, platformUrl: string, platformToken: string, hostedObligations?: HostedSettlementReportObligations | undefined);
|
|
2487
|
+
/** Create the pending-reports table and migrate the legacy SDK outbox (idempotent). */
|
|
2488
|
+
init(): Promise<void>;
|
|
2489
|
+
private createTables;
|
|
2490
|
+
private migrateLegacySdkOutbox;
|
|
2491
|
+
private assertSdkTableShape;
|
|
2492
|
+
private assertSdkColumns;
|
|
2493
|
+
private tableColumns;
|
|
2494
|
+
/** Persist a revenue report and attempt immediate delivery. */
|
|
2495
|
+
report(payload: RevenueReportPayload): Promise<void>;
|
|
2496
|
+
/** The permanent deduplication claim and outbox insert commit together.
|
|
2497
|
+
* The claim survives successful delivery, including for custom job stores.
|
|
2237
2498
|
*/
|
|
2238
|
-
|
|
2499
|
+
reportTerminal(payload: JobTerminalReportPayload): Promise<void>;
|
|
2500
|
+
/** Persist a zero-revenue job cost and attempt immediate delivery. */
|
|
2501
|
+
reportCost(payload: JobCostReportPayload): Promise<void>;
|
|
2239
2502
|
/**
|
|
2240
|
-
*
|
|
2241
|
-
* is one — the credit ledger's spend gate.
|
|
2242
|
-
*
|
|
2243
|
-
* `statuses` is the caller's, because the two gates disagree by design: a
|
|
2244
|
-
* draw is blocked by {@link X402_SPEND_BLOCKING_SETTLEMENT_STATUSES}, a fresh
|
|
2245
|
-
* drain only by {@link X402_WEDGED_SETTLEMENT_STATUSES}.
|
|
2246
|
-
*
|
|
2247
|
-
* On the dedicated settlement pool, and that is what makes the gate callable
|
|
2248
|
-
* at all: a draw routinely runs inside an open channel transaction holding a
|
|
2249
|
-
* host-pool client, and taking a second one from that pool is the
|
|
2250
|
-
* self-deadlock. The oldest blocking row wins, so a channel that somehow
|
|
2251
|
-
* accumulated two names the one an operator should reach for first.
|
|
2503
|
+
* Join a credit deposit to the caller-owned rail transaction.
|
|
2252
2504
|
*
|
|
2253
|
-
*
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
2256
|
-
* draw on that channel down with it — when the row is exactly the kind that
|
|
2257
|
-
* ought to block them.
|
|
2258
|
-
*/
|
|
2259
|
-
pendingRefundSettlement(channelId: string, statuses: X402SettlementStatus[]): Promise<X402UnresolvedRefund | undefined>;
|
|
2260
|
-
/**
|
|
2261
|
-
* Read one settlement by its own identifier — the handle an operator holds
|
|
2262
|
-
* Same dedicated pool as {@link getSettlement}, for the same
|
|
2263
|
-
* reason: the repair reads this while a channel transaction may be open.
|
|
2505
|
+
* This method performs no network I/O: it only writes the reporter-owned
|
|
2506
|
+
* durable queue row through `tx`. The retry drain delivers the row after the
|
|
2507
|
+
* rail transaction commits, including after a process restart.
|
|
2264
2508
|
*/
|
|
2265
|
-
|
|
2266
|
-
/**
|
|
2267
|
-
|
|
2509
|
+
enqueueDeposit(tx: RevenueReporterQuerier, payload: CreditDepositPayload): Promise<void>;
|
|
2510
|
+
/** Join a released draw to the transaction that made the hold terminal. */
|
|
2511
|
+
enqueueCreditDrawRelease(tx: RevenueReporterQuerier, payload: CreditDrawReleasePayload): Promise<void>;
|
|
2268
2512
|
/**
|
|
2269
|
-
*
|
|
2270
|
-
*
|
|
2271
|
-
*
|
|
2272
|
-
* Keyset-paginated on `(created_at, settlement_id)` for the reason the
|
|
2273
|
-
* blocked-invoice queue is: a wedged row never self-clears, so a fixed first
|
|
2274
|
-
* page of rows the operator has declined to act on would starve everything
|
|
2275
|
-
* behind it on every run, forever.
|
|
2276
|
-
*
|
|
2277
|
-
* The keyset is on `created_at`, which never moves, while `updatedBeforeMs`
|
|
2278
|
-
* filters on `updated_at`, which does — so a row an operator touches mid-walk
|
|
2279
|
-
* can leave the queue but can never jump the cursor and be skipped. The cursor
|
|
2280
|
-
* itself is Postgres's text rendering of that timestamp, handed back verbatim;
|
|
2281
|
-
* see {@link X402SettlementCursor} for why a `Date` cannot carry it.
|
|
2282
|
-
*
|
|
2283
|
-
* `updatedBeforeMs` is the staleness floor. A healthy settlement passes
|
|
2284
|
-
* through `submitting` and `settled` inside one request, so anything younger
|
|
2285
|
-
* than the floor is in flight rather than wedged and must not be offered as
|
|
2286
|
-
* repairable work.
|
|
2513
|
+
* Await durable queue insertion through tx so the terminal drain and its
|
|
2514
|
+
* report commit or roll back together; no network I/O here.
|
|
2287
2515
|
*/
|
|
2288
|
-
|
|
2289
|
-
statuses: X402SettlementStatus[];
|
|
2290
|
-
updatedBeforeMs: number;
|
|
2291
|
-
limit: number;
|
|
2292
|
-
after?: X402SettlementCursor;
|
|
2293
|
-
}): Promise<X402SettlementPage>;
|
|
2516
|
+
enqueueCreditDrain(tx: RevenueReporterQuerier, payload: CreditDrainPayload): Promise<void>;
|
|
2294
2517
|
/**
|
|
2295
|
-
*
|
|
2296
|
-
*
|
|
2297
|
-
*
|
|
2298
|
-
*
|
|
2299
|
-
*
|
|
2300
|
-
*
|
|
2301
|
-
*
|
|
2302
|
-
* the
|
|
2303
|
-
*
|
|
2304
|
-
*
|
|
2305
|
-
* {@link recordSettlementResponse}, whose `status <> 'complete'` CAS lifts a
|
|
2306
|
-
* written-off row back to `settled`.
|
|
2518
|
+
* Join a payout to the transaction that commits the movement it reports
|
|
2519
|
+
* the batch close, the accumulator mark, the drain booking.
|
|
2520
|
+
* No network I/O, only the durable queue row through `tx`, the discipline
|
|
2521
|
+
* {@link enqueueCreditDrain} set and for the same reason: the money has
|
|
2522
|
+
* already moved on a rail this ledger does not own, so the platform row is
|
|
2523
|
+
* the only record the builder's dashboard has of it, and a row lost between
|
|
2524
|
+
* the fact and the queue would understate "paid out" forever. Without `tx`
|
|
2525
|
+
* the row is queued on the pool, for callers with no fact of their own to
|
|
2526
|
+
* commit. Idempotent on `(dvmId, payoutId)` platform-side, so the retry
|
|
2527
|
+
* loop redelivers freely.
|
|
2307
2528
|
*/
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
prepareSettlement(intent: X402SettlementIdentity): Promise<X402SettlementIntent>;
|
|
2529
|
+
enqueuePayout(payload: PayoutReportPayload, tx?: RevenueReporterQuerier): Promise<void>;
|
|
2530
|
+
enqueueHostedPayout(payload: PayoutReportPayload, obligationIds: string[], tx?: RevenueReporterQuerier): Promise<void>;
|
|
2311
2531
|
/**
|
|
2312
|
-
*
|
|
2313
|
-
*
|
|
2314
|
-
*
|
|
2315
|
-
* and sign the settlement against — bookmarked here, with the
|
|
2316
|
-
* block height, because these two are the only description of that instant
|
|
2317
|
-
* that survives the call.
|
|
2532
|
+
* Report one landed payout on its own and try to deliver it at once — the
|
|
2533
|
+
* live Tempo settlement, which has no dvmkit transaction to join. Durable
|
|
2534
|
+
* like {@link report}.
|
|
2318
2535
|
*/
|
|
2319
|
-
|
|
2320
|
-
/** Return an explicitly failed facilitator submission to the safe retry phase. */
|
|
2321
|
-
markSettlementRetryable(settlementId: string): Promise<void>;
|
|
2536
|
+
reportPayout(payload: PayoutReportPayload): Promise<void>;
|
|
2322
2537
|
/**
|
|
2323
|
-
*
|
|
2324
|
-
*
|
|
2325
|
-
*
|
|
2326
|
-
*
|
|
2327
|
-
* `written_off` row back to `settled` (which is what makes the write-off
|
|
2328
|
-
* reversible), and a row that is no longer written off must not still be
|
|
2329
|
-
* carrying the note and resolution timestamp of a decision that has been
|
|
2330
|
-
* reversed. On the live path the columns are already `NULL` and this is a
|
|
2331
|
-
* no-op.
|
|
2538
|
+
* Report one rail's pending-pool snapshot. Best-effort and
|
|
2539
|
+
* fire-and-forget, deliberately unlike {@link reportPayout}: a snapshot is
|
|
2540
|
+
* replaced by the next tick, so queueing a stale one behind an outage would
|
|
2541
|
+
* only deliver figures the platform already has newer ones for.
|
|
2332
2542
|
*/
|
|
2333
|
-
|
|
2543
|
+
reportPayoutPending(payload: PayoutPendingPayload): Promise<void>;
|
|
2334
2544
|
/**
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2337
|
-
* `resolvedAtMs` is set only by the operator repair, so an audit
|
|
2338
|
-
* read can tell a settlement that completed on its own from one a human had
|
|
2339
|
-
* to finish.
|
|
2545
|
+
* Await durable queue insertion through tx so expiry release or reversal and
|
|
2546
|
+
* its report commit together. A missing reversal would overstate builder value.
|
|
2340
2547
|
*/
|
|
2341
|
-
|
|
2342
|
-
get(channelId: string): Promise<Channel | undefined>;
|
|
2343
|
-
/** List all channel snapshots in stable channel-id order. */
|
|
2344
|
-
list(): Promise<Channel[]>;
|
|
2345
|
-
/** Atomic backend-level read/modify/write for one channel. */
|
|
2346
|
-
updateChannel(channelId: string, update: (current: Channel | undefined) => Channel | undefined): Promise<ChannelUpdateResult>;
|
|
2548
|
+
enqueueCreditExpiryRelease(tx: RevenueReporterQuerier, payload: CreditExpiryReleasePayload): Promise<void>;
|
|
2347
2549
|
/**
|
|
2348
|
-
*
|
|
2349
|
-
*
|
|
2350
|
-
*
|
|
2351
|
-
*
|
|
2352
|
-
*
|
|
2353
|
-
*
|
|
2354
|
-
*
|
|
2550
|
+
* Report a reaper-force-failed paid job to the platform so the
|
|
2551
|
+
* operator receives an ambient notice that a DVM died or wedged mid-job.
|
|
2552
|
+
* Best-effort and fire-and-forget: unlike {@link report} there is no local
|
|
2553
|
+
* durable queue — a non-2xx or transport failure is structured-logged
|
|
2554
|
+
* (`paid_job_death_report_failed`) and dropped, since the reaper's own
|
|
2555
|
+
* `stale_job_failed` log is the durable record and a failed *alert* must never
|
|
2556
|
+
* wedge the sweep.
|
|
2355
2557
|
*/
|
|
2558
|
+
reportPaidJobDeath(payload: PaidJobDeathPayload): Promise<void>;
|
|
2356
2559
|
/**
|
|
2357
|
-
*
|
|
2358
|
-
*
|
|
2359
|
-
*
|
|
2360
|
-
*
|
|
2361
|
-
* payer's verified refund voucher authorises". Outside this scope a decrease
|
|
2362
|
-
* of `chargedCumulativeAmount` is refused flat, which is what keeps the
|
|
2363
|
-
* arbitrary-decrease guard meaningful.
|
|
2560
|
+
* Report a settled draw whose revenue event was skipped.
|
|
2561
|
+
* Best-effort and fire-and-forget: the SDK's structured
|
|
2562
|
+
* `revenue_skipped_no_rail` log is the durable backstop, so a failed notice
|
|
2563
|
+
* must never wedge terminal handling or the orphan-draw reconciler.
|
|
2364
2564
|
*/
|
|
2365
|
-
|
|
2366
|
-
|
|
2565
|
+
reportRevenueSkippedNoRail(payload: RevenueSkippedNoRailPayload): Promise<void>;
|
|
2566
|
+
reportX402SelfRelayGasBalance(payload: X402SelfRelayGasBalancePayload): Promise<void>;
|
|
2567
|
+
reportX402SelfRelayRpcHealth(payload: X402SelfRelayRpcHealthPayload): Promise<void>;
|
|
2568
|
+
reportTempoSettlementBalance(payload: TempoSettlementBalancePayload): Promise<void>;
|
|
2569
|
+
reportTempoSettlementFailure(payload: TempoSettlementFailurePayload): Promise<void>;
|
|
2570
|
+
private postTempoSettlementReadiness;
|
|
2571
|
+
startRetryLoop(): void;
|
|
2572
|
+
stop(): void;
|
|
2367
2573
|
/**
|
|
2368
|
-
*
|
|
2369
|
-
*
|
|
2370
|
-
*
|
|
2371
|
-
*
|
|
2372
|
-
* through {@link updateChannel}. Two bounds keep that from wedging the pool
|
|
2373
|
-
* local callers are serialized in-process, so this holds at most
|
|
2374
|
-
* one client per process no matter how many requests arrive, and a waiting
|
|
2375
|
-
* acquire carries a `lock_timeout` so a wedged fleet-mate can't pin that
|
|
2376
|
-
* client — and the caller's request — indefinitely. A timeout reports the
|
|
2377
|
-
* lock as not acquired, which is what every caller already handles.
|
|
2378
|
-
*
|
|
2379
|
-
* The queue depth is reserved synchronously, before the promise chain can
|
|
2380
|
-
* yield. That makes the non-waiting form a true local try-lock even while the
|
|
2381
|
-
* first caller is still acquiring its connection: an overlapping request
|
|
2382
|
-
* returns `false` instead of queuing behind the facilitator operation.
|
|
2574
|
+
* Operator-facing queue snapshot: distinguishes the live retry queue from
|
|
2575
|
+
* terminal (stale) rows and breaks the latter down by classification reason
|
|
2576
|
+
* (`token_rotated`, `client_error:<status>`, `abandoned`). Lets monitoring tell
|
|
2577
|
+
* transient backlog apart from permanently-failed rows.
|
|
2383
2578
|
*/
|
|
2384
|
-
|
|
2579
|
+
getQueueStats(): Promise<{
|
|
2580
|
+
liveRetry: number;
|
|
2581
|
+
stale: number;
|
|
2582
|
+
byReason: Record<string, number>;
|
|
2583
|
+
}>;
|
|
2385
2584
|
/**
|
|
2386
|
-
*
|
|
2387
|
-
*
|
|
2388
|
-
* The session lock keeps one per-DVM gas EOA to one in-flight submission
|
|
2389
|
-
* across the whole fleet — two machines submitting at once collide on the
|
|
2390
|
-
* nonce. It is reached from `resource.settlePayment` inside
|
|
2391
|
-
* {@link withChannelTransaction} and from upstream's claim/settle inside
|
|
2392
|
-
* {@link withFleetSettlementLock}, so on both paths a host-pool client is
|
|
2393
|
-
* already checked out: it therefore acquires from the terminal settlement
|
|
2394
|
-
* pool, which is the rule in this class's doc, and never from the host pool
|
|
2395
|
-
* the caller is holding.
|
|
2396
|
-
*
|
|
2397
|
-
* The wait is bounded by `lock_timeout` and a timeout **refuses** rather than
|
|
2398
|
-
* proceeding unlocked, which is where this differs from
|
|
2399
|
-
* {@link withFleetSettlementLock}: a fleet lock nobody got means the claim is
|
|
2400
|
-
* someone else's turn, while a submission lock nobody got means the caller
|
|
2401
|
-
* must not submit at all. The refusal is typed and retryable, and names the
|
|
2402
|
-
* holder when Postgres will tell us who it is.
|
|
2585
|
+
* Deliver one bounded batch from the durable queue.
|
|
2403
2586
|
*
|
|
2404
|
-
*
|
|
2405
|
-
*
|
|
2406
|
-
* deposits arrive.
|
|
2587
|
+
* Processes due, non-stale rows; failures back off up to five minutes and
|
|
2588
|
+
* become terminal after 24 hours. Successful deliveries remove their rows.
|
|
2407
2589
|
*/
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
private
|
|
2412
|
-
private
|
|
2413
|
-
private runUnderFleetLock;
|
|
2414
|
-
private updateInTransaction;
|
|
2590
|
+
drainPending(): Promise<void>;
|
|
2591
|
+
private enqueue;
|
|
2592
|
+
private insertPending;
|
|
2593
|
+
private attemptDelivery;
|
|
2594
|
+
private markStale;
|
|
2415
2595
|
}
|
|
2416
2596
|
|
|
2417
2597
|
interface TempoSessionSettlementStore {
|
|
@@ -2486,6 +2666,8 @@ declare class CreditLedger {
|
|
|
2486
2666
|
readonly durable = true;
|
|
2487
2667
|
private x402Settlements?;
|
|
2488
2668
|
private expiryReleaseOutbox?;
|
|
2669
|
+
private hostingCleanupFence?;
|
|
2670
|
+
private readonly invoiceSettlementClients;
|
|
2489
2671
|
constructor(pool: CreditLedgerPool, tempoSessionStore?: TempoSessionSettlementStore | undefined);
|
|
2490
2672
|
/**
|
|
2491
2673
|
* Bound after construction because the settlement store shares this ledger and is
|
|
@@ -2498,6 +2680,7 @@ declare class CreditLedger {
|
|
|
2498
2680
|
* wiring, ledger transitions still run.
|
|
2499
2681
|
*/
|
|
2500
2682
|
useCreditExpiryReleaseOutbox(outbox: CreditExpiryReleaseOutbox): void;
|
|
2683
|
+
useHostingCleanupFence(fence: HostingCleanupFence): void;
|
|
2501
2684
|
init(): Promise<void>;
|
|
2502
2685
|
private createTables;
|
|
2503
2686
|
/**
|
|
@@ -2584,6 +2767,8 @@ declare class CreditLedger {
|
|
|
2584
2767
|
drawId: string;
|
|
2585
2768
|
amountMicro: number;
|
|
2586
2769
|
jobId?: string;
|
|
2770
|
+
hostingAdmissionId?: string;
|
|
2771
|
+
hostingPaymentSeq?: number;
|
|
2587
2772
|
/** External transaction handle — caller owns BEGIN/COMMIT (spec condition 3). */
|
|
2588
2773
|
tx?: CreditLedgerQuerier;
|
|
2589
2774
|
nowMs?: number;
|
|
@@ -2679,6 +2864,12 @@ declare class CreditLedger {
|
|
|
2679
2864
|
amountMicro: number;
|
|
2680
2865
|
/** Rail the money arrived on, for the deposit audit trail. */
|
|
2681
2866
|
rail?: string;
|
|
2867
|
+
hostingAdmissionId?: string;
|
|
2868
|
+
hostingCallerPubkey?: string;
|
|
2869
|
+
hostingJobId?: string;
|
|
2870
|
+
hostingPaymentSeq?: number;
|
|
2871
|
+
hostingInvoicePaymentHash?: string;
|
|
2872
|
+
hostingInvoiceRepairPaymentHash?: string;
|
|
2682
2873
|
/** External transaction handle — caller owns BEGIN/COMMIT (spec condition 3). */
|
|
2683
2874
|
tx?: CreditLedgerQuerier;
|
|
2684
2875
|
nowMs?: number;
|
|
@@ -3177,6 +3368,12 @@ interface CreditLedgerLike {
|
|
|
3177
3368
|
amountMicro: number;
|
|
3178
3369
|
expiryMs: number;
|
|
3179
3370
|
basis: CreditFundingBasis;
|
|
3371
|
+
hostingAdmissionId?: string;
|
|
3372
|
+
hostingJobId?: string;
|
|
3373
|
+
hostingFundId?: string;
|
|
3374
|
+
hostingPaymentSeq?: number;
|
|
3375
|
+
hostingInvoicePaymentHash?: string;
|
|
3376
|
+
hostingInvoiceRepairPaymentHash?: string;
|
|
3180
3377
|
tx?: CreditLedgerQuerier;
|
|
3181
3378
|
nowMs?: number;
|
|
3182
3379
|
}): Promise<CreditSnapshot>;
|
|
@@ -3185,6 +3382,8 @@ interface CreditLedgerLike {
|
|
|
3185
3382
|
drawId: string;
|
|
3186
3383
|
amountMicro: number;
|
|
3187
3384
|
jobId?: string;
|
|
3385
|
+
hostingAdmissionId?: string;
|
|
3386
|
+
hostingPaymentSeq?: number;
|
|
3188
3387
|
tx?: CreditLedgerQuerier;
|
|
3189
3388
|
nowMs?: number;
|
|
3190
3389
|
}): Promise<DrawResult>;
|
|
@@ -3218,6 +3417,12 @@ interface CreditLedgerLike {
|
|
|
3218
3417
|
fundId: string;
|
|
3219
3418
|
amountMicro: number;
|
|
3220
3419
|
rail?: string;
|
|
3420
|
+
hostingAdmissionId?: string;
|
|
3421
|
+
hostingCallerPubkey?: string;
|
|
3422
|
+
hostingJobId?: string;
|
|
3423
|
+
hostingPaymentSeq?: number;
|
|
3424
|
+
hostingInvoicePaymentHash?: string;
|
|
3425
|
+
hostingInvoiceRepairPaymentHash?: string;
|
|
3221
3426
|
tx?: CreditLedgerQuerier;
|
|
3222
3427
|
nowMs?: number;
|
|
3223
3428
|
}): Promise<FundingRecord>;
|
|
@@ -3871,6 +4076,13 @@ interface CreditFundArgs {
|
|
|
3871
4076
|
* Rail value contributed by this funding; required on writes.
|
|
3872
4077
|
*/
|
|
3873
4078
|
basis: CreditFundingBasis;
|
|
4079
|
+
/** Server-verified original hosted admission, never accepted from the caller body. */
|
|
4080
|
+
hostingAdmissionId?: string;
|
|
4081
|
+
hostingJobId?: string;
|
|
4082
|
+
hostingFundId?: string;
|
|
4083
|
+
hostingPaymentSeq?: number;
|
|
4084
|
+
hostingInvoicePaymentHash?: string;
|
|
4085
|
+
hostingInvoiceRepairPaymentHash?: string;
|
|
3874
4086
|
/**
|
|
3875
4087
|
* External transaction handle; caller owns BEGIN, COMMIT and ROLLBACK.
|
|
3876
4088
|
*/
|
|
@@ -4119,6 +4331,8 @@ interface TempoSessionLifecycle {
|
|
|
4119
4331
|
*/
|
|
4120
4332
|
declare class PostgresTempoSessionStore implements Store.AtomicStore {
|
|
4121
4333
|
private readonly pool;
|
|
4334
|
+
private hostingCleanupFence?;
|
|
4335
|
+
useHostingCleanupFence(fence: HostingCleanupFence): void;
|
|
4122
4336
|
private readonly acceptance;
|
|
4123
4337
|
constructor(pool: Pool);
|
|
4124
4338
|
init(): Promise<void>;
|
|
@@ -4132,6 +4346,7 @@ declare class PostgresTempoSessionStore implements Store.AtomicStore {
|
|
|
4132
4346
|
withVoucherAcceptance<value, result>(voucher: {
|
|
4133
4347
|
channelId: string;
|
|
4134
4348
|
cumulativeAmount: bigint;
|
|
4349
|
+
hostingFunding?: HostedFundingReservation;
|
|
4135
4350
|
}, commit: (tx: PoolClient, voucher: TempoVoucherDelta) => Promise<result>, operation: () => Promise<value>): Promise<{
|
|
4136
4351
|
value: value;
|
|
4137
4352
|
result: result | undefined;
|
|
@@ -4240,6 +4455,7 @@ interface MppOpts {
|
|
|
4240
4455
|
*/
|
|
4241
4456
|
tempoSession?: {
|
|
4242
4457
|
store: Store.AtomicStore;
|
|
4458
|
+
hostedObligations?: HostedSettlementReportObligations;
|
|
4243
4459
|
account: Account;
|
|
4244
4460
|
getClient?: (parameters: {
|
|
4245
4461
|
chainId?: number;
|
|
@@ -4300,6 +4516,10 @@ interface MppxServer {
|
|
|
4300
4516
|
* always the mutating path, so this is the only one the SDK binds.
|
|
4301
4517
|
*/
|
|
4302
4518
|
broadcastCredential(credential: Credential.Credential | string, options?: any): Promise<Receipt.Receipt>;
|
|
4519
|
+
/** Non-mutating mppx HMAC, route, expiry and method preflight. */
|
|
4520
|
+
validateCredential?(credential: Credential.Credential | string, options?: any): Promise<Method.Validation>;
|
|
4521
|
+
completeHostedTempoCharge?: (challengeId: string) => Promise<void>;
|
|
4522
|
+
permitHostedTempoClose?: (credential: Credential.Credential | string, reservationId: string) => void;
|
|
4303
4523
|
/**
|
|
4304
4524
|
* Typed dispatcher over `mppx.challenge.<method>.<intent>(opts)`. Localizes
|
|
4305
4525
|
* the cast across the open challenge dispatcher so callers don't repeat it.
|
|
@@ -5410,7 +5630,7 @@ interface DVMConfig<State = Record<string, unknown>, InputSchema extends ZodLike
|
|
|
5410
5630
|
/** Payment methods this DVM accepts. Default: derived from configured rails — adds "cashu" when `mints` is set, "x402" when `x402` is set, "tempo" when `tempo` is set. */
|
|
5411
5631
|
paymentMethods?: PaymentMethod[];
|
|
5412
5632
|
/** x402 stablecoin payment configuration (provider-side). */
|
|
5413
|
-
x402?: X402Config;
|
|
5633
|
+
x402?: X402Config$1;
|
|
5414
5634
|
/** MPP multi-rail payment handle (provider-side, built via `createMppFromOpts`). */
|
|
5415
5635
|
mpp?: MppxServer;
|
|
5416
5636
|
/**
|
|
@@ -5499,7 +5719,7 @@ interface DVMDescriptor<State = Record<string, unknown>, InputSchema extends Zod
|
|
|
5499
5719
|
/** Payment methods this DVM accepts. */
|
|
5500
5720
|
readonly paymentMethods?: PaymentMethod[];
|
|
5501
5721
|
/** x402 stablecoin payment configuration. */
|
|
5502
|
-
readonly x402?: X402Config;
|
|
5722
|
+
readonly x402?: X402Config$1;
|
|
5503
5723
|
/** MPP multi-rail payment handle. */
|
|
5504
5724
|
readonly mpp?: MppxServer;
|
|
5505
5725
|
/**
|
|
@@ -5541,4 +5761,4 @@ declare class StepCache {
|
|
|
5541
5761
|
static deserialize(records: StepRecord[]): StepCache;
|
|
5542
5762
|
}
|
|
5543
5763
|
|
|
5544
|
-
export { type X402Version as $, type ApprovalContent as A, SignedRequestError as B, type Currency as C, type DVMConfig as D, type SignedRequestFailure as E, type SignedRequestReplayStore as F, type SignedRequestSignOpts as G, type SignedRequestStatementHeader as H, type IncomingMessage as I, type JobCost as J, type KVStore as K, type Logger as L, type SignedRequestVerifier as M, createSignedRequestVerifier as N, isZodSchema as O, type PaymentContent as P, type QuoteConfig as Q, type ResolvedCreditConfig as R, type SDKJobContext as S, signedRequestStatementHeader as T, UnsupportedCurrencyError as U, validateCurrency as V, type JsonValue as W, type FundingReceipt as X, type JobReceipt as Y, type ZodLike as Z, type PaymentRequired as _, type DVMDescriptor as a, isDrainReceipt as a$, type MppxCredential as a0, type Message as a1, type MessageType as a2, type MppxChallenge as a3, X402_BATCH_SETTLEMENT_SCHEME as a4, X402_EXACT_SCHEME as a5, type ReceiptCredit as a6, type X402Wallet as a7, type ResourceInfo as a8, type BuildPaymentRequirementsOpts as a9, type UnsignedFundingReceipt as aA, type UnsignedJobReceipt as aB, type VerifyResponse as aC, type WorkingContent as aD, type X402Config as aE, type X402ResponseBody as aF, type X402SelfRelayRpcFailureReason as aG, X402_DEFAULT_NETWORK as aH, X402_V1_VERSION as aI, X402_VERSION as aJ, buildPaymentRequiredV2 as aK, buildPaymentRequirements as aL, caip2ToX402Network as aM, canonicalRequestPath as aN, canonicaliseForSigning as aO, canonicalize as aP, chainIdFromCaip2 as aQ, computeResultHash as aR, decodePayment as aS, decodePaymentRequiredHeader as aT, encodePayment as aU, encodePaymentRequiredHeader as aV, encodeSettleResponseHeader as aW, exactEvmAuthorization as aX, isArtifactMessage as aY, isCancelMessage as aZ, isCompleteMessage as a_, type CapabilityDescriptor as aa, type CashuMode as ab, type CompleteContent as ac, type DrainReceipt as ad, type DrainReceiptEvent as ae, type ExactEvmPayload as af, type ExactEvmPayloadAuthorization as ag, type FundingMethod as ah, type MessageFrom as ai, type PaymentPayload as aj, type PaymentPayloadV1 as ak, type PaymentPayloadV2 as al, type PaymentRequestContent as am, type PaymentRequiredV2 as an, type PaymentRequirements as ao, type PaymentRequirementsV1 as ap, type PaymentRequirementsV2 as aq, type PromptContent as ar, RAIL_REFUNDABLE as as, type ReceiptOutcome as at, type ReceiptPayment as au, type SettleResponse as av, StepCache as aw, type StepRecord as ax, type TextContent as ay, type UnsignedDrainReceipt as az, type ArtifactContent as b, type
|
|
5764
|
+
export { type X402Version as $, type ApprovalContent as A, SignedRequestError as B, type Currency as C, type DVMConfig as D, type SignedRequestFailure as E, type SignedRequestReplayStore as F, type SignedRequestSignOpts as G, type SignedRequestStatementHeader as H, type IncomingMessage as I, type JobCost as J, type KVStore as K, type Logger as L, type SignedRequestVerifier as M, createSignedRequestVerifier as N, isZodSchema as O, type PaymentContent as P, type QuoteConfig as Q, type ResolvedCreditConfig as R, type SDKJobContext as S, signedRequestStatementHeader as T, UnsupportedCurrencyError as U, validateCurrency as V, type JsonValue as W, type FundingReceipt as X, type JobReceipt as Y, type ZodLike as Z, type PaymentRequired as _, type DVMDescriptor as a, isDrainReceipt as a$, type MppxCredential as a0, type Message as a1, type MessageType as a2, type MppxChallenge as a3, X402_BATCH_SETTLEMENT_SCHEME as a4, X402_EXACT_SCHEME as a5, type ReceiptCredit as a6, type X402Wallet as a7, type ResourceInfo as a8, type BuildPaymentRequirementsOpts as a9, type UnsignedFundingReceipt as aA, type UnsignedJobReceipt as aB, type VerifyResponse as aC, type WorkingContent as aD, type X402Config$1 as aE, type X402ResponseBody as aF, type X402SelfRelayRpcFailureReason as aG, X402_DEFAULT_NETWORK as aH, X402_V1_VERSION as aI, X402_VERSION as aJ, buildPaymentRequiredV2 as aK, buildPaymentRequirements as aL, caip2ToX402Network as aM, canonicalRequestPath as aN, canonicaliseForSigning as aO, canonicalize as aP, chainIdFromCaip2 as aQ, computeResultHash as aR, decodePayment as aS, decodePaymentRequiredHeader as aT, encodePayment as aU, encodePaymentRequiredHeader as aV, encodeSettleResponseHeader as aW, exactEvmAuthorization as aX, isArtifactMessage as aY, isCancelMessage as aZ, isCompleteMessage as a_, type CapabilityDescriptor as aa, type CashuMode as ab, type CompleteContent as ac, type DrainReceipt as ad, type DrainReceiptEvent as ae, type ExactEvmPayload as af, type ExactEvmPayloadAuthorization as ag, type FundingMethod as ah, type MessageFrom as ai, type PaymentPayload as aj, type PaymentPayloadV1 as ak, type PaymentPayloadV2 as al, type PaymentRequestContent as am, type PaymentRequiredV2 as an, type PaymentRequirements as ao, type PaymentRequirementsV1 as ap, type PaymentRequirementsV2 as aq, type PromptContent as ar, RAIL_REFUNDABLE as as, type ReceiptOutcome as at, type ReceiptPayment as au, type SettleResponse as av, StepCache as aw, type StepRecord as ax, type TextContent as ay, type UnsignedDrainReceipt as az, type ArtifactContent as b, type DrawRecord as b$, isFundingReceipt as b0, isPaymentRequestMessage as b1, isPromptMessage as b2, isSignedJobReceipt as b3, isTextMessage as b4, isWorkingMessage as b5, paymentRequiredV2FromV1 as b6, signDrainReceipt as b7, signFundingReceipt as b8, signReceipt as b9, _testing as bA, createDefaultReplayStore as bB, depleteLots as bC, fifoOrder as bD, inKindDrawMsats as bE, isInKindDepletion as bF, isNonChannelBitcoinRail as bG, lotOwedSats as bH, netOwedSats as bI, wrapMppx as bJ, type CreditDrainEnqueue as bK, HostedSettlementReportObligations as bL, type DVMAuthScheme as bM, type CreditLedgerLike as bN, type X402RefundSettlementGate as bO, type CreditSnapshot as bP, type DrawResult as bQ, type X402SettlementStatus as bR, type X402UnresolvedRefund as bS, type GrownDrawResult as bT, type DrawResolution as bU, type FundingRecord as bV, type CreditInvoiceRecord as bW, type InvoiceSettlement as bX, type BlockedInvoiceCursor as bY, type InvoiceReconciliation as bZ, type InvoiceWriteOff as b_, usdcContractByCaip2 as ba, usdcContractFor as bb, usdcDomainNameFor as bc, usdcDomainVersionFor as bd, verifyDrainReceipt as be, verifyFundingReceipt as bf, verifyReceipt as bg, x402NetworkToCaip2 as bh, type X402ExactVersionSupport as bi, type X402Receipt as bj, type TransactionalPayoutHook as bk, type CashuMeltCompleted as bl, type CreditDepositPayload as bm, type CreditDrainPayload as bn, type FundingLot as bo, type LotDebit as bp, type LotDepletion as bq, type MppxServer as br, NON_CHANNEL_BITCOIN_RAILS as bs, type NonChannelBitcoinRail as bt, PayoutReporter as bu, PostgresTempoSessionStore as bv, RevenueReporter as bw, SIGNED_ENVELOPE_FIELDS as bx, SIGNED_ENVELOPE_TYPES as by, type X402TrackedChannel as bz, type CancelContent as c, type X402BatchSettlementConfig as c$, type StalePendingDrawCursor as c0, type TempoCreditLossEvidence as c1, type CreditLedgerQuerier as c2, type TempoCreditLoss as c3, type X402CreditLossEvidence as c4, type X402CreditLoss as c5, type DrainMethod as c6, type DrainRequestResult as c7, type BitcoinDepositLiability as c8, type CreditDrainRecord as c9, type JobTerminalReportPayload as cA, PostgresX402ChannelStorage as cB, type ReplayStoreBackend as cC, type RevenueSkippedNoRailPayload as cD, type RevenueSkippedNoRailReason as cE, type Secp256k1AuthOpts as cF, type X402ChannelStorageOpts as cG, type X402RelayLockHolder as cH, type X402RelaySubmissionLock as cI, X402RelaySubmissionLockError as cJ, allocateDrawValue as cK, clientCompatibilityAttributes as cL, clientCompatibilityMiddleware as cM, clientUpgradeRequired as cN, parseClientCapabilities as cO, parseClientCompatibility as cP, parseDvmClient as cQ, parseProtocolVersion as cR, requireClientCompatibility as cS, secp256k1Auth as cT, signedRequestInput as cU, type HostedIntent as cV, type X402SettlementIntent as cW, type HostedFundingReservation as cX, type X402SettlementCursor as cY, type X402SettlementWriteOff as cZ, type X402FacilitatorAuth as c_, type ChannelDrainCursor as ca, type DrainWriteOff as cb, type DrainReleaseResult as cc, type DrainFulfilment as cd, type DrainTransitionResult as ce, HostingCleanupFence as cf, CLIENT_COMPATIBILITY_HEADERS as cg, type ClientCompatibility as ch, type ClientCompatibilityEnv as ci, type ClientCompatibilityGate as cj, type ClientCompatibilityRequirement as ck, type ClientSemVer as cl, type CreditFundingBasis as cm, type CreditInvoiceStatus as cn, CreditLedger as co, CreditLedgerError as cp, type CreditLedgerErrorCode as cq, type CreditLedgerErrorDetails as cr, type CreditLedgerPool as cs, type CreditStatus as ct, DRAIN_DELIVERY_RESERVE_SATS as cu, DVM_PROTOCOL_VERSION as cv, type DrainConflictReason as cw, type DrawRailValue as cx, type DrawStatus as cy, type JobCostReportPayload as cz, type CanonicalEnvelope as d, type X402PayoutObserver as d0, type CreditDepositEnqueue as d1, type X402SettlementReconciliationReason as d2, type CreditDrawReleaseEnqueue as d3, 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, DEFAULT_JOB_RETENTION_DAYS as k, type DVMRouteContext as l, type InputType as m, InvalidCurrencyError as n, type PaymentMethod as o, type PriceValue as p, type ProgressContent as q, type PromptOpts as r, type QuoteContext as s, type QuoteResult as t, type ResponseContent as u, type SDKPaymentRequestOpts as v, SIGNED_REQUEST_AUTH_ID as w, SIGNED_REQUEST_STATEMENT_VERSION as x, type SignedRequestAudience as y, type SignedRequestDomain as z };
|