@dvmkit/sdk 0.8.1 → 0.9.0
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-ILBSZMJL.js → chunk-4VV7WACY.js} +1 -1
- package/dist/{chunk-JIZ4ZIXX.js → chunk-73C4JN2E.js} +421 -233
- package/dist/{chunk-ALWVJ2RB.js → chunk-BAQRUYRL.js} +3 -3
- package/dist/{chunk-AVV5SYGO.js → chunk-GAZBBMAA.js} +1 -1
- package/dist/{chunk-G3MPHW5I.js → chunk-GODOH2AF.js} +3 -3
- package/dist/{chunk-2U6LAASN.js → chunk-HC4I7LAV.js} +1 -1
- package/dist/{chunk-7PM4R4GD.js → chunk-IBX2ORBV.js} +1 -1
- package/dist/{chunk-NA2KOR6G.js → chunk-IYQQXEHV.js} +3 -3
- package/dist/{chunk-J5J63WDC.js → chunk-K2MSIKMM.js} +3 -3
- package/dist/{chunk-RSNC5T6C.js → chunk-KGL3YMZA.js} +1 -1
- package/dist/{chunk-UVS7MRJX.js → chunk-LCCG5HQM.js} +2 -2
- package/dist/{chunk-QKE5NDPF.js → chunk-LFVIZH2G.js} +167 -25
- package/dist/{chunk-ZEPJF4JC.js → chunk-LW7KZQHG.js} +4 -4
- package/dist/{chunk-BFGQG4WY.js → chunk-OIP44KBB.js} +5 -0
- package/dist/{chunk-UWGBI5WK.js → chunk-Q3VYRU5R.js} +3 -3
- package/dist/{chunk-IOQIXRGM.js → chunk-QSZW2I5Y.js} +1 -1
- package/dist/{chunk-IHQUBATH.js → chunk-TFDR2YP2.js} +3 -3
- package/dist/{chunk-OP7VCGQR.js → chunk-TRY7BJA4.js} +1 -1
- package/dist/{chunk-NU5RWJ5B.js → chunk-WB3E7KE7.js} +3 -3
- package/dist/{chunk-UDJNZ3UG.js → chunk-WLPLHXNP.js} +1 -1
- package/dist/{chunk-FM6QAGJC.js → chunk-YYRY32NR.js} +24 -8
- package/dist/{chunk-AGE5XZKE.js → chunk-ZBQKXAYK.js} +17 -4
- package/dist/{content-retention-B7ddtJ2y.d.ts → content-retention-CVBYehRS.d.ts} +1 -1
- package/dist/{credit-ledger-KIQIE6I6.js → credit-ledger-CZQ56DYI.js} +4 -4
- package/dist/{credit-menu-BH3g9GQa.d.ts → credit-menu-QntEhZJS.d.ts} +4 -4
- package/dist/{fx-DN26_J7o.d.ts → fx-CMyNFenZ.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -4
- package/dist/internal/caller.d.ts +6 -6
- package/dist/internal/caller.js +16 -16
- package/dist/internal/server.d.ts +7 -7
- package/dist/internal/server.js +26 -26
- package/dist/{job-store-Cq21JT37.d.ts → job-store-qOD-FrQF.d.ts} +1 -1
- package/dist/{lightning-backend-S8F8nf8q.d.ts → lightning-backend-DtdG6UT5.d.ts} +1 -1
- package/dist/{memory-credit-ledger-ATYFAYBG.js → memory-credit-ledger-EMN7G3MO.js} +5 -5
- package/dist/{mpp-setup-ZTORW3VF.js → mpp-setup-VW53KFPO.js} +5 -3
- package/dist/{payout-reporter-NN6K2IVQ.js → payout-reporter-TZX2S7KG.js} +2 -2
- package/dist/{postgres-consumed-credential-store-XJTE5PCA.js → postgres-consumed-credential-store-V464OAJB.js} +3 -3
- package/dist/{postgres-job-store-AR6ZJUQV.js → postgres-job-store-27VOCSPO.js} +2 -2
- package/dist/{postgres-replay-store-33K33QV7.js → postgres-replay-store-7GJFAQXY.js} +2 -2
- package/dist/{pricing-G5IN5YJE.js → pricing-NNMBNP7N.js} +3 -3
- package/dist/{revenue-reporter-KDUZLD5F.js → revenue-reporter-4EAVPWXS.js} +2 -2
- package/dist/server/index.d.ts +14 -10
- package/dist/server/index.js +49 -45
- package/dist/{step-cache-CR12EZ1Q.d.ts → step-cache-B7svmPz3.d.ts} +54 -9
- package/dist/{tempo-charge-store-XEGJYM3G.js → tempo-charge-store-2RPOJBM2.js} +2 -2
- package/dist/{tempo-session-store-3WTDOO7H.js → tempo-session-store-LI3Q3A3U.js} +3 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +7 -7
- package/dist/{usd-s_VZ_eS7.d.ts → usd-D3izFz71.d.ts} +1 -1
- package/dist/{x402-HQ5GRQZU.js → x402-SL36LSOD.js} +4 -4
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
diagnosticConsole
|
|
3
|
-
} from "./chunk-BFGQG4WY.js";
|
|
4
1
|
import {
|
|
5
2
|
withSdkInitLock
|
|
6
3
|
} from "./chunk-COYKJRMM.js";
|
|
4
|
+
import {
|
|
5
|
+
diagnosticConsole
|
|
6
|
+
} from "./chunk-OIP44KBB.js";
|
|
7
7
|
|
|
8
8
|
// src/sdk/server/postgres-replay-store.ts
|
|
9
9
|
var DEFAULT_TABLE_NAME = "signed_request_replays";
|
|
@@ -2,12 +2,12 @@ import {
|
|
|
2
2
|
ContentHoldError,
|
|
3
3
|
applyContentHoldMutation
|
|
4
4
|
} from "./chunk-AFV77S4C.js";
|
|
5
|
-
import {
|
|
6
|
-
diagnosticConsole
|
|
7
|
-
} from "./chunk-BFGQG4WY.js";
|
|
8
5
|
import {
|
|
9
6
|
withSdkInitLock
|
|
10
7
|
} from "./chunk-COYKJRMM.js";
|
|
8
|
+
import {
|
|
9
|
+
diagnosticConsole
|
|
10
|
+
} from "./chunk-OIP44KBB.js";
|
|
11
11
|
|
|
12
12
|
// src/sdk/server/postgres-job-store.ts
|
|
13
13
|
import { createHash } from "crypto";
|
|
@@ -2,12 +2,12 @@ import {
|
|
|
2
2
|
parseStoreValue,
|
|
3
3
|
stringifyStoreValue
|
|
4
4
|
} from "./chunk-YG7G4DPZ.js";
|
|
5
|
-
import {
|
|
6
|
-
diagnosticConsole
|
|
7
|
-
} from "./chunk-BFGQG4WY.js";
|
|
8
5
|
import {
|
|
9
6
|
withSdkInitLock
|
|
10
7
|
} from "./chunk-COYKJRMM.js";
|
|
8
|
+
import {
|
|
9
|
+
diagnosticConsole
|
|
10
|
+
} from "./chunk-OIP44KBB.js";
|
|
11
11
|
|
|
12
12
|
// src/sdk/server/tempo-charge-store.ts
|
|
13
13
|
import { Credential } from "mppx";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
diagnosticConsole
|
|
3
|
-
} from "./chunk-BFGQG4WY.js";
|
|
4
1
|
import {
|
|
5
2
|
withSdkInitLock
|
|
6
3
|
} from "./chunk-COYKJRMM.js";
|
|
4
|
+
import {
|
|
5
|
+
diagnosticConsole
|
|
6
|
+
} from "./chunk-OIP44KBB.js";
|
|
7
7
|
|
|
8
8
|
// src/sdk/server/payout-reporter.ts
|
|
9
9
|
import { createHash, randomUUID } from "crypto";
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
createX402FacilitatorAuthHeaders,
|
|
3
3
|
settleWithFacilitator,
|
|
4
4
|
verifyWithFacilitator
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-GAZBBMAA.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-OIP44KBB.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 }) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loggers
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QSZW2I5Y.js";
|
|
4
4
|
import {
|
|
5
5
|
diagnosticConsole
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OIP44KBB.js";
|
|
7
7
|
|
|
8
8
|
// src/sdk/server/mpp-setup.ts
|
|
9
9
|
import { createHash, randomUUID } from "crypto";
|
|
@@ -313,6 +313,15 @@ function logStateFailure(_error) {
|
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
// src/sdk/server/mpp-setup.ts
|
|
316
|
+
var TempoDrainRefundUnprovenError = class extends Error {
|
|
317
|
+
constructor(reason, message) {
|
|
318
|
+
super(message);
|
|
319
|
+
this.reason = reason;
|
|
320
|
+
}
|
|
321
|
+
reason;
|
|
322
|
+
};
|
|
323
|
+
var TempoCloseDescriptorError = class extends MppxErrors.VerificationFailedError {
|
|
324
|
+
};
|
|
316
325
|
var MPPX_HMAC_MISMATCH_REASON = "challenge was not issued by this server";
|
|
317
326
|
var TEMPO_SETTLEMENT_LEASE_FIELD = "settlementLeaseUntil";
|
|
318
327
|
var TEMPO_SETTLEMENT_LEASE_OWNER_FIELD = "settlementLeaseOwner";
|
|
@@ -717,9 +726,11 @@ var DEFAULT_TEMPO_SESSION_CHAIN_OPS = {
|
|
|
717
726
|
};
|
|
718
727
|
function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
719
728
|
let creditLedger;
|
|
729
|
+
let finalizeDrain;
|
|
720
730
|
Object.assign(mppx, {
|
|
721
|
-
useTempoCreditLedger: (ledger) => {
|
|
731
|
+
useTempoCreditLedger: (ledger, finalizer) => {
|
|
722
732
|
creditLedger = ledger;
|
|
733
|
+
finalizeDrain = finalizer;
|
|
723
734
|
}
|
|
724
735
|
});
|
|
725
736
|
const chainOps = {
|
|
@@ -737,7 +748,7 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
737
748
|
assertTempoSessionCloseVoucher: async (voucher) => {
|
|
738
749
|
const descriptor = readChannelDescriptor(voucher.descriptor);
|
|
739
750
|
if (!descriptor) {
|
|
740
|
-
throw new
|
|
751
|
+
throw new TempoCloseDescriptorError({
|
|
741
752
|
reason: "descriptor required for TIP-1034 session action"
|
|
742
753
|
});
|
|
743
754
|
}
|
|
@@ -755,22 +766,22 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
755
766
|
escrow
|
|
756
767
|
});
|
|
757
768
|
if (computed.toLowerCase() !== voucher.channelId.toLowerCase()) {
|
|
758
|
-
throw new
|
|
769
|
+
throw new TempoCloseDescriptorError({
|
|
759
770
|
reason: "channel descriptor does not match channelId"
|
|
760
771
|
});
|
|
761
772
|
}
|
|
762
773
|
if (!sameHex(descriptor.payee, voucher.request.recipient)) {
|
|
763
|
-
throw new
|
|
774
|
+
throw new TempoCloseDescriptorError({
|
|
764
775
|
reason: "channel descriptor payee does not match server destination"
|
|
765
776
|
});
|
|
766
777
|
}
|
|
767
778
|
if (!sameHex(descriptor.token, voucher.request.currency)) {
|
|
768
|
-
throw new
|
|
779
|
+
throw new TempoCloseDescriptorError({
|
|
769
780
|
reason: "channel descriptor token does not match server token"
|
|
770
781
|
});
|
|
771
782
|
}
|
|
772
783
|
if (!sameHex(descriptor.operator, details.operator ?? ZERO_ADDRESS)) {
|
|
773
|
-
throw new
|
|
784
|
+
throw new TempoCloseDescriptorError({
|
|
774
785
|
reason: "channel descriptor operator does not match server operator"
|
|
775
786
|
});
|
|
776
787
|
}
|
|
@@ -784,7 +795,7 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
784
795
|
}
|
|
785
796
|
const storedDescriptor = readChannelDescriptor(stored.descriptor);
|
|
786
797
|
if (!storedDescriptor || !sameDescriptor(descriptor, storedDescriptor)) {
|
|
787
|
-
throw new
|
|
798
|
+
throw new TempoCloseDescriptorError({
|
|
788
799
|
reason: "credential descriptor does not match stored channel"
|
|
789
800
|
});
|
|
790
801
|
}
|
|
@@ -886,6 +897,9 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
886
897
|
value: {
|
|
887
898
|
...value,
|
|
888
899
|
finalized: active.finalized || chainFinalized,
|
|
900
|
+
...chainFinalized && !active.finalized && {
|
|
901
|
+
depositBeforeFinalization: active.deposit
|
|
902
|
+
},
|
|
889
903
|
closeRequestedAt: chainFinalized ? 0n : BigInt(chainState.closeRequestedAt),
|
|
890
904
|
deposit: chainFinalized ? 0n : maxBigInt(active.deposit, chainState.deposit),
|
|
891
905
|
settledOnChain: maxBigInt(active.settledOnChain, chainState.settled)
|
|
@@ -914,6 +928,9 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
914
928
|
value: {
|
|
915
929
|
...current,
|
|
916
930
|
finalized: active.finalized || chainFinalized,
|
|
931
|
+
...chainFinalized && !active.finalized && {
|
|
932
|
+
depositBeforeFinalization: active.deposit
|
|
933
|
+
},
|
|
917
934
|
closeRequestedAt: chainFinalized ? 0n : BigInt(chainState.closeRequestedAt),
|
|
918
935
|
deposit: chainFinalized ? 0n : maxBigInt(active.deposit, chainState.deposit),
|
|
919
936
|
settledOnChain: maxBigInt(active.settledOnChain, chainState.settled)
|
|
@@ -1151,7 +1168,7 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
1151
1168
|
}
|
|
1152
1169
|
}
|
|
1153
1170
|
}
|
|
1154
|
-
await
|
|
1171
|
+
await reconcileTempoSessionCloses();
|
|
1155
1172
|
return { scanned: rows.length, closeRequested, settled, inFlight };
|
|
1156
1173
|
};
|
|
1157
1174
|
const recoverTempoSessionClose = async (channelId, drainId) => {
|
|
@@ -1177,7 +1194,7 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
1177
1194
|
const pending = await config.hostedObligations?.pendingTempoClose(channelId, drainId);
|
|
1178
1195
|
const selfHostedReport = !config.hostedObligations && !!config.onSessionSettlement && !!store.claimCloseReference && !!store.getCloseRecovery;
|
|
1179
1196
|
let reference = pending?.txHash ?? void 0;
|
|
1180
|
-
if (pending && !reference || selfHostedReport) {
|
|
1197
|
+
if (pending && pending.state !== "unmatched" && !reference || selfHostedReport) {
|
|
1181
1198
|
const recovery = await store.getCloseRecovery?.(channelId);
|
|
1182
1199
|
const notBeforeMs = pending?.reservedAtMs ?? recovery?.updatedAtMs;
|
|
1183
1200
|
if (notBeforeMs === void 0) throw new Error(`Tempo session close intent ${channelId} not found`);
|
|
@@ -1192,15 +1209,18 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
1192
1209
|
if (pending) {
|
|
1193
1210
|
reference = await settleUnsubmittedClose(current.channelId, drainId, pending.id, found);
|
|
1194
1211
|
} else if (found.outcome === "ours") {
|
|
1212
|
+
const client = await getClient({ chainId: current.chainId });
|
|
1213
|
+
const { settledToPayee } = await chainOps.readChannelClose(client, current.channelId, found.txHash);
|
|
1195
1214
|
const claimed = await store.claimCloseReference(channelId, drainId, found.txHash);
|
|
1196
1215
|
if (claimed.claimed) {
|
|
1197
|
-
await reportRecoveredClose(current, found.txHash, recovery?.settledBeforeClose ?? null);
|
|
1216
|
+
await reportRecoveredClose(current, found.txHash, recovery?.settledBeforeClose ?? null, settledToPayee);
|
|
1198
1217
|
}
|
|
1199
1218
|
return claimed.reference;
|
|
1200
1219
|
} else if (found.outcome === "none") {
|
|
1201
1220
|
loggers.tempo.warn("tempo_session_close_recovery_unmatched", {
|
|
1202
1221
|
channel_id: current.channelId,
|
|
1203
|
-
drain_id: drainId
|
|
1222
|
+
drain_id: drainId,
|
|
1223
|
+
amount_at_stake_native: (current.highestVoucherAmount - (recovery?.settledBeforeClose ?? current.settledOnChain)).toString()
|
|
1204
1224
|
});
|
|
1205
1225
|
}
|
|
1206
1226
|
}
|
|
@@ -1234,7 +1254,7 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
1234
1254
|
}
|
|
1235
1255
|
if (search.outcome === "exit_requested") return { outcome: "exit" };
|
|
1236
1256
|
if (search.stoppedAt !== void 0) await cursor.save(search.stoppedAt);
|
|
1237
|
-
return { outcome: "none" };
|
|
1257
|
+
return { outcome: "none", terminal: search.stoppedAt !== void 0 };
|
|
1238
1258
|
};
|
|
1239
1259
|
const settleUnsubmittedClose = async (channelId, drainId, obligationId, found) => {
|
|
1240
1260
|
const obligations = config.hostedObligations;
|
|
@@ -1247,16 +1267,20 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
1247
1267
|
loggers.tempo.warn("tempo_session_close_exit_resolved", {
|
|
1248
1268
|
channel_id: channelId,
|
|
1249
1269
|
drain_id: drainId,
|
|
1250
|
-
obligation_id: obligationId
|
|
1270
|
+
obligation_id: obligationId,
|
|
1271
|
+
amount_at_stake_native: await obligations.tempoCloseAmountAtStake(obligationId)
|
|
1251
1272
|
});
|
|
1252
1273
|
}
|
|
1253
1274
|
return void 0;
|
|
1254
1275
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1276
|
+
if (found.terminal && await obligations.markTempoCloseUnmatched(obligationId)) {
|
|
1277
|
+
loggers.tempo.warn("tempo_session_close_recovery_unmatched", {
|
|
1278
|
+
channel_id: channelId,
|
|
1279
|
+
drain_id: drainId,
|
|
1280
|
+
obligation_id: obligationId,
|
|
1281
|
+
amount_at_stake_native: await obligations.tempoCloseAmountAtStake(obligationId)
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1260
1284
|
return void 0;
|
|
1261
1285
|
};
|
|
1262
1286
|
const reconcileUnsubmittedCloses = async () => {
|
|
@@ -1292,13 +1316,129 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
1292
1316
|
}
|
|
1293
1317
|
}
|
|
1294
1318
|
};
|
|
1295
|
-
const
|
|
1296
|
-
const
|
|
1297
|
-
|
|
1319
|
+
const reconcileFinalizedDrains = async () => {
|
|
1320
|
+
const ledger = creditLedger;
|
|
1321
|
+
if (!ledger || !finalizeDrain) return;
|
|
1322
|
+
let after;
|
|
1323
|
+
do {
|
|
1324
|
+
const page = await ledger.listChannelDrains({
|
|
1325
|
+
limit: 100,
|
|
1326
|
+
after,
|
|
1327
|
+
createdBeforeMs: Date.now(),
|
|
1328
|
+
rail: "tempo",
|
|
1329
|
+
includeResolved: true
|
|
1330
|
+
});
|
|
1331
|
+
for (const drain of page) {
|
|
1332
|
+
if (drain.status !== "exit_required" || !drain.channelId) continue;
|
|
1333
|
+
try {
|
|
1334
|
+
const stored = await store.get(drain.channelId);
|
|
1335
|
+
const current = readTempoSessionState(stored);
|
|
1336
|
+
if (!current) throw new Error("Channel history is unavailable");
|
|
1337
|
+
const client = await getClient({ chainId: current.chainId });
|
|
1338
|
+
const [chainState] = await chainOps.getChannelStatesBatch(client, [current.channelId], current.escrowContract);
|
|
1339
|
+
if (!chainState || chainState.deposit !== 0n) continue;
|
|
1340
|
+
await terminalizeLostBacking(current, chainState);
|
|
1341
|
+
await writeBackChainState(current, chainState);
|
|
1342
|
+
const pending = await config.hostedObligations?.pendingTempoClose(drain.channelId, drain.drainId);
|
|
1343
|
+
if (pending && !pending.txHash) {
|
|
1344
|
+
if (pending.state === "unmatched") throw new TempoDrainRefundUnprovenError("close_history_unmatched", "Close history requires an operator retry");
|
|
1345
|
+
continue;
|
|
1346
|
+
}
|
|
1347
|
+
const recovery = await store.getCloseRecovery?.(drain.channelId);
|
|
1348
|
+
const found = pending?.txHash ? { outcome: "ours", txHash: pending.txHash } : await findFinalizedClose(current, stored, drain.drainId, drain.createdAt, {
|
|
1349
|
+
from: recovery?.searchBefore ?? null,
|
|
1350
|
+
save: (resumeAt) => store.recordCloseSearch?.(drain.channelId, drain.drainId, resumeAt)
|
|
1351
|
+
});
|
|
1352
|
+
let refundedNative;
|
|
1353
|
+
let reference;
|
|
1354
|
+
if (found.outcome === "ours") {
|
|
1355
|
+
reference = found.txHash;
|
|
1356
|
+
const receipt = await chainOps.readChannelClose(client, current.channelId, found.txHash);
|
|
1357
|
+
refundedNative = receipt.refundedToPayer;
|
|
1358
|
+
if (!config.hostedObligations && config.onSessionSettlement && store.claimCloseReference) {
|
|
1359
|
+
const claimed = await store.claimCloseReference(drain.channelId, drain.drainId, found.txHash);
|
|
1360
|
+
if (claimed.claimed) {
|
|
1361
|
+
await reportRecoveredClose(current, found.txHash, recovery?.settledBeforeClose ?? null, receipt.settledToPayee);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
} else if (found.outcome === "exit") {
|
|
1365
|
+
const snapshot = stored;
|
|
1366
|
+
const deposit = typeof snapshot.depositBeforeFinalization === "bigint" ? snapshot.depositBeforeFinalization : current.deposit;
|
|
1367
|
+
if (deposit <= 0n) throw new TempoDrainRefundUnprovenError("collateral_missing", "The pre-exit collateral is unavailable");
|
|
1368
|
+
refundedNative = deposit - maxBigInt(current.settledOnChain, chainState.settled);
|
|
1369
|
+
reference = `channel:${current.channelId}`;
|
|
1370
|
+
} else {
|
|
1371
|
+
throw new TempoDrainRefundUnprovenError("close_evidence_missing", "Channel finalization has no matching refund evidence");
|
|
1372
|
+
}
|
|
1373
|
+
if (drain.drainedNative === null || refundedNative < BigInt(drain.drainedNative)) {
|
|
1374
|
+
throw new TempoDrainRefundUnprovenError("refund_short", "Proven refund is below the drain debit");
|
|
1375
|
+
}
|
|
1376
|
+
const creditUnbacked = (await ledger.get(drain.creditId))?.status === "unbacked";
|
|
1377
|
+
await store.confirmClose?.(drain.channelId, drain.drainId, reference);
|
|
1378
|
+
const changed = await finalizeDrain(drain, {
|
|
1379
|
+
reference,
|
|
1380
|
+
refundedNative,
|
|
1381
|
+
outcome: found.outcome === "ours" ? "cooperative_close" : "payer_exit",
|
|
1382
|
+
creditUnbacked
|
|
1383
|
+
});
|
|
1384
|
+
if (changed) {
|
|
1385
|
+
loggers.tempo.warn(creditUnbacked ? "tempo_session_drain_unbacked_resolved" : "tempo_session_drain_finalized", {
|
|
1386
|
+
channel_id: drain.channelId,
|
|
1387
|
+
credit_id: drain.creditId,
|
|
1388
|
+
drain_id: drain.drainId,
|
|
1389
|
+
amount_at_stake_native: drain.drainedNative.toString(),
|
|
1390
|
+
refund_native: refundedNative.toString(),
|
|
1391
|
+
builder_loss_native: (current.highestVoucherAmount > maxBigInt(current.settledOnChain, chainState.settled) ? current.highestVoucherAmount - maxBigInt(current.settledOnChain, chainState.settled) : 0n).toString()
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
} catch (error) {
|
|
1395
|
+
let failure = error;
|
|
1396
|
+
if (error instanceof TempoDrainRefundUnprovenError) {
|
|
1397
|
+
try {
|
|
1398
|
+
await store.update(drain.channelId, (current) => {
|
|
1399
|
+
if (!isStateRecord(current)) throw new Error("Channel history is unavailable");
|
|
1400
|
+
const key = JSON.stringify([drain.creditId, drain.drainId, error.reason]);
|
|
1401
|
+
const seen = Array.isArray(current.drainRefundWarnings) ? current.drainRefundWarnings : [];
|
|
1402
|
+
if (seen.includes(key)) return { op: "noop", result: false };
|
|
1403
|
+
loggers.tempo.warn("tempo_session_drain_refund_unproven", {
|
|
1404
|
+
channel_id: drain.channelId,
|
|
1405
|
+
credit_id: drain.creditId,
|
|
1406
|
+
drain_id: drain.drainId,
|
|
1407
|
+
amount_at_stake_native: drain.drainedNative?.toString() ?? null,
|
|
1408
|
+
amount_at_stake_micro: drain.amountMicro,
|
|
1409
|
+
currency: drain.currency,
|
|
1410
|
+
reason: error.reason,
|
|
1411
|
+
error: error.message
|
|
1412
|
+
});
|
|
1413
|
+
return { op: "set", value: { ...current, drainRefundWarnings: [...seen, key] }, result: true };
|
|
1414
|
+
});
|
|
1415
|
+
continue;
|
|
1416
|
+
} catch (claimError) {
|
|
1417
|
+
failure = claimError;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
loggers.tempo.warn("tempo_session_close_reconcile_failed", {
|
|
1421
|
+
channel_id: drain.channelId,
|
|
1422
|
+
credit_id: drain.creditId,
|
|
1423
|
+
drain_id: drain.drainId,
|
|
1424
|
+
error: failure instanceof Error ? failure.message : String(failure)
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
const last = page.at(-1);
|
|
1429
|
+
after = page.length === 100 && last ? { createdAt: last.createdAt, creditId: last.creditId, drainId: last.drainId } : void 0;
|
|
1430
|
+
} while (after);
|
|
1431
|
+
};
|
|
1432
|
+
const reconcileTempoSessionCloses = async () => {
|
|
1433
|
+
await reconcileUnsubmittedCloses();
|
|
1434
|
+
await reconcileFinalizedDrains();
|
|
1435
|
+
};
|
|
1436
|
+
const reportRecoveredClose = async (channel, txHash, settledBeforeClose, settledToPayee) => {
|
|
1298
1437
|
if (settledBeforeClose === null || settledToPayee < settledBeforeClose) {
|
|
1299
1438
|
loggers.tempo.warn("tempo_session_close_settlement_unreported", {
|
|
1300
1439
|
channel_id: channel.channelId,
|
|
1301
|
-
tx_hash: txHash
|
|
1440
|
+
tx_hash: txHash,
|
|
1441
|
+
amount_at_stake_native: settledToPayee.toString()
|
|
1302
1442
|
});
|
|
1303
1443
|
return;
|
|
1304
1444
|
}
|
|
@@ -1314,6 +1454,7 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
1314
1454
|
loggers.tempo.warn("tempo_session_close_settlement_callback_failed", {
|
|
1315
1455
|
channel_id: channel.channelId,
|
|
1316
1456
|
tx_hash: txHash,
|
|
1457
|
+
amount_at_stake_native: (settledToPayee - settledBeforeClose).toString(),
|
|
1317
1458
|
error: error instanceof Error ? error.message : String(error)
|
|
1318
1459
|
});
|
|
1319
1460
|
}
|
|
@@ -1326,7 +1467,7 @@ function attachTempoSessionRuntime(mppx, config, chainOpOverrides = {}) {
|
|
|
1326
1467
|
const confirmClose = store.confirmClose?.bind(store);
|
|
1327
1468
|
return Object.assign(mppx, {
|
|
1328
1469
|
sweepTempoSessions,
|
|
1329
|
-
...config?.hostedObligations && { reconcileTempoSessionCloses
|
|
1470
|
+
...config?.hostedObligations && { reconcileTempoSessionCloses },
|
|
1330
1471
|
...beginClose && {
|
|
1331
1472
|
beginTempoSessionClose: async (channelId, drainId) => {
|
|
1332
1473
|
await beginClose(channelId, drainId);
|
|
@@ -1417,6 +1558,7 @@ export {
|
|
|
1417
1558
|
TEMPO_SETTLEMENT_DEFAULT_LOW_BALANCE_MICRO,
|
|
1418
1559
|
TempoSettlementReadiness,
|
|
1419
1560
|
isTempoInsufficientFundsError,
|
|
1561
|
+
TempoCloseDescriptorError,
|
|
1420
1562
|
MPPX_HMAC_MISMATCH_REASON,
|
|
1421
1563
|
TEMPO_SETTLEMENT_LEASE_FIELD,
|
|
1422
1564
|
TEMPO_SETTLEMENT_LEASE_OWNER_FIELD,
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicaliseForSigning
|
|
3
3
|
} from "./chunk-UKD3R2KU.js";
|
|
4
|
-
import {
|
|
5
|
-
callerLoggers
|
|
6
|
-
} from "./chunk-66HGCPBU.js";
|
|
7
4
|
import {
|
|
8
5
|
DvmError
|
|
9
6
|
} from "./chunk-BPYMEMQX.js";
|
|
7
|
+
import {
|
|
8
|
+
callerLoggers
|
|
9
|
+
} from "./chunk-66HGCPBU.js";
|
|
10
10
|
import {
|
|
11
11
|
diagnosticConsole
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OIP44KBB.js";
|
|
13
13
|
|
|
14
14
|
// src/lib/cashu/lock-pubkey.ts
|
|
15
15
|
function toLockPubkey(raw) {
|
|
@@ -418,6 +418,7 @@ var diagnostic_catalogue_v1_default = {
|
|
|
418
418
|
"tempo_session_advertised_refresh_failed",
|
|
419
419
|
"tempo_session_close_attempts_exhausted",
|
|
420
420
|
"tempo_session_close_challenge_failed",
|
|
421
|
+
"tempo_session_close_descriptor_refused",
|
|
421
422
|
"tempo_session_close_exit_resolved",
|
|
422
423
|
"tempo_session_close_failed",
|
|
423
424
|
"tempo_session_close_failure_reconcile_failed",
|
|
@@ -425,9 +426,13 @@ var diagnostic_catalogue_v1_default = {
|
|
|
425
426
|
"tempo_session_close_recovery_failed",
|
|
426
427
|
"tempo_session_close_recovery_search_paused",
|
|
427
428
|
"tempo_session_close_recovery_unmatched",
|
|
429
|
+
"tempo_session_close_report_written_off",
|
|
428
430
|
"tempo_session_close_reservation_released",
|
|
429
431
|
"tempo_session_close_settlement_callback_failed",
|
|
430
432
|
"tempo_session_close_settlement_unreported",
|
|
433
|
+
"tempo_session_drain_finalized",
|
|
434
|
+
"tempo_session_drain_refund_unproven",
|
|
435
|
+
"tempo_session_drain_unbacked_resolved",
|
|
431
436
|
"tempo_session_open_refused",
|
|
432
437
|
"tempo_session_settled",
|
|
433
438
|
"tempo_session_withheld",
|
|
@@ -2,12 +2,12 @@ import {
|
|
|
2
2
|
parseStoreValue,
|
|
3
3
|
stringifyStoreValue
|
|
4
4
|
} from "./chunk-YG7G4DPZ.js";
|
|
5
|
-
import {
|
|
6
|
-
withPostgresTransactionRecovery
|
|
7
|
-
} from "./chunk-7PM4R4GD.js";
|
|
8
5
|
import {
|
|
9
6
|
withSdkInitLock
|
|
10
7
|
} from "./chunk-COYKJRMM.js";
|
|
8
|
+
import {
|
|
9
|
+
withPostgresTransactionRecovery
|
|
10
|
+
} from "./chunk-IBX2ORBV.js";
|
|
11
11
|
|
|
12
12
|
// src/sdk/server/tempo-session-store.ts
|
|
13
13
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
applyContentHoldMutation,
|
|
4
4
|
isTerminal
|
|
5
5
|
} from "./chunk-AFV77S4C.js";
|
|
6
|
-
import {
|
|
7
|
-
loggers
|
|
8
|
-
} from "./chunk-IOQIXRGM.js";
|
|
9
6
|
import {
|
|
10
7
|
redactUrl
|
|
11
8
|
} from "./chunk-FUJ36YDV.js";
|
|
9
|
+
import {
|
|
10
|
+
loggers
|
|
11
|
+
} from "./chunk-QSZW2I5Y.js";
|
|
12
12
|
|
|
13
13
|
// src/sdk/server/job-store.ts
|
|
14
14
|
function accumulateAskedTopUp(prior, ask) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
diagnosticConsole
|
|
3
|
-
} from "./chunk-BFGQG4WY.js";
|
|
4
1
|
import {
|
|
5
2
|
withSdkInitLock
|
|
6
3
|
} from "./chunk-COYKJRMM.js";
|
|
4
|
+
import {
|
|
5
|
+
diagnosticConsole
|
|
6
|
+
} from "./chunk-OIP44KBB.js";
|
|
7
7
|
|
|
8
8
|
// src/sdk/server/revenue-reporter.ts
|
|
9
9
|
import { randomUUID } from "crypto";
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
withSdkInitLock
|
|
3
|
+
} from "./chunk-COYKJRMM.js";
|
|
1
4
|
import {
|
|
2
5
|
applyRetryToPool
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-IBX2ORBV.js";
|
|
4
7
|
import {
|
|
5
8
|
x402NetworkByCaip2
|
|
6
9
|
} from "./chunk-BPYMEMQX.js";
|
|
7
10
|
import {
|
|
8
11
|
diagnosticConsole
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import {
|
|
11
|
-
withSdkInitLock
|
|
12
|
-
} from "./chunk-COYKJRMM.js";
|
|
12
|
+
} from "./chunk-OIP44KBB.js";
|
|
13
13
|
|
|
14
14
|
// src/sdk/server/credit-ledger.ts
|
|
15
15
|
import { randomUUID } from "crypto";
|
|
@@ -3153,7 +3153,7 @@ var CreditLedger = class {
|
|
|
3153
3153
|
const { rows } = await this.pool.query(
|
|
3154
3154
|
`SELECT d.*, ${DRAIN_CREDIT_COLUMNS} FROM credit_drains d
|
|
3155
3155
|
JOIN credits c ON c.credit_id = d.credit_id
|
|
3156
|
-
WHERE d.status
|
|
3156
|
+
WHERE d.status IN ('pending','exit_required') ORDER BY d.created_at, d.credit_id, d.drain_id`
|
|
3157
3157
|
);
|
|
3158
3158
|
return rows.map(drainRecordFromJoinRow);
|
|
3159
3159
|
}
|
|
@@ -3173,7 +3173,7 @@ var CreditLedger = class {
|
|
|
3173
3173
|
const { rows } = await this.pool.query(
|
|
3174
3174
|
`SELECT d.*, ${DRAIN_CREDIT_COLUMNS} FROM credit_drains d
|
|
3175
3175
|
JOIN credits c ON c.credit_id = d.credit_id
|
|
3176
|
-
WHERE d.status
|
|
3176
|
+
WHERE d.status IN ('pending','exit_required')
|
|
3177
3177
|
AND d.method = 'tempo'
|
|
3178
3178
|
AND c.tempo_channel_id IS NOT NULL
|
|
3179
3179
|
AND d.created_at <= $1
|
|
@@ -3225,6 +3225,22 @@ var CreditLedger = class {
|
|
|
3225
3225
|
[args.creditId, args.drainId]
|
|
3226
3226
|
);
|
|
3227
3227
|
}
|
|
3228
|
+
/** Keep the debit and close intent while the caller uses its timed channel exit. */
|
|
3229
|
+
async requireTempoDrainExit(args) {
|
|
3230
|
+
const q = args.tx ?? this.pool;
|
|
3231
|
+
const { rows } = await q.query(
|
|
3232
|
+
`UPDATE credit_drains d SET status='exit_required'
|
|
3233
|
+
FROM credits c WHERE d.credit_id=c.credit_id AND d.credit_id=$1 AND d.drain_id=$2
|
|
3234
|
+
AND d.status='pending' AND d.method='tempo' AND c.tempo_channel_id IS NOT NULL
|
|
3235
|
+
RETURNING d.*`,
|
|
3236
|
+
[args.creditId, args.drainId]
|
|
3237
|
+
);
|
|
3238
|
+
if (rows.length) return { drain: await this.rejoin(q, rows[0]), replayed: false };
|
|
3239
|
+
return this.refuseTransition(q, args.creditId, args.drainId, {
|
|
3240
|
+
idempotentWhen: (row) => row.status === "exit_required" || row.status === "sent",
|
|
3241
|
+
verb: "require timed exit"
|
|
3242
|
+
});
|
|
3243
|
+
}
|
|
3228
3244
|
/**
|
|
3229
3245
|
* Cancel an unfulfilled Tempo cooperative drain and restore the exact
|
|
3230
3246
|
* balance plus rail basis it removed (`pending -> released`). This is only
|
|
@@ -3366,7 +3382,7 @@ var CreditLedger = class {
|
|
|
3366
3382
|
const nowMs = args.nowMs ?? Date.now();
|
|
3367
3383
|
const { rows } = await q.query(
|
|
3368
3384
|
`UPDATE credit_drains SET status = 'sent', sent_ref = $3, sent_at = $4
|
|
3369
|
-
WHERE credit_id = $1 AND drain_id = $2 AND status
|
|
3385
|
+
WHERE credit_id = $1 AND drain_id = $2 AND status IN ('pending','exit_required') AND method <> 'cashu'
|
|
3370
3386
|
RETURNING *`,
|
|
3371
3387
|
[args.creditId, args.drainId, JSON.stringify(args.sentRef), nowMs]
|
|
3372
3388
|
);
|