@dvmkit/sdk 0.10.1 → 0.11.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-3Q6FTZDB.js → chunk-3YFFZLW7.js} +2 -2
- package/dist/{chunk-YOSZIWC3.js → chunk-55DVLBCD.js} +135 -8
- package/dist/{chunk-VMHYGKWT.js → chunk-566CFXGU.js} +9 -1
- package/dist/chunk-5H7P3ORL.js +92 -0
- package/dist/{chunk-3R7NQA3Z.js → chunk-7KQLPVV7.js} +1 -1
- package/dist/{chunk-SYBFMHXQ.js → chunk-AIQGF52I.js} +242 -38
- package/dist/{chunk-JNECYURT.js → chunk-D4FFS3IS.js} +1 -1
- package/dist/{chunk-VTHYQCDB.js → chunk-E7ZBCT5U.js} +1 -1
- package/dist/{chunk-MMGSYMIC.js → chunk-EP7OVWI5.js} +1 -1
- package/dist/{chunk-LDIPVPKI.js → chunk-FFRTAQUW.js} +1 -1
- package/dist/chunk-H3GGFEE5.js +57 -0
- package/dist/{chunk-M3T5JYDJ.js → chunk-HNXMWHLU.js} +1 -1
- package/dist/{chunk-2OGBZPPJ.js → chunk-HQPQ4PFI.js} +1 -1
- package/dist/{chunk-UKD3R2KU.js → chunk-HW2UCOOC.js} +4 -55
- package/dist/{chunk-FFXXGCVG.js → chunk-ITRSITFP.js} +1 -1
- package/dist/{chunk-MU3IE434.js → chunk-JLT2YIBS.js} +267 -52
- package/dist/{chunk-STJBBVNL.js → chunk-K2ZNIQZP.js} +458 -69
- package/dist/{chunk-KFCEXCZM.js → chunk-KJZVTWMX.js} +1 -1
- package/dist/{chunk-W3VGCNG6.js → chunk-LDKV4UHI.js} +1 -1
- package/dist/{chunk-AJ6UHXUA.js → chunk-M4NFYEEY.js} +1 -1
- package/dist/{chunk-MB6R4XPZ.js → chunk-NS2OMIU7.js} +15 -3
- package/dist/{chunk-VUGEEMLT.js → chunk-RPUFWIT4.js} +11 -89
- package/dist/{chunk-EWZFANHJ.js → chunk-VWIMQ5H5.js} +1 -1
- package/dist/{chunk-2XQLL6N6.js → chunk-WFCVS6QB.js} +2 -2
- package/dist/{chunk-QGNXYVBO.js → chunk-Z4FOPROB.js} +17 -0
- package/dist/{content-retention-CrILCNxk.d.ts → content-retention-ySTD1wwZ.d.ts} +1 -1
- package/dist/{credit-ledger-4PZNAMDT.js → credit-ledger-4CBYLSLY.js} +6 -3
- package/dist/{credit-menu-BWsVqMh-.d.ts → credit-menu-BYySnZBk.d.ts} +4 -4
- package/dist/{fx-BM5h4o5S.d.ts → fx-DVTNs9q1.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +6 -5
- package/dist/internal/caller.d.ts +6 -6
- package/dist/internal/caller.js +26 -22
- package/dist/internal/server.d.ts +7 -7
- package/dist/internal/server.js +24 -22
- package/dist/{job-store-92v8e1LQ.d.ts → job-store-DRwXCJDI.d.ts} +1 -1
- package/dist/{lightning-backend-9oky_wJi.d.ts → lightning-backend-BX5YvjnM.d.ts} +1 -1
- package/dist/memory-credit-ledger-DKCN7FY2.js +14 -0
- package/dist/{mpp-setup-FURD45LI.js → mpp-setup-JU3MT7QX.js} +3 -3
- package/dist/{payout-reporter-HVJ6WUF3.js → payout-reporter-NXWQVKXW.js} +2 -2
- package/dist/{postgres-consumed-credential-store-WO6OCFXZ.js → postgres-consumed-credential-store-WXJLM6J7.js} +1 -1
- package/dist/{postgres-job-store-H45PRBLA.js → postgres-job-store-WIIVRZPD.js} +2 -2
- package/dist/{postgres-replay-store-XXJSA457.js → postgres-replay-store-QJ4FG6AR.js} +2 -2
- package/dist/{pricing-X4XU5HXY.js → pricing-KCLENT2C.js} +3 -3
- package/dist/{revenue-reporter-EV3K5L6B.js → revenue-reporter-HD4GMXGD.js} +2 -2
- package/dist/server/index.d.ts +28 -12
- package/dist/server/index.js +31 -29
- package/dist/{step-cache--7yA5Ihr.d.ts → step-cache-d-VKen4K.d.ts} +147 -13
- package/dist/{tempo-charge-store-5JOIYUIH.js → tempo-charge-store-7LTUAQHQ.js} +2 -2
- package/dist/{tempo-session-store-S6QMLQUA.js → tempo-session-store-LYAIDR2B.js} +3 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +6 -6
- package/dist/{usd-C7lvLvLk.d.ts → usd-DSroMJ--.d.ts} +1 -1
- package/dist/{x402-EN5LSOVM.js → x402-RUJ4LUKF.js} +3 -3
- package/package.json +1 -1
- package/recipes/postgres/financial-export-v1.sql +19 -5
- package/dist/memory-credit-ledger-RP7DT5IY.js +0 -11
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-XRLKGCAW.js";
|
|
6
6
|
import {
|
|
7
7
|
x402EarnedCeiling
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ITRSITFP.js";
|
|
9
9
|
import {
|
|
10
10
|
createX402FacilitatorAuthHeaders,
|
|
11
11
|
createX402FacilitatorClient,
|
|
@@ -14,16 +14,13 @@ import {
|
|
|
14
14
|
verifyWithFacilitator,
|
|
15
15
|
withPaymentDiagnostics,
|
|
16
16
|
withPaymentStage
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-E7ZBCT5U.js";
|
|
18
18
|
import {
|
|
19
19
|
MemoryCreditLedger
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-55DVLBCD.js";
|
|
21
21
|
import {
|
|
22
22
|
PostgresJobStore
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import {
|
|
25
|
-
parseStoreValue
|
|
26
|
-
} from "./chunk-YG7G4DPZ.js";
|
|
23
|
+
} from "./chunk-HQPQ4PFI.js";
|
|
27
24
|
import {
|
|
28
25
|
SIGNED_ENVELOPE_FIELDS,
|
|
29
26
|
SignedRequestError,
|
|
@@ -31,7 +28,7 @@ import {
|
|
|
31
28
|
humanizeZodIssues,
|
|
32
29
|
signedRequestInput,
|
|
33
30
|
zodIssuesOf
|
|
34
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-WFCVS6QB.js";
|
|
35
32
|
import {
|
|
36
33
|
MemoryJobStore,
|
|
37
34
|
UNCAPPABLE_ASK_TOTAL,
|
|
@@ -40,7 +37,7 @@ import {
|
|
|
40
37
|
isJobRetentionStore,
|
|
41
38
|
isReceiptIssuingStore,
|
|
42
39
|
isStreamableJobStore
|
|
43
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-KJZVTWMX.js";
|
|
44
41
|
import {
|
|
45
42
|
ContentHoldError,
|
|
46
43
|
JobCancelledError,
|
|
@@ -66,7 +63,6 @@ import {
|
|
|
66
63
|
attestedDvmIdentity,
|
|
67
64
|
buildAdminChallenge,
|
|
68
65
|
checkMintHealth,
|
|
69
|
-
computeResultHash,
|
|
70
66
|
createMonotonicClock,
|
|
71
67
|
formatStaleness,
|
|
72
68
|
getWallet,
|
|
@@ -75,12 +71,9 @@ import {
|
|
|
75
71
|
isX402SettlementReconciliationReason,
|
|
76
72
|
parseTraceparent,
|
|
77
73
|
receiptPubkeyFromSecret,
|
|
78
|
-
signDrainReceipt,
|
|
79
|
-
signFundingReceipt,
|
|
80
|
-
signReceipt,
|
|
81
74
|
toLockPubkey,
|
|
82
75
|
verifyChallenge
|
|
83
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-RPUFWIT4.js";
|
|
84
77
|
import {
|
|
85
78
|
X402_BATCH_SETTLEMENT_SCHEME,
|
|
86
79
|
X402_DEFAULT_FACILITATOR,
|
|
@@ -103,12 +96,11 @@ import {
|
|
|
103
96
|
import {
|
|
104
97
|
RAIL_REFUNDABLE,
|
|
105
98
|
canonicalRequestPath,
|
|
106
|
-
canonicalize,
|
|
107
99
|
isZodSchema
|
|
108
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-HW2UCOOC.js";
|
|
109
101
|
import {
|
|
110
102
|
createConsoleLogger
|
|
111
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-7KQLPVV7.js";
|
|
112
104
|
import {
|
|
113
105
|
StepCache
|
|
114
106
|
} from "./chunk-BQJIFRAZ.js";
|
|
@@ -125,7 +117,7 @@ import {
|
|
|
125
117
|
roundUsd,
|
|
126
118
|
satsToFiat,
|
|
127
119
|
validateCurrency
|
|
128
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-LDKV4UHI.js";
|
|
129
121
|
import {
|
|
130
122
|
CreditLedger,
|
|
131
123
|
CreditLedgerError,
|
|
@@ -141,17 +133,29 @@ import {
|
|
|
141
133
|
isNonChannelBitcoinRail,
|
|
142
134
|
netOwedSats,
|
|
143
135
|
x402SettlementPending
|
|
144
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-K2ZNIQZP.js";
|
|
137
|
+
import {
|
|
138
|
+
parseStoreValue
|
|
139
|
+
} from "./chunk-YG7G4DPZ.js";
|
|
145
140
|
import {
|
|
146
141
|
withSdkInitLock
|
|
147
142
|
} from "./chunk-COYKJRMM.js";
|
|
143
|
+
import {
|
|
144
|
+
computeResultHash,
|
|
145
|
+
signDrainReceipt,
|
|
146
|
+
signFundingReceipt,
|
|
147
|
+
signReceipt
|
|
148
|
+
} from "./chunk-5H7P3ORL.js";
|
|
148
149
|
import {
|
|
149
150
|
withPostgresRecoveryContext
|
|
150
|
-
} from "./chunk-
|
|
151
|
+
} from "./chunk-EP7OVWI5.js";
|
|
151
152
|
import {
|
|
152
153
|
DvmError,
|
|
153
154
|
x402NetworkByCaip2
|
|
154
155
|
} from "./chunk-BPYMEMQX.js";
|
|
156
|
+
import {
|
|
157
|
+
canonicalize
|
|
158
|
+
} from "./chunk-H3GGFEE5.js";
|
|
155
159
|
import {
|
|
156
160
|
TEMPO_MODERATO_CHAIN_ID,
|
|
157
161
|
TEMPO_MODERATO_RPC,
|
|
@@ -162,17 +166,17 @@ import {
|
|
|
162
166
|
installHostedTempoCloseGuard,
|
|
163
167
|
isTempoInsufficientFundsError,
|
|
164
168
|
resolveTempoNetwork
|
|
165
|
-
} from "./chunk-
|
|
169
|
+
} from "./chunk-JLT2YIBS.js";
|
|
166
170
|
import {
|
|
167
171
|
loggers,
|
|
168
172
|
pinDimension,
|
|
169
173
|
withSpan,
|
|
170
174
|
withTraceContext
|
|
171
|
-
} from "./chunk-
|
|
175
|
+
} from "./chunk-HNXMWHLU.js";
|
|
172
176
|
import {
|
|
173
177
|
createDiagnosticCapabilityAuthority,
|
|
174
178
|
diagnosticConsole
|
|
175
|
-
} from "./chunk-
|
|
179
|
+
} from "./chunk-Z4FOPROB.js";
|
|
176
180
|
|
|
177
181
|
// src/sdk/server/lock-pubkey-store.ts
|
|
178
182
|
async function initLockPubkeyTable(db) {
|
|
@@ -3884,7 +3888,7 @@ async function verifyUpfrontPaymentInScope(opts) {
|
|
|
3884
3888
|
x402Requirements
|
|
3885
3889
|
});
|
|
3886
3890
|
}
|
|
3887
|
-
const { verifyX402Payment } = await import("./x402-
|
|
3891
|
+
const { verifyX402Payment } = await import("./x402-RUJ4LUKF.js");
|
|
3888
3892
|
const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
|
|
3889
3893
|
x402Payment,
|
|
3890
3894
|
x402Config,
|
|
@@ -4908,7 +4912,7 @@ async function verifyIncomingPaymentInScope(body, opts, snapshot) {
|
|
|
4908
4912
|
snapshot
|
|
4909
4913
|
});
|
|
4910
4914
|
}
|
|
4911
|
-
const { verifyX402Payment } = await import("./x402-
|
|
4915
|
+
const { verifyX402Payment } = await import("./x402-RUJ4LUKF.js");
|
|
4912
4916
|
const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
|
|
4913
4917
|
body.content.x402_payment,
|
|
4914
4918
|
opts.x402Config,
|
|
@@ -5339,7 +5343,7 @@ async function processIncomingPaymentInScope(job, body, opts) {
|
|
|
5339
5343
|
try {
|
|
5340
5344
|
const requiredMsats = job.pendingPaymentMsats ?? 0;
|
|
5341
5345
|
const requiredUsdcMicro = job.pendingX402AmountUsdcMicro !== void 0 ? BigInt(job.pendingX402AmountUsdcMicro) : BigInt(msatsToUsdc(requiredMsats, rate));
|
|
5342
|
-
const { verifyX402Payment } = await import("./x402-
|
|
5346
|
+
const { verifyX402Payment } = await import("./x402-RUJ4LUKF.js");
|
|
5343
5347
|
const receipt = await withPaymentStage("verifier_broadcast", () => verifyX402Payment(
|
|
5344
5348
|
body.content.x402_payment,
|
|
5345
5349
|
opts.x402Config,
|
|
@@ -6943,13 +6947,37 @@ async function fundingReceiptFor(ledger, issuer, funding) {
|
|
|
6943
6947
|
|
|
6944
6948
|
// src/sdk/server/tempo-drain-leg.ts
|
|
6945
6949
|
async function completeTempoChannelDrain(args) {
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6950
|
+
if (!args.tx && args.refundedNative !== void 0 && /^\d{1,78}$/.test(String(args.refundedNative))) {
|
|
6951
|
+
try {
|
|
6952
|
+
if (args.finalization?.outcome === "payer_exit" || args.finalization?.outcome === "payer_exit_unbacked") {
|
|
6953
|
+
if (BigInt(args.refundedNative) > 0n) await args.deps.ledger.recordTempoDrainPayment?.({ creditId: args.creditId, drainId: args.drainId });
|
|
6954
|
+
} else {
|
|
6955
|
+
await args.deps.ledger.recordTempoDrainRefund?.({ creditId: args.creditId, drainId: args.drainId, refundedNative: args.refundedNative });
|
|
6956
|
+
}
|
|
6957
|
+
} catch (error) {
|
|
6958
|
+
loggers.tempo.warn("tempo_drain_refund_evidence_write_failed", {});
|
|
6959
|
+
throw error;
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
let settlementEvidence;
|
|
6963
|
+
try {
|
|
6964
|
+
settlementEvidence = refundFloorEvidence({
|
|
6965
|
+
rail: "tempo",
|
|
6966
|
+
refundedNative: args.refundedNative,
|
|
6967
|
+
debitedNative: args.debitedNative,
|
|
6968
|
+
creditId: args.creditId,
|
|
6969
|
+
drainId: args.drainId
|
|
6970
|
+
});
|
|
6971
|
+
} catch (error) {
|
|
6972
|
+
if (error instanceof DrainRefundFloorError && !args.tx) {
|
|
6973
|
+
try {
|
|
6974
|
+
await args.deps.ledger.recordTempoDrainHold?.({ creditId: args.creditId, drainId: args.drainId, reason: "refund_short" });
|
|
6975
|
+
} catch {
|
|
6976
|
+
loggers.tempo.warn("tempo_drain_hold_reason_write_failed", {});
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6979
|
+
throw error;
|
|
6980
|
+
}
|
|
6953
6981
|
return commitTerminalDrain(
|
|
6954
6982
|
args.deps.drainReport,
|
|
6955
6983
|
async (innerTx) => {
|
|
@@ -7690,6 +7718,11 @@ async function recoverTempoDrain(deps, mpp, record, channelId) {
|
|
|
7690
7718
|
}
|
|
7691
7719
|
if (!result) return null;
|
|
7692
7720
|
if (!/^0x[0-9a-fA-F]{64}$/.test(result)) {
|
|
7721
|
+
try {
|
|
7722
|
+
await deps.ledger.recordTempoDrainHold?.({ creditId: record.creditId, drainId: record.drainId, reason: "close_evidence_missing" });
|
|
7723
|
+
} catch {
|
|
7724
|
+
loggers.tempo.warn("tempo_drain_hold_reason_write_failed", { amount_micro: record.amountMicro });
|
|
7725
|
+
}
|
|
7693
7726
|
loggers.tempo.warn("tempo_session_drain_refund_unproven", {
|
|
7694
7727
|
channel_id: channelId,
|
|
7695
7728
|
credit_id: record.creditId,
|
|
@@ -10130,6 +10163,7 @@ var HostedSettlementReportObligations = class {
|
|
|
10130
10163
|
FROM sdk_hosted_settlement_report_obligations target
|
|
10131
10164
|
WHERE target.dvm_id=$1 AND target.scope=$2 AND target.rail='tempo' AND target.operation='close'
|
|
10132
10165
|
AND target.state IN ('unmatched','no_report')
|
|
10166
|
+
AND ($7::text IS NULL OR target.channel_id=$7)
|
|
10133
10167
|
)
|
|
10134
10168
|
SELECT * FROM candidates
|
|
10135
10169
|
WHERE ($6::boolean OR (state='unmatched' AND (retryable OR write_off_allowed)))
|
|
@@ -10141,7 +10175,8 @@ var HostedSettlementReportObligations = class {
|
|
|
10141
10175
|
args.limit,
|
|
10142
10176
|
args.after?.createdAtMs ?? null,
|
|
10143
10177
|
args.after?.obligationId ?? null,
|
|
10144
|
-
args.all ?? false
|
|
10178
|
+
args.all ?? false,
|
|
10179
|
+
args.channelId ?? null
|
|
10145
10180
|
]
|
|
10146
10181
|
);
|
|
10147
10182
|
return result.rows.map((row) => ({
|
|
@@ -17170,6 +17205,8 @@ function shortSha(sha) {
|
|
|
17170
17205
|
}
|
|
17171
17206
|
|
|
17172
17207
|
// src/sdk/server/admin-credit.ts
|
|
17208
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
17209
|
+
import { bytesToHex } from "@noble/hashes/utils.js";
|
|
17173
17210
|
import {
|
|
17174
17211
|
Amount as Amount3,
|
|
17175
17212
|
CheckStateEnum as CheckStateEnum3,
|
|
@@ -17177,6 +17214,7 @@ import {
|
|
|
17177
17214
|
getTokenMetadata as getTokenMetadata2
|
|
17178
17215
|
} from "@cashu/cashu-ts";
|
|
17179
17216
|
function installAdminCreditRoutes(app, opts) {
|
|
17217
|
+
app.post("/admin/credit/refund-draw", (c) => handleRefundDraw(c, opts));
|
|
17180
17218
|
app.get("/admin/credit/pending-drains", (c) => handlePendingDrains(c, opts));
|
|
17181
17219
|
app.get("/admin/credit/bitcoin-liability", (c) => handleBitcoinLiability(c, opts));
|
|
17182
17220
|
app.post("/admin/credit/park-drain", (c) => handleParkDrain(c, opts));
|
|
@@ -17197,9 +17235,68 @@ function installAdminCreditRoutes(app, opts) {
|
|
|
17197
17235
|
app.post("/admin/credit/write-off-tempo-close-report", (c) => handleTempoCloseReportAction(c, opts, "write-off"));
|
|
17198
17236
|
app.get("/admin/credit/tempo-drains", (c) => handleTempoDrains(c, opts));
|
|
17199
17237
|
app.get("/admin/credit/tempo-losses", (c) => handleTempoLosses(c, opts));
|
|
17238
|
+
app.post("/admin/credit/book-tempo-drain", (c) => handleTempoDrainDecision(c, opts, "book_refunded"));
|
|
17239
|
+
app.post("/admin/credit/release-tempo-drain", (c) => handleTempoDrainDecision(c, opts, "release"));
|
|
17200
17240
|
app.post("/admin/credit/reconcile-tempo-drain", (c) => handleReconcileTempo(c, opts));
|
|
17201
17241
|
app.post("/admin/credit/write-off-tempo-drain", (c) => handleWriteOffTempo(c, opts));
|
|
17202
17242
|
}
|
|
17243
|
+
async function handleRefundDraw(c, opts) {
|
|
17244
|
+
const auth = await requireAuth(c, opts);
|
|
17245
|
+
if ("response" in auth) return auth.response;
|
|
17246
|
+
const body = await c.req.json().catch(() => null);
|
|
17247
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
17248
|
+
return c.json({ error: "invalid_body", message: "Request body must be a JSON object." }, 400);
|
|
17249
|
+
}
|
|
17250
|
+
const value = body;
|
|
17251
|
+
const nonempty = (item) => typeof item === "string" && item.length > 0;
|
|
17252
|
+
const byJob = nonempty(value.jobId) && value.creditId === void 0 && value.drawId === void 0;
|
|
17253
|
+
const byDraw = value.jobId === void 0 && nonempty(value.creditId) && nonempty(value.drawId);
|
|
17254
|
+
if (!byJob && !byDraw || !nonempty(value.refundId) || value.amountMicro !== void 0 && (typeof value.amountMicro !== "number" || !Number.isSafeInteger(value.amountMicro) || value.amountMicro <= 0) || value.reason !== void 0 && (typeof value.reason !== "string" || value.reason.length > 280)) {
|
|
17255
|
+
return c.json({ error: "invalid_body", message: "Provide jobId or creditId + drawId, refundId, an optional positive safe integer amountMicro, and an optional reason of at most 280 characters." }, 400);
|
|
17256
|
+
}
|
|
17257
|
+
if (!opts.ledger.durable || !opts.enqueueCreditDrawRefund) {
|
|
17258
|
+
return c.json({ error: "credit_refund_unavailable", hint: "Configure the persistent credit store and platform refund reporter, then retry with the original refund ID." }, 503);
|
|
17259
|
+
}
|
|
17260
|
+
try {
|
|
17261
|
+
const result = await opts.ledger.refundDraw({
|
|
17262
|
+
...byJob ? { jobId: value.jobId } : { creditId: value.creditId, drawId: value.drawId },
|
|
17263
|
+
refundId: value.refundId,
|
|
17264
|
+
...value.amountMicro === void 0 ? {} : { amountMicro: value.amountMicro },
|
|
17265
|
+
...value.reason === void 0 ? {} : { reason: value.reason },
|
|
17266
|
+
outbox: { dvmId: opts.dvmId, enqueue: opts.enqueueCreditDrawRefund },
|
|
17267
|
+
...opts.receiptIssuer ? { trustedReceiptPubkey: opts.receiptIssuer.pubkey } : {},
|
|
17268
|
+
...opts.now ? { nowMs: opts.now() } : {}
|
|
17269
|
+
});
|
|
17270
|
+
return c.json({
|
|
17271
|
+
status: "refunded",
|
|
17272
|
+
refund_id: result.refundId,
|
|
17273
|
+
credit_id: result.creditId,
|
|
17274
|
+
draw_id: result.drawId,
|
|
17275
|
+
job_id: result.jobId,
|
|
17276
|
+
amount_micro: result.amountMicro,
|
|
17277
|
+
currency: result.currency,
|
|
17278
|
+
ledger_seq: result.ledgerSeq,
|
|
17279
|
+
refunded_at: result.refundedAt,
|
|
17280
|
+
balance_after_micro: result.balanceAfterMicro,
|
|
17281
|
+
replayed: result.replayed
|
|
17282
|
+
});
|
|
17283
|
+
} catch (error) {
|
|
17284
|
+
if (!(error instanceof CreditLedgerError)) throw error;
|
|
17285
|
+
const hints = {
|
|
17286
|
+
draw_not_found: "Check the job ID or credit/draw IDs. If they are correct, this DVM may lack trusted ownership evidence; ask an operator to inspect that evidence. Retrying alone cannot establish ownership.",
|
|
17287
|
+
draw_not_completed: "Wait for the job to complete. Failed or cancelled jobs release their pending hold automatically.",
|
|
17288
|
+
refund_exceeds_draw: "Refund no more than the remaining unrefunded charge. Omit amountMicro to refund that remainder.",
|
|
17289
|
+
credit_not_open: "The original credit is closed or expired. Ask an operator to inspect the original payment before arranging a refund.",
|
|
17290
|
+
credit_refund_channel_unsupported: "This channel returns unused collateral through its normal exit. Ask an operator about refunding completed spending.",
|
|
17291
|
+
credit_refund_basis_unavailable: "The original payment value cannot be proven automatically. Ask an operator to inspect independent original-payment evidence before arranging a repair. Retrying alone cannot establish that evidence.",
|
|
17292
|
+
credit_refund_state_unavailable: "The original credit reopening order cannot be proven automatically. Ask an operator to inspect independent funding and drain evidence before arranging a repair. Retrying alone cannot establish that evidence.",
|
|
17293
|
+
refund_conflict: "Repeat the original arguments, or deliberately choose a new refund ID for a different refund."
|
|
17294
|
+
};
|
|
17295
|
+
const hint = hints[error.code];
|
|
17296
|
+
if (!hint) throw error;
|
|
17297
|
+
return c.json({ error: error.code, hint }, error.code === "draw_not_found" ? 404 : error.code === "refund_conflict" ? 409 : 422);
|
|
17298
|
+
}
|
|
17299
|
+
}
|
|
17203
17300
|
async function handleX402ExactSettlements(c, opts) {
|
|
17204
17301
|
const auth = await requireAuth(c, opts);
|
|
17205
17302
|
if ("response" in auth) return auth.response;
|
|
@@ -17318,6 +17415,9 @@ async function handleTempoLosses(c, opts) {
|
|
|
17318
17415
|
currency: loss.currency,
|
|
17319
17416
|
former_balance_micro: loss.formerBalanceMicro,
|
|
17320
17417
|
settled_on_chain_native: loss.settledOnChainNative.toString(),
|
|
17418
|
+
refunded_to_payer_native: loss.refundedToPayerNative?.toString() ?? null,
|
|
17419
|
+
loss_native: loss.lossNative?.toString() ?? null,
|
|
17420
|
+
loss_basis: loss.lossBasis ?? null,
|
|
17321
17421
|
highest_voucher_native: loss.highestVoucherNative.toString(),
|
|
17322
17422
|
consumed_service_micro: loss.consumedServiceMicro,
|
|
17323
17423
|
consumed_native: loss.consumedNative.toString(),
|
|
@@ -18304,13 +18404,18 @@ async function handleTempoDrains(c, opts) {
|
|
|
18304
18404
|
...after ? { after } : {},
|
|
18305
18405
|
createdBeforeMs: nowMs - minAge,
|
|
18306
18406
|
rail: "tempo",
|
|
18307
|
-
includeResolved: c.req.query("include_resolved") === "true"
|
|
18407
|
+
includeResolved: c.req.query("include_resolved") === "true",
|
|
18408
|
+
includeWrittenOff: true
|
|
18308
18409
|
});
|
|
18309
18410
|
const wantsEvidence = c.req.query("evidence") !== "false";
|
|
18310
18411
|
const rows2 = [];
|
|
18311
18412
|
for (const drain of drains) {
|
|
18312
18413
|
const evidence = wantsEvidence && drain.channelId ? await classifyTempoCloseEvidence(rail.rail, drain.channelId) : void 0;
|
|
18313
|
-
|
|
18414
|
+
const closeReports = drain.channelId && opts.hostedSettlementReports ? await opts.hostedSettlementReports.store.listTempoCloseReports({ limit: 50, all: true, channelId: drain.channelId }) : [];
|
|
18415
|
+
rows2.push({
|
|
18416
|
+
...tempoDrainBody(drain, evidence),
|
|
18417
|
+
close_reports: closeReports.map((row) => ({ obligation_id: row.id, status: row.status, written_off: row.writtenOff }))
|
|
18418
|
+
});
|
|
18314
18419
|
}
|
|
18315
18420
|
const last = drains.length === limit ? drains[drains.length - 1] : void 0;
|
|
18316
18421
|
return c.json({
|
|
@@ -18324,6 +18429,90 @@ async function handleTempoDrains(c, opts) {
|
|
|
18324
18429
|
} : {}
|
|
18325
18430
|
});
|
|
18326
18431
|
}
|
|
18432
|
+
async function handleTempoDrainDecision(c, opts, decision) {
|
|
18433
|
+
const auth = await requireAuth(c, opts);
|
|
18434
|
+
if ("response" in auth) return auth.response;
|
|
18435
|
+
const body = await c.req.json().catch(() => null);
|
|
18436
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
18437
|
+
return c.json({ error: "invalid_body", message: "Request body must be a JSON object." }, 400);
|
|
18438
|
+
}
|
|
18439
|
+
const value = body;
|
|
18440
|
+
const text = (item, max) => typeof item === "string" && item.trim().length > 0 && item.trim().length <= max;
|
|
18441
|
+
if (Object.keys(value).some((key) => !["credit_id", "drain_id", "reason", "evidence"].includes(key)) || typeof value.credit_id !== "string" || value.credit_id.length === 0 || typeof value.drain_id !== "string" || value.drain_id.length === 0 || !text(value.reason, 2e3) || value.evidence !== void 0 && !text(value.evidence, 4e3)) {
|
|
18442
|
+
return c.json({ error: "invalid_body", message: "Provide credit_id, drain_id, a reason of 1\u20132000 characters, and optional evidence of 1\u20134000 characters." }, 400);
|
|
18443
|
+
}
|
|
18444
|
+
if (!opts.ledger.durable || !opts.ledger.decideTempoDrain) {
|
|
18445
|
+
return c.json({ error: "tempo_drain_decision_unavailable", message: "A durable Tempo decision store is required." }, 503);
|
|
18446
|
+
}
|
|
18447
|
+
const args = {
|
|
18448
|
+
creditId: value.credit_id,
|
|
18449
|
+
drainId: value.drain_id,
|
|
18450
|
+
decision,
|
|
18451
|
+
reason: value.reason.trim(),
|
|
18452
|
+
...value.evidence === void 0 ? {} : { evidence: value.evidence.trim() },
|
|
18453
|
+
nowMs: opts.now ? opts.now() : Date.now()
|
|
18454
|
+
};
|
|
18455
|
+
if (decision === "book_refunded" && opts.requireDrainReport && !opts.enqueueCreditDrain) {
|
|
18456
|
+
const existing = await opts.ledger.getDrain(args);
|
|
18457
|
+
if (existing && !existing.operatorDecision && (existing.status === "pending" || existing.status === "exit_required")) {
|
|
18458
|
+
return c.json({ error: "tempo_drain_decision_unavailable", message: "Hosted booking requires a transactional drain reporter. Restore its configuration, then retry the original decision." }, 503);
|
|
18459
|
+
}
|
|
18460
|
+
}
|
|
18461
|
+
const client = await opts.db.connect();
|
|
18462
|
+
let result;
|
|
18463
|
+
try {
|
|
18464
|
+
await client.query("BEGIN");
|
|
18465
|
+
let decided;
|
|
18466
|
+
const transition = async () => {
|
|
18467
|
+
decided = await opts.ledger.decideTempoDrain({ ...args, tx: client });
|
|
18468
|
+
return decided;
|
|
18469
|
+
};
|
|
18470
|
+
if (decision === "book_refunded") {
|
|
18471
|
+
await commitTerminalDrain({ db: opts.db, dvmId: opts.dvmId, enqueueCreditDrain: opts.enqueueCreditDrain }, transition, client);
|
|
18472
|
+
} else {
|
|
18473
|
+
await transition();
|
|
18474
|
+
}
|
|
18475
|
+
result = decided;
|
|
18476
|
+
await client.query("COMMIT");
|
|
18477
|
+
} catch (error) {
|
|
18478
|
+
await client.query("ROLLBACK").catch(() => void 0);
|
|
18479
|
+
if (error instanceof CreditLedgerError) {
|
|
18480
|
+
return c.json(
|
|
18481
|
+
{ error: error.code, message: error.message },
|
|
18482
|
+
error.code === "drain_not_found" || error.code === "credit_not_found" ? 404 : ["drain_decision_conflict", "tempo_drain_close_pending", "tempo_drain_refund_paid", "tempo_drain_refund_unknown"].includes(error.code) ? 409 : 422
|
|
18483
|
+
);
|
|
18484
|
+
}
|
|
18485
|
+
throw error;
|
|
18486
|
+
} finally {
|
|
18487
|
+
client.release();
|
|
18488
|
+
}
|
|
18489
|
+
if (!result.replayed) {
|
|
18490
|
+
diagnosticConsole.warn({
|
|
18491
|
+
dataset: "tempo",
|
|
18492
|
+
event: decision === "book_refunded" ? "tempo_drain_booked_as_refunded" : "tempo_drain_released",
|
|
18493
|
+
credit_id: result.drain.creditId,
|
|
18494
|
+
drain_id: result.drain.drainId,
|
|
18495
|
+
channel_id: result.drain.channelId,
|
|
18496
|
+
drain_reference: `sha256:${bytesToHex(sha256(new TextEncoder().encode(JSON.stringify([result.drain.creditId, result.drain.drainId]))))}`,
|
|
18497
|
+
amount_micro: result.drain.amountMicro,
|
|
18498
|
+
amount_native: result.drain.drainedNative?.toString() ?? null,
|
|
18499
|
+
currency: result.drain.currency
|
|
18500
|
+
});
|
|
18501
|
+
}
|
|
18502
|
+
return c.json({
|
|
18503
|
+
status: result.drain.status,
|
|
18504
|
+
credit_id: result.drain.creditId,
|
|
18505
|
+
drain_id: result.drain.drainId,
|
|
18506
|
+
channel_id: result.drain.channelId,
|
|
18507
|
+
amount_micro: result.drain.amountMicro,
|
|
18508
|
+
amount_native: result.drain.drainedNative,
|
|
18509
|
+
currency: result.drain.currency,
|
|
18510
|
+
decision: result.drain.operatorDecision,
|
|
18511
|
+
balance_micro: result.credit.balanceMicro,
|
|
18512
|
+
spendable_micro: result.spendableMicro,
|
|
18513
|
+
replayed: result.replayed
|
|
18514
|
+
});
|
|
18515
|
+
}
|
|
18327
18516
|
async function handleReconcileTempo(c, opts) {
|
|
18328
18517
|
const auth = await requireAuth(c, opts);
|
|
18329
18518
|
if ("response" in auth) return auth.response;
|
|
@@ -18337,6 +18526,12 @@ async function handleReconcileTempo(c, opts) {
|
|
|
18337
18526
|
if (record.status === "sent") return tempoReconciledBody(c, record, channelId, true);
|
|
18338
18527
|
const evidence = await classifyTempoCloseEvidence(rail.rail, channelId);
|
|
18339
18528
|
if (evidence.chain !== "found") return tempoEvidenceRefusal(c, evidence);
|
|
18529
|
+
try {
|
|
18530
|
+
await opts.ledger.recordTempoDrainRefund?.({ creditId: record.creditId, drainId: record.drainId, refundedNative: evidence.refundedToPayer });
|
|
18531
|
+
} catch (error) {
|
|
18532
|
+
diagnosticConsole.warn({ dataset: "tempo", event: "tempo_drain_refund_evidence_write_failed", amount_micro: record.amountMicro });
|
|
18533
|
+
throw error;
|
|
18534
|
+
}
|
|
18340
18535
|
const client = await opts.db.connect();
|
|
18341
18536
|
let booked;
|
|
18342
18537
|
try {
|
|
@@ -18825,6 +19020,10 @@ function tempoDrainBody(drain, evidence) {
|
|
|
18825
19020
|
created_at: drain.createdAt,
|
|
18826
19021
|
write_off_note: drain.writeOffNote,
|
|
18827
19022
|
written_off_at: drain.writtenOffAt,
|
|
19023
|
+
decision: drain.operatorDecision ?? null,
|
|
19024
|
+
proven_refund_native: drain.provenRefundNative ?? null,
|
|
19025
|
+
refund_payment_proven: drain.refundPaymentProven ?? false,
|
|
19026
|
+
held_reason: drain.status === "pending" || drain.status === "exit_required" ? drain.holdReason ?? (evidence?.chain === "found" && drain.drainedNative !== null && BigInt(evidence.refundedToPayer) < BigInt(drain.drainedNative) ? "refund_short" : drain.status === "exit_required" ? "exit_required" : "refund_unproven") : null,
|
|
18828
19027
|
...evidence ? { chain: evidence } : {}
|
|
18829
19028
|
};
|
|
18830
19029
|
}
|
|
@@ -20170,7 +20369,7 @@ async function createDVMServer(descriptor, opts) {
|
|
|
20170
20369
|
}
|
|
20171
20370
|
}
|
|
20172
20371
|
if (!opts.onJobCompleted && platformToken && platformUrl && dvmId && opts.db) {
|
|
20173
|
-
const { RevenueReporter } = await import("./revenue-reporter-
|
|
20372
|
+
const { RevenueReporter } = await import("./revenue-reporter-HD4GMXGD.js");
|
|
20174
20373
|
const reporter = new RevenueReporter(
|
|
20175
20374
|
opts.db,
|
|
20176
20375
|
platformUrl,
|
|
@@ -20180,7 +20379,7 @@ async function createDVMServer(descriptor, opts) {
|
|
|
20180
20379
|
revenueReporter = reporter;
|
|
20181
20380
|
await reporter.init();
|
|
20182
20381
|
reporter.startRetryLoop();
|
|
20183
|
-
const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-
|
|
20382
|
+
const { MemoryX402BatchStore, PayoutReporter } = await import("./payout-reporter-NXWQVKXW.js");
|
|
20184
20383
|
payoutReporter = new PayoutReporter({
|
|
20185
20384
|
dvmId,
|
|
20186
20385
|
db: opts.db,
|
|
@@ -20208,6 +20407,7 @@ async function createDVMServer(descriptor, opts) {
|
|
|
20208
20407
|
const effectiveOnRevenueSkippedNoRail = opts.onRevenueSkippedNoRail ?? (activeRevenueReporter ? (info) => activeRevenueReporter.reportRevenueSkippedNoRail(info) : void 0);
|
|
20209
20408
|
const enqueueCreditDeposit = !opts.onCreditFunded && activeRevenueReporter ? activeRevenueReporter.enqueueDeposit.bind(activeRevenueReporter) : void 0;
|
|
20210
20409
|
const enqueueCreditDrain = activeRevenueReporter ? activeRevenueReporter.enqueueCreditDrain.bind(activeRevenueReporter) : void 0;
|
|
20410
|
+
const enqueueCreditDrawRefund = activeRevenueReporter ? activeRevenueReporter.enqueueCreditDrawRefund.bind(activeRevenueReporter) : void 0;
|
|
20211
20411
|
const enqueueCreditDrawRelease = activeRevenueReporter ? activeRevenueReporter.enqueueCreditDrawRelease.bind(activeRevenueReporter) : void 0;
|
|
20212
20412
|
unsubscribeTempoSettlementReadiness = opts.tempoSettlementReadiness?.subscribe({
|
|
20213
20413
|
onBalance: (observation) => activeRevenueReporter && dvmId ? activeRevenueReporter.reportTempoSettlementBalance({
|
|
@@ -20363,6 +20563,7 @@ async function createDVMServer(descriptor, opts) {
|
|
|
20363
20563
|
} : void 0;
|
|
20364
20564
|
server = new DVMServer(descriptor, {
|
|
20365
20565
|
...opts,
|
|
20566
|
+
platformUrl,
|
|
20366
20567
|
hostingControlInstance: hostingControl,
|
|
20367
20568
|
hostedSettlementReports,
|
|
20368
20569
|
hostingClosureSupport,
|
|
@@ -20377,6 +20578,7 @@ async function createDVMServer(descriptor, opts) {
|
|
|
20377
20578
|
enqueueCreditDeposit,
|
|
20378
20579
|
enqueueCreditDrain,
|
|
20379
20580
|
enqueueCreditDrawRelease,
|
|
20581
|
+
enqueueCreditDrawRefund,
|
|
20380
20582
|
x402BatchSettlement,
|
|
20381
20583
|
x402BatchSettlementFactory,
|
|
20382
20584
|
x402ExactSettlement,
|
|
@@ -20832,8 +21034,10 @@ var DVMServer = class {
|
|
|
20832
21034
|
receiptIssuer: this.receiptIssuer,
|
|
20833
21035
|
creditTtlMs: () => this.creditTtlMs(),
|
|
20834
21036
|
creditCurrency: () => this.creditCurrency(),
|
|
21037
|
+
enqueueCreditDrawRefund: this.opts.enqueueCreditDrawRefund,
|
|
20835
21038
|
enqueueCreditDeposit: this.opts.enqueueCreditDeposit,
|
|
20836
21039
|
enqueueCreditDrain: this.opts.enqueueCreditDrain,
|
|
21040
|
+
requireDrainReport: !!this.opts.platformUrl,
|
|
20837
21041
|
lightningReceive: this.opts.lightningReceive,
|
|
20838
21042
|
// x402 repair queue rides the same surface, and resolves
|
|
20839
21043
|
// the rail the same lazy way the verify path does — constructing the
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/lib/cast-auth/canonical-json.ts
|
|
2
|
+
function canonicalize(value) {
|
|
3
|
+
return JSON.stringify(sortDeep(value));
|
|
4
|
+
}
|
|
5
|
+
function canonicaliseForSigning(value) {
|
|
6
|
+
return new TextEncoder().encode(canonicalize(value));
|
|
7
|
+
}
|
|
8
|
+
function sortDeep(value) {
|
|
9
|
+
if (value === null) return null;
|
|
10
|
+
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
11
|
+
if (typeof value === "number") {
|
|
12
|
+
if (!Number.isFinite(value)) {
|
|
13
|
+
throw new TypeError(`canonicalize: non-finite number is not JSON-serialisable`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(value)) return value.map(sortDeep);
|
|
18
|
+
if (typeof value === "object") {
|
|
19
|
+
const sorted = {};
|
|
20
|
+
for (const key of Object.keys(value).sort()) {
|
|
21
|
+
const v = value[key];
|
|
22
|
+
if (v === void 0) continue;
|
|
23
|
+
sorted[key] = sortDeep(v);
|
|
24
|
+
}
|
|
25
|
+
return sorted;
|
|
26
|
+
}
|
|
27
|
+
throw new TypeError(`canonicalize: unsupported value of type ${typeof value}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/lib/cast-auth/verify.ts
|
|
31
|
+
import { schnorr } from "@noble/curves/secp256k1.js";
|
|
32
|
+
function verifyCanonicalSignedRequest(payload, signatureHex, pubkeyHex) {
|
|
33
|
+
if (!/^[0-9a-f]{64}$/i.test(pubkeyHex) || !/^[0-9a-f]{128}$/i.test(signatureHex)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const data = canonicaliseForSigning(payload);
|
|
37
|
+
const signature = hexToBytes(signatureHex);
|
|
38
|
+
const pubkey = hexToBytes(pubkeyHex);
|
|
39
|
+
try {
|
|
40
|
+
return schnorr.verify(signature, data, pubkey);
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function hexToBytes(hex) {
|
|
46
|
+
const out = new Uint8Array(hex.length / 2);
|
|
47
|
+
for (let i = 0; i < out.length; i++) {
|
|
48
|
+
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
canonicalize,
|
|
55
|
+
canonicaliseForSigning,
|
|
56
|
+
verifyCanonicalSignedRequest
|
|
57
|
+
};
|