@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
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
createX402FacilitatorAuthHeaders,
|
|
3
3
|
settleWithFacilitator,
|
|
4
4
|
verifyWithFacilitator
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-E7ZBCT5U.js";
|
|
6
6
|
import {
|
|
7
7
|
buildPaymentRequirements,
|
|
8
8
|
decodePayment,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "./chunk-66HGCPBU.js";
|
|
16
16
|
import {
|
|
17
17
|
diagnosticConsole
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-Z4FOPROB.js";
|
|
19
19
|
|
|
20
20
|
// src/lib/x402/verify.ts
|
|
21
21
|
async function verifyX402Payment(paymentHeader, config, requiredUsdcMicro, resource, btcUsdRate, exactVersions = { v1: true, v2: true }) {
|
|
@@ -10,12 +10,15 @@ import {
|
|
|
10
10
|
fifoOrder,
|
|
11
11
|
growthRailValue,
|
|
12
12
|
inKindDrawMsats,
|
|
13
|
+
isFundingRail,
|
|
13
14
|
isInKindDepletion,
|
|
14
15
|
isNonChannelBitcoinRail,
|
|
15
16
|
lotOwedSats,
|
|
16
17
|
netOwedSats,
|
|
18
|
+
refundAssetDelta,
|
|
19
|
+
refundRequest,
|
|
17
20
|
x402SettlementPending
|
|
18
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-K2ZNIQZP.js";
|
|
19
22
|
|
|
20
23
|
// src/sdk/server/memory-credit-ledger.ts
|
|
21
24
|
import { randomUUID } from "crypto";
|
|
@@ -26,7 +29,9 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
26
29
|
* machine and drawing on another would `credit_not_found`.
|
|
27
30
|
*/
|
|
28
31
|
durable = false;
|
|
32
|
+
tempoCreditLossEvidenceVersion = 1;
|
|
29
33
|
credits = /* @__PURE__ */ new Map();
|
|
34
|
+
refunds = /* @__PURE__ */ new Map();
|
|
30
35
|
fundings = /* @__PURE__ */ new Map();
|
|
31
36
|
invoices = /* @__PURE__ */ new Map();
|
|
32
37
|
tempoLosses = /* @__PURE__ */ new Map();
|
|
@@ -35,7 +40,7 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
35
40
|
/** Internal disposable transaction snapshot. The caller must serialize every ledger access. */
|
|
36
41
|
forkForTransaction() {
|
|
37
42
|
const fork = new _MemoryCreditLedger();
|
|
38
|
-
for (const name of ["credits", "fundings", "invoices", "tempoLosses", "x402Losses"]) {
|
|
43
|
+
for (const name of ["credits", "fundings", "invoices", "tempoLosses", "x402Losses", "refunds"]) {
|
|
39
44
|
const target = fork[name];
|
|
40
45
|
for (const [key, value] of this[name]) target.set(key, structuredClone(value));
|
|
41
46
|
}
|
|
@@ -54,6 +59,90 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
54
59
|
if (wedged) throw x402SettlementPending(args.creditId ?? randomUUID(), wedged);
|
|
55
60
|
return run(() => this.fundSync(args));
|
|
56
61
|
}
|
|
62
|
+
/** Development accounting parity; this non-durable store cannot mount admin refunds. */
|
|
63
|
+
refundDraw(args) {
|
|
64
|
+
return run(() => {
|
|
65
|
+
const request = refundRequest(args);
|
|
66
|
+
const prior = this.refunds.get(args.refundId);
|
|
67
|
+
if (prior) {
|
|
68
|
+
if (prior.request !== request) throw new CreditLedgerError("refund_conflict", "This refundId was recorded with different arguments.");
|
|
69
|
+
return { ...prior.result, replayed: true };
|
|
70
|
+
}
|
|
71
|
+
const matches = [...this.credits.values()].flatMap((credit2) => [...credit2.draws.values()].filter((draw2) => args.jobId !== void 0 ? draw2.jobId === args.jobId : credit2.creditId === args.creditId && draw2.drawId === args.drawId).map((draw2) => ({ credit: credit2, draw: draw2 })));
|
|
72
|
+
if (matches.length !== 1 || !matches[0].draw.jobId) {
|
|
73
|
+
throw new CreditLedgerError("draw_not_found", "No unique job-bound draw matches this selector.");
|
|
74
|
+
}
|
|
75
|
+
const { credit, draw } = matches[0];
|
|
76
|
+
if (draw.status !== "settled") throw new CreditLedgerError("draw_not_completed", "Only a settled completed draw can be refunded.");
|
|
77
|
+
const nowMs = args.nowMs ?? Date.now();
|
|
78
|
+
const lastDrain = [...credit.drains.values()].filter((drain) => drain.status !== "released").sort((a, b) => b.ledgerSeq - a.ledgerSeq)[0];
|
|
79
|
+
const drained = lastDrain && lastDrain.balanceAfterMicro === 0 && (credit.lastFundingLedgerSeq === null || credit.lastFundingLedgerSeq < lastDrain.ledgerSeq) && ![...credit.draws.values()].some((later) => later.ledgerSeq > lastDrain.ledgerSeq && later.amountMicro > 0) && ![...this.fundings.values()].some((funding) => funding.creditId === credit.creditId && funding.ledgerSeq !== null && funding.ledgerSeq >= lastDrain.ledgerSeq && funding.amountMicro > 0);
|
|
80
|
+
if (credit.status !== "active" || credit.expiryMs <= nowMs) {
|
|
81
|
+
throw new CreditLedgerError("credit_not_open", "The original credit is no longer open for refunds.");
|
|
82
|
+
}
|
|
83
|
+
if (credit.tempoChannelId || credit.x402ChannelId) {
|
|
84
|
+
throw new CreditLedgerError("credit_refund_channel_unsupported", "Channel collateral cannot back a refund of settled spending.");
|
|
85
|
+
}
|
|
86
|
+
if (credit.balanceMicro === 0 && drained) {
|
|
87
|
+
if (credit.lastFundingLedgerSeq === null) {
|
|
88
|
+
throw new CreditLedgerError("credit_refund_state_unavailable", "The original credit's historical funding and drain order cannot be established.");
|
|
89
|
+
}
|
|
90
|
+
throw new CreditLedgerError("credit_not_open", "The original credit is no longer open for refunds.");
|
|
91
|
+
}
|
|
92
|
+
const before = [...this.refunds.values()].reduce((sum, refund) => sum + (refund.result.creditId === credit.creditId && refund.result.drawId === draw.drawId ? refund.result.amountMicro : 0), 0);
|
|
93
|
+
const amountMicro = args.amountMicro ?? draw.amountMicro - before;
|
|
94
|
+
if (amountMicro <= 0 || amountMicro > draw.amountMicro - before) {
|
|
95
|
+
throw new CreditLedgerError("refund_exceeds_draw", "The refund exceeds the draw's remaining unrefunded charge.");
|
|
96
|
+
}
|
|
97
|
+
if (!isFundingRail(credit.rail)) throw new CreditLedgerError("credit_refund_basis_unavailable", "The original funding rail is not recorded on this older credit.");
|
|
98
|
+
const bitcoin = isNonChannelBitcoinRail(credit.rail);
|
|
99
|
+
const originalSats = draw.settledSats ?? null;
|
|
100
|
+
if (bitcoin && originalSats === null) throw new CreditLedgerError("credit_refund_basis_unavailable", "The original Bitcoin value is not provable from this older draw.");
|
|
101
|
+
const after = before + amountMicro;
|
|
102
|
+
const restoredSats = bitcoin ? refundAssetDelta(originalSats, draw.amountMicro, before, after) : null;
|
|
103
|
+
const restoredMsats = restoredSats === null ? refundAssetDelta(draw.drawMsats, draw.amountMicro, before, after) : restoredSats * 1e3;
|
|
104
|
+
const restoredNative = draw.drawNative === null ? null : refundAssetDelta(draw.drawNative, draw.amountMicro, before, after);
|
|
105
|
+
const balanceAfterMicro = credit.balanceMicro + amountMicro;
|
|
106
|
+
const ledgerSeq = credit.lastLedgerSeq + 1;
|
|
107
|
+
if (![
|
|
108
|
+
balanceAfterMicro,
|
|
109
|
+
ledgerSeq,
|
|
110
|
+
credit.msatsRemaining + restoredMsats,
|
|
111
|
+
(credit.nativeRemaining ?? 0) + (restoredNative ?? 0)
|
|
112
|
+
].every(Number.isSafeInteger)) {
|
|
113
|
+
throw new Error("credit ledger: refund exceeds safe integer range");
|
|
114
|
+
}
|
|
115
|
+
credit.balanceMicro = balanceAfterMicro;
|
|
116
|
+
credit.lastLedgerSeq = ledgerSeq;
|
|
117
|
+
credit.msatsRemaining += restoredMsats;
|
|
118
|
+
if (credit.nativeRemaining !== null) credit.nativeRemaining += restoredNative ?? 0;
|
|
119
|
+
if (bitcoin) credit.lots.push({
|
|
120
|
+
lotId: `lot:refund:${randomUUID()}`,
|
|
121
|
+
creditId: credit.creditId,
|
|
122
|
+
rail: credit.rail,
|
|
123
|
+
satsFunded: restoredSats,
|
|
124
|
+
satsBasisKnown: true,
|
|
125
|
+
creditedMicro: amountMicro,
|
|
126
|
+
remainingMicro: amountMicro,
|
|
127
|
+
fundingRef: args.refundId,
|
|
128
|
+
createdAt: nowMs
|
|
129
|
+
});
|
|
130
|
+
const result = {
|
|
131
|
+
refundId: args.refundId,
|
|
132
|
+
creditId: credit.creditId,
|
|
133
|
+
drawId: draw.drawId,
|
|
134
|
+
jobId: draw.jobId,
|
|
135
|
+
amountMicro,
|
|
136
|
+
currency: credit.currency,
|
|
137
|
+
ledgerSeq,
|
|
138
|
+
refundedAt: nowMs,
|
|
139
|
+
balanceAfterMicro,
|
|
140
|
+
replayed: false
|
|
141
|
+
};
|
|
142
|
+
this.refunds.set(args.refundId, { request, result });
|
|
143
|
+
return { ...result };
|
|
144
|
+
});
|
|
145
|
+
}
|
|
57
146
|
async draw(args) {
|
|
58
147
|
const blocking = await this.blockingRefundForCredit(
|
|
59
148
|
args.creditId,
|
|
@@ -427,6 +516,7 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
427
516
|
}
|
|
428
517
|
);
|
|
429
518
|
}
|
|
519
|
+
existing.lastFundingLedgerSeq = existing.lastLedgerSeq;
|
|
430
520
|
existing.balanceMicro += args.amountMicro;
|
|
431
521
|
existing.expiryMs = args.expiryMs;
|
|
432
522
|
applyBasis(existing, b);
|
|
@@ -457,6 +547,7 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
457
547
|
expiryMs: args.expiryMs,
|
|
458
548
|
status: "active",
|
|
459
549
|
lastLedgerSeq: 0,
|
|
550
|
+
lastFundingLedgerSeq: 0,
|
|
460
551
|
createdAt: nowMs,
|
|
461
552
|
draws: /* @__PURE__ */ new Map(),
|
|
462
553
|
rail: null,
|
|
@@ -687,6 +778,15 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
687
778
|
return run(() => {
|
|
688
779
|
const channelId = evidence.channelId.toLowerCase();
|
|
689
780
|
const observedAt = evidence.observedAt ?? Date.now();
|
|
781
|
+
const refunded = evidence.refundedToPayerNative ?? 0n;
|
|
782
|
+
if (refunded < 0n) throw new Error("Tempo close refund must be nonnegative");
|
|
783
|
+
const payerExit = evidence.closeKind === "payer_exit";
|
|
784
|
+
if (payerExit && evidence.refundedToPayerNative !== void 0 || evidence.closeKind === "cooperative_close" && evidence.refundedToPayerNative === void 0) {
|
|
785
|
+
throw new Error("Tempo close kind requires matching exact cooperative refund evidence");
|
|
786
|
+
}
|
|
787
|
+
const lossNative = (payerExit ? evidence.consumedNative : evidence.highestVoucherNative) - evidence.settledOnChainNative - (payerExit ? 0n : refunded);
|
|
788
|
+
const lossBasis = payerExit ? "payer_exit" : evidence.refundedToPayerNative === void 0 ? "caller_asserted" : "cooperative_close";
|
|
789
|
+
if (lossNative <= 0n) return [...this.tempoLosses.values()].filter((loss) => loss.channelId === channelId);
|
|
690
790
|
for (const credit of this.credits.values()) {
|
|
691
791
|
if (credit.tempoChannelId !== channelId) continue;
|
|
692
792
|
if (credit.status === "unbacked") continue;
|
|
@@ -701,6 +801,9 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
701
801
|
highestVoucherNative: evidence.highestVoucherNative,
|
|
702
802
|
consumedServiceMicro,
|
|
703
803
|
consumedNative: evidence.consumedNative,
|
|
804
|
+
refundedToPayerNative: evidence.refundedToPayerNative ?? null,
|
|
805
|
+
lossNative,
|
|
806
|
+
lossBasis,
|
|
704
807
|
observedAt
|
|
705
808
|
});
|
|
706
809
|
credit.status = "unbacked";
|
|
@@ -708,9 +811,9 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
708
811
|
return [...this.tempoLosses.values()].filter((loss) => loss.channelId === channelId);
|
|
709
812
|
});
|
|
710
813
|
}
|
|
711
|
-
listTempoCreditLosses(args) {
|
|
814
|
+
listTempoCreditLosses(args, _tx) {
|
|
712
815
|
return Promise.resolve(
|
|
713
|
-
[...this.tempoLosses.values()].sort((a, b) => b.observedAt - a.observedAt || a.creditId.localeCompare(b.creditId)).slice(0, args?.limit ?? 50)
|
|
816
|
+
[...this.tempoLosses.values()].filter((loss) => !args?.channelId || loss.channelId === args.channelId.toLowerCase()).sort((a, b) => b.observedAt - a.observedAt || a.creditId.localeCompare(b.creditId)).slice(0, args?.limit ?? 50)
|
|
714
817
|
);
|
|
715
818
|
}
|
|
716
819
|
terminalizeX402Credit(evidence, _tx) {
|
|
@@ -841,7 +944,9 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
841
944
|
sentAt: null,
|
|
842
945
|
releasedAt: null,
|
|
843
946
|
writeOffNote: null,
|
|
844
|
-
writtenOffAt: null
|
|
947
|
+
writtenOffAt: null,
|
|
948
|
+
provenRefundNative: null,
|
|
949
|
+
refundPaymentProven: false
|
|
845
950
|
};
|
|
846
951
|
applyLotDebits(credit, drain.lotDebits, -1);
|
|
847
952
|
credit.drains.set(args.drainId, drain);
|
|
@@ -874,7 +979,7 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
874
979
|
const lots = openLots(credit);
|
|
875
980
|
const covered = lots.reduce((sum, lot) => sum + lot.remainingMicro, 0);
|
|
876
981
|
const basisless = lots.reduce(
|
|
877
|
-
(sum, lot) => sum + (lot.satsFunded === 0 ? lot.remainingMicro : 0),
|
|
982
|
+
(sum, lot) => sum + (lot.satsFunded === 0 && !lot.satsBasisKnown ? lot.remainingMicro : 0),
|
|
878
983
|
0
|
|
879
984
|
);
|
|
880
985
|
const inKind = covered >= credit.balanceMicro && basisless === 0;
|
|
@@ -947,7 +1052,7 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
947
1052
|
for (const drain of credit.drains.values()) {
|
|
948
1053
|
if (drain.status !== "pending" && drain.status !== "exit_required" || drain.method !== "tempo") continue;
|
|
949
1054
|
if (drain.createdAt > args.createdBeforeMs) continue;
|
|
950
|
-
if (!args.includeResolved && drain.writtenOffAt !== null) continue;
|
|
1055
|
+
if (!args.includeResolved && !args.includeWrittenOff && drain.writtenOffAt !== null) continue;
|
|
951
1056
|
out.push(drainRecord(drain, credit));
|
|
952
1057
|
}
|
|
953
1058
|
}
|
|
@@ -992,6 +1097,25 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
992
1097
|
return { drain: drainRecord(drain, credit), replayed: false };
|
|
993
1098
|
});
|
|
994
1099
|
}
|
|
1100
|
+
recordTempoDrainRefund(args) {
|
|
1101
|
+
const amount = String(args.refundedNative);
|
|
1102
|
+
if (!/^\d{1,78}$/.test(amount)) return Promise.reject(new CreditLedgerError("invalid_basis", "Tempo refund evidence must be a bounded non-negative integer."));
|
|
1103
|
+
return this.recordTempoDrainRefundEvidence(args, amount);
|
|
1104
|
+
}
|
|
1105
|
+
recordTempoDrainPayment(args) {
|
|
1106
|
+
return this.recordTempoDrainRefundEvidence(args, null);
|
|
1107
|
+
}
|
|
1108
|
+
recordTempoDrainRefundEvidence(args, amount) {
|
|
1109
|
+
return run(() => {
|
|
1110
|
+
const credit = this.credits.get(args.creditId);
|
|
1111
|
+
const drain = credit?.drains.get(args.drainId);
|
|
1112
|
+
if (!credit?.tempoChannelId || !drain || drain.method !== "tempo" || drain.status !== "pending" && drain.status !== "exit_required") return;
|
|
1113
|
+
if (amount !== null && (drain.provenRefundNative === null || BigInt(amount) > BigInt(drain.provenRefundNative))) {
|
|
1114
|
+
drain.provenRefundNative = amount;
|
|
1115
|
+
}
|
|
1116
|
+
drain.refundPaymentProven ||= amount === null || BigInt(amount) > 0n;
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
995
1119
|
releaseDrain(args) {
|
|
996
1120
|
return run(() => {
|
|
997
1121
|
const { credit, drain } = this.requireDrain(args.creditId, args.drainId);
|
|
@@ -1115,6 +1239,7 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
|
|
|
1115
1239
|
if (isNonChannelBitcoinRail(credit.rail)) {
|
|
1116
1240
|
const depletion = depleteLots(openLots(credit), draw.amountMicro);
|
|
1117
1241
|
const settledMsats = inKindDrawMsats(depletion, draw.amountMicro);
|
|
1242
|
+
draw.settledSats = settledMsats === null ? null : settledMsats / 1e3;
|
|
1118
1243
|
if (settledMsats !== null && settledMsats !== draw.drawMsats) {
|
|
1119
1244
|
draw.allocatedMsats = draw.drawMsats;
|
|
1120
1245
|
draw.drawMsats = settledMsats;
|
|
@@ -1295,7 +1420,9 @@ function drainRecord(drain, credit) {
|
|
|
1295
1420
|
sentAt: drain.sentAt,
|
|
1296
1421
|
releasedAt: drain.releasedAt,
|
|
1297
1422
|
writeOffNote: drain.writeOffNote,
|
|
1298
|
-
writtenOffAt: drain.writtenOffAt
|
|
1423
|
+
writtenOffAt: drain.writtenOffAt,
|
|
1424
|
+
provenRefundNative: drain.provenRefundNative,
|
|
1425
|
+
refundPaymentProven: drain.refundPaymentProven
|
|
1299
1426
|
};
|
|
1300
1427
|
}
|
|
1301
1428
|
function assertAmount(amountMicro, opts) {
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-COYKJRMM.js";
|
|
4
4
|
import {
|
|
5
5
|
diagnosticConsole
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Z4FOPROB.js";
|
|
7
7
|
|
|
8
8
|
// src/sdk/server/revenue-reporter.ts
|
|
9
9
|
import { randomUUID } from "crypto";
|
|
@@ -13,6 +13,7 @@ var ENDPOINTS = {
|
|
|
13
13
|
cost: "/_internal/job-cost",
|
|
14
14
|
deposit: "/_internal/credit-deposit",
|
|
15
15
|
release: "/_internal/credit-draw-release",
|
|
16
|
+
refund: "/_internal/credit-draw-refund",
|
|
16
17
|
drain: "/_internal/credit-drain",
|
|
17
18
|
payout: "/_internal/payout",
|
|
18
19
|
expiry_release: "/_internal/credit-expiry-release"
|
|
@@ -166,6 +167,10 @@ var RevenueReporter = class _RevenueReporter {
|
|
|
166
167
|
async enqueueDeposit(tx, payload) {
|
|
167
168
|
await this.insertPending(tx, "deposit", payload);
|
|
168
169
|
}
|
|
170
|
+
/** Persist without network delivery before the refund accounting transaction commits. */
|
|
171
|
+
async enqueueCreditDrawRefund(tx, payload) {
|
|
172
|
+
await this.insertPending(tx, "refund", payload);
|
|
173
|
+
}
|
|
169
174
|
/** Join a released draw to the transaction that made the hold terminal. */
|
|
170
175
|
async enqueueCreditDrawRelease(tx, payload) {
|
|
171
176
|
await this.insertPending(tx, "release", payload);
|
|
@@ -620,6 +625,9 @@ var RevenueReporter = class _RevenueReporter {
|
|
|
620
625
|
diagnosticConsole.log(`[revenue-reporter] reported ${kind} ${label}`);
|
|
621
626
|
return;
|
|
622
627
|
}
|
|
628
|
+
if (kind === "refund" && res.status === 409) {
|
|
629
|
+
throw new Error("Original draw revenue has not arrived yet");
|
|
630
|
+
}
|
|
623
631
|
if (res.status === 401) {
|
|
624
632
|
await this.markStale(id, "token_rotated");
|
|
625
633
|
diagnosticConsole.warn(
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
canonicaliseForSigning
|
|
3
|
+
} from "./chunk-H3GGFEE5.js";
|
|
4
|
+
|
|
5
|
+
// src/lib/receipt.ts
|
|
6
|
+
import { schnorr } from "@noble/curves/secp256k1.js";
|
|
7
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
8
|
+
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
|
9
|
+
function computeResultHash(summary, artifactContents) {
|
|
10
|
+
const artifactHashes = artifactContents.map(
|
|
11
|
+
(content) => bytesToHex(sha256(canonicaliseForSigning(content)))
|
|
12
|
+
);
|
|
13
|
+
return bytesToHex(
|
|
14
|
+
sha256(
|
|
15
|
+
canonicaliseForSigning({
|
|
16
|
+
summary: summary ?? null,
|
|
17
|
+
artifact_hashes: artifactHashes
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
function signReceipt(unsigned, secretHex) {
|
|
23
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
24
|
+
const signature = bytesToHex(schnorr.sign(bytes, hexToBytes(secretHex)));
|
|
25
|
+
return { ...unsigned, signature };
|
|
26
|
+
}
|
|
27
|
+
function verifyReceipt(receipt) {
|
|
28
|
+
try {
|
|
29
|
+
const { signature, ...unsigned } = receipt;
|
|
30
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
31
|
+
return schnorr.verify(hexToBytes(signature), bytes, hexToBytes(receipt.receipt_pubkey));
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function isSignedJobReceipt(value) {
|
|
37
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
38
|
+
const r = value;
|
|
39
|
+
const paid = r.paid;
|
|
40
|
+
return r.v === 1 && typeof r.job_id === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.capability === "string" && (r.outcome === "completed" || r.outcome === "failed" || r.outcome === "cancelled") && typeof r.seq === "number" && typeof r.issued_at === "number" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string" && !!paid && typeof paid === "object" && !Array.isArray(paid) && typeof paid.msats === "number";
|
|
41
|
+
}
|
|
42
|
+
function signDrainReceipt(unsigned, secretHex) {
|
|
43
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
44
|
+
const signature = bytesToHex(schnorr.sign(bytes, hexToBytes(secretHex)));
|
|
45
|
+
return { ...unsigned, signature };
|
|
46
|
+
}
|
|
47
|
+
function signFundingReceipt(unsigned, secretHex) {
|
|
48
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
49
|
+
const signature = bytesToHex(schnorr.sign(bytes, hexToBytes(secretHex)));
|
|
50
|
+
return { ...unsigned, signature };
|
|
51
|
+
}
|
|
52
|
+
function isFundingReceipt(value) {
|
|
53
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
54
|
+
const r = value;
|
|
55
|
+
return r.v === 1 && r.kind === "credit_funding" && typeof r.credit_id === "string" && typeof r.fund_id === "string" && typeof r.funded_micro === "number" && typeof r.balance_after_micro === "number" && typeof r.ledger_seq === "number" && typeof r.issued_at === "number" && typeof r.caller_pubkey === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string";
|
|
56
|
+
}
|
|
57
|
+
function verifyFundingReceipt(receipt) {
|
|
58
|
+
try {
|
|
59
|
+
const { signature, ...unsigned } = receipt;
|
|
60
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
61
|
+
return schnorr.verify(hexToBytes(signature), bytes, hexToBytes(receipt.receipt_pubkey));
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function isDrainReceipt(value) {
|
|
67
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
68
|
+
const r = value;
|
|
69
|
+
return r.v === 1 && r.kind === "credit_drain" && typeof r.credit_id === "string" && typeof r.drain_id === "string" && (r.event === "requested" || r.event === "parked" || r.event === "picked_up" || r.event === "sent" || r.event === "released") && typeof r.method === "string" && typeof r.amount_micro === "number" && typeof r.issued_at === "number" && typeof r.caller_pubkey === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string";
|
|
70
|
+
}
|
|
71
|
+
function verifyDrainReceipt(receipt) {
|
|
72
|
+
try {
|
|
73
|
+
const { signature, ...unsigned } = receipt;
|
|
74
|
+
const bytes = canonicaliseForSigning(unsigned);
|
|
75
|
+
return schnorr.verify(hexToBytes(signature), bytes, hexToBytes(receipt.receipt_pubkey));
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
computeResultHash,
|
|
83
|
+
signReceipt,
|
|
84
|
+
verifyReceipt,
|
|
85
|
+
isSignedJobReceipt,
|
|
86
|
+
signDrainReceipt,
|
|
87
|
+
signFundingReceipt,
|
|
88
|
+
isFundingReceipt,
|
|
89
|
+
verifyFundingReceipt,
|
|
90
|
+
isDrainReceipt,
|
|
91
|
+
verifyDrainReceipt
|
|
92
|
+
};
|