@gvnrdao/dh-sdk 0.0.306 → 0.0.308
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/browser/dist/browser.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +616 -122
- package/dist/index.mjs +539 -44
- package/dist/modules/diamond-hands-sdk.d.ts +79 -12
- package/dist/utils/stale-spend-invalidate-message.d.ts +46 -0
- package/dist/utils/withdrawal-reconciliation.utils.d.ts +65 -0
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1514,6 +1514,19 @@ var require_pkg_src = __commonJS({
|
|
|
1514
1514
|
publicKey: "0x046da0ad6da7dd4a0063cbeac6f2bdb6a9889c2178b23470833356a8ee5630f7f0eee0db348d9d9aaaadd23eccf0063c8f68ee5e446abe30b882bf1de4c932bc21",
|
|
1515
1515
|
ethAddress: "0xc7e1588Db94Bbe92Ab209B833235144Fc5b97607"
|
|
1516
1516
|
}
|
|
1517
|
+
},
|
|
1518
|
+
btcUtxoInvalidator: {
|
|
1519
|
+
cid: "QmRue1EAjuJwCDTtTH4czdkkmDEfNpT7iudvwtjaRs6AdA",
|
|
1520
|
+
authorizedCidHex: cidToHex(
|
|
1521
|
+
"QmRue1EAjuJwCDTtTH4czdkkmDEfNpT7iudvwtjaRs6AdA"
|
|
1522
|
+
),
|
|
1523
|
+
name: "Btc Utxo Invalidator",
|
|
1524
|
+
description: "Production Btc Utxo Invalidator",
|
|
1525
|
+
version: "1.0.0",
|
|
1526
|
+
deployed: true,
|
|
1527
|
+
deployedAt: 1784801043540,
|
|
1528
|
+
size: 60717,
|
|
1529
|
+
hash: "2ca9736edf3d54bba7445491920cba28429029c32a2f2830cad831a37a3cb310"
|
|
1517
1530
|
}
|
|
1518
1531
|
};
|
|
1519
1532
|
function getDeploymentsForNetwork(network) {
|
|
@@ -2259,7 +2272,7 @@ ${errorReport}`);
|
|
|
2259
2272
|
}
|
|
2260
2273
|
init_debug_logger();
|
|
2261
2274
|
init_session_signature_cache();
|
|
2262
|
-
var
|
|
2275
|
+
var import_ethers17 = __require("ethers");
|
|
2263
2276
|
var EXPIRED_LOAN_MIN_LIQUIDATION_THRESHOLD_BPS = 11e3;
|
|
2264
2277
|
var GRACE_PERIOD_DAYS = 30;
|
|
2265
2278
|
var SATOSHIS_PER_BITCOIN = 100000000n;
|
|
@@ -5089,12 +5102,12 @@ ${auth.clientId}`;
|
|
|
5089
5102
|
}
|
|
5090
5103
|
async function executeVaultSnapshot(params) {
|
|
5091
5104
|
global.ethers = {
|
|
5092
|
-
...
|
|
5105
|
+
...import_ethers17.ethers,
|
|
5093
5106
|
providers: {
|
|
5094
|
-
StaticJsonRpcProvider:
|
|
5107
|
+
StaticJsonRpcProvider: import_ethers17.ethers.JsonRpcProvider
|
|
5095
5108
|
},
|
|
5096
|
-
Contract:
|
|
5097
|
-
utils:
|
|
5109
|
+
Contract: import_ethers17.ethers.Contract,
|
|
5110
|
+
utils: import_ethers17.ethers
|
|
5098
5111
|
// v6 moved utils to top level
|
|
5099
5112
|
};
|
|
5100
5113
|
global.Lit = {
|
|
@@ -5552,7 +5565,7 @@ var init_network_configs = __esm({
|
|
|
5552
5565
|
});
|
|
5553
5566
|
|
|
5554
5567
|
// src/modules/diamond-hands-sdk.ts
|
|
5555
|
-
import { Contract as Contract6, Interface as Interface4, JsonRpcProvider, AbiCoder as AbiCoder2, keccak256 as
|
|
5568
|
+
import { Contract as Contract6, Interface as Interface4, JsonRpcProvider, AbiCoder as AbiCoder2, keccak256 as keccak2566, solidityPackedKeccak256 as solidityPackedKeccak2565, toUtf8Bytes as toUtf8Bytes6, getBytes as getBytes5, zeroPadValue as zeroPadValue5, toBeHex as toBeHex2, parseEther, formatEther, formatUnits, computeAddress, Signature, ZeroHash, ZeroAddress as ZeroAddress3, MaxUint256 } from "ethers";
|
|
5556
5569
|
|
|
5557
5570
|
// src/types/result.ts
|
|
5558
5571
|
function success(value) {
|
|
@@ -6952,10 +6965,203 @@ async function isLaneGuardActiveOnProvider(provider) {
|
|
|
6952
6965
|
}
|
|
6953
6966
|
}
|
|
6954
6967
|
|
|
6955
|
-
// src/utils/
|
|
6956
|
-
|
|
6968
|
+
// src/utils/withdrawal-reconciliation.utils.ts
|
|
6969
|
+
var defaultHttpGetJson = async (url) => {
|
|
6970
|
+
const res = await fetch(url, { headers: { accept: "application/json" } });
|
|
6971
|
+
let body = null;
|
|
6972
|
+
try {
|
|
6973
|
+
body = await res.json();
|
|
6974
|
+
} catch {
|
|
6975
|
+
body = null;
|
|
6976
|
+
}
|
|
6977
|
+
return { status: res.status, body };
|
|
6978
|
+
};
|
|
6979
|
+
async function reconcileAuthorizedSpend(spend, esploraBaseUrl, httpGetJson = defaultHttpGetJson) {
|
|
6980
|
+
const base = esploraBaseUrl.replace(/\/+$/, "");
|
|
6981
|
+
const fundingRes = await httpGetJson(`${base}/tx/${spend.txid}`);
|
|
6982
|
+
if (fundingRes.status === 404) {
|
|
6983
|
+
return {
|
|
6984
|
+
spend,
|
|
6985
|
+
status: "UNFUNDED",
|
|
6986
|
+
reason: `Funding tx ${spend.txid} not found on the network`
|
|
6987
|
+
};
|
|
6988
|
+
}
|
|
6989
|
+
if (fundingRes.status !== 200) {
|
|
6990
|
+
throw new Error(
|
|
6991
|
+
`Withdrawal reconciliation: esplora /tx/${spend.txid} returned HTTP ${fundingRes.status}`
|
|
6992
|
+
);
|
|
6993
|
+
}
|
|
6994
|
+
const funding = fundingRes.body;
|
|
6995
|
+
if (!funding?.status?.confirmed) {
|
|
6996
|
+
return {
|
|
6997
|
+
spend,
|
|
6998
|
+
status: "UNFUNDED",
|
|
6999
|
+
reason: `Funding tx ${spend.txid} is not confirmed yet`
|
|
7000
|
+
};
|
|
7001
|
+
}
|
|
7002
|
+
const output = funding.vout?.[spend.vout];
|
|
7003
|
+
if (!output || typeof output.value !== "number") {
|
|
7004
|
+
return {
|
|
7005
|
+
spend,
|
|
7006
|
+
status: "CORRUPT",
|
|
7007
|
+
reason: `Authorized vout ${spend.vout} does not exist on tx ${spend.txid} (${funding.vout?.length ?? 0} outputs)`
|
|
7008
|
+
};
|
|
7009
|
+
}
|
|
7010
|
+
const onChainOutputValue = output.value;
|
|
7011
|
+
if (onChainOutputValue !== spend.satoshis) {
|
|
7012
|
+
return {
|
|
7013
|
+
spend,
|
|
7014
|
+
status: "CORRUPT",
|
|
7015
|
+
onChainOutputValue,
|
|
7016
|
+
reason: `Authorization declares ${spend.satoshis} sats for ${spend.txid}:${spend.vout} but the on-chain output is ${onChainOutputValue} sats`
|
|
7017
|
+
};
|
|
7018
|
+
}
|
|
7019
|
+
if (spend.targetAmount > onChainOutputValue) {
|
|
7020
|
+
return {
|
|
7021
|
+
spend,
|
|
7022
|
+
status: "CORRUPT",
|
|
7023
|
+
onChainOutputValue,
|
|
7024
|
+
reason: `Authorized targetAmount ${spend.targetAmount} sats exceeds the ${onChainOutputValue}-sat output it is authorized against`
|
|
7025
|
+
};
|
|
7026
|
+
}
|
|
7027
|
+
const outspendRes = await httpGetJson(
|
|
7028
|
+
`${base}/tx/${spend.txid}/outspend/${spend.vout}`
|
|
7029
|
+
);
|
|
7030
|
+
if (outspendRes.status !== 200) {
|
|
7031
|
+
throw new Error(
|
|
7032
|
+
`Withdrawal reconciliation: esplora outspend for ${spend.txid}:${spend.vout} returned HTTP ${outspendRes.status}`
|
|
7033
|
+
);
|
|
7034
|
+
}
|
|
7035
|
+
const outspend = outspendRes.body;
|
|
7036
|
+
if (outspend?.spent) {
|
|
7037
|
+
const spendingTxid = typeof outspend.txid === "string" && outspend.txid ? outspend.txid : null;
|
|
7038
|
+
if (spendingTxid) {
|
|
7039
|
+
const spendingRes = await httpGetJson(`${base}/tx/${spendingTxid}`);
|
|
7040
|
+
if (spendingRes.status === 200) {
|
|
7041
|
+
const spendingTx = spendingRes.body;
|
|
7042
|
+
const includesOutpoint = (spendingTx.vin ?? []).some(
|
|
7043
|
+
(v) => v.txid === spend.txid && Number(v.vout) === spend.vout
|
|
7044
|
+
);
|
|
7045
|
+
if (includesOutpoint) {
|
|
7046
|
+
const paidToTargetSats = (spendingTx.vout ?? []).filter(
|
|
7047
|
+
(o) => (o.scriptpubkey_address ?? "").toLowerCase() === spend.targetAddress.toLowerCase()
|
|
7048
|
+
).reduce((sum, o) => sum + (o.value ?? 0), 0);
|
|
7049
|
+
if (paidToTargetSats > 0) {
|
|
7050
|
+
return {
|
|
7051
|
+
spend,
|
|
7052
|
+
status: "EXECUTED",
|
|
7053
|
+
spendingTxid,
|
|
7054
|
+
paidToTargetSats,
|
|
7055
|
+
onChainOutputValue,
|
|
7056
|
+
reason: `Outpoint spent by ${spendingTxid}, paying ${paidToTargetSats} sats to the authorized target \u2014 withdrawal complete`
|
|
7057
|
+
};
|
|
7058
|
+
}
|
|
7059
|
+
return {
|
|
7060
|
+
spend,
|
|
7061
|
+
status: "SPENT_MISMATCH",
|
|
7062
|
+
spendingTxid,
|
|
7063
|
+
onChainOutputValue,
|
|
7064
|
+
reason: `Outpoint spent by ${spendingTxid} which pays the authorized target nothing \u2014 authorization is unexecutable`
|
|
7065
|
+
};
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
}
|
|
7069
|
+
return {
|
|
7070
|
+
spend,
|
|
7071
|
+
status: "EXECUTABLE",
|
|
7072
|
+
onChainOutputValue,
|
|
7073
|
+
reason: "Esplora claims the outpoint is spent but no spending tx provably includes it (known regtest-faucet artifact) \u2014 treated as unspent"
|
|
7074
|
+
};
|
|
7075
|
+
}
|
|
7076
|
+
return {
|
|
7077
|
+
spend,
|
|
7078
|
+
status: "EXECUTABLE",
|
|
7079
|
+
onChainOutputValue,
|
|
7080
|
+
reason: "Outpoint confirmed, unspent, and coherent with the authorization"
|
|
7081
|
+
};
|
|
7082
|
+
}
|
|
7083
|
+
|
|
7084
|
+
// src/utils/stale-spend-invalidate-message.ts
|
|
7085
|
+
import {
|
|
7086
|
+
keccak256 as keccak2563,
|
|
7087
|
+
toUtf8Bytes as toUtf8Bytes3,
|
|
7088
|
+
solidityPackedKeccak256 as solidityPackedKeccak2563,
|
|
7089
|
+
getBytes as getBytes3,
|
|
7090
|
+
zeroPadValue
|
|
7091
|
+
} from "ethers";
|
|
7092
|
+
var RECOVERY_INVALIDATE_ACTION = "btc-utxo-invalidator";
|
|
6957
7093
|
var QUANTUM_SECONDS = 60;
|
|
6958
7094
|
var QUANTUM_SAFE_THRESHOLD = 40;
|
|
7095
|
+
function buildInvalidateMessageHash(params) {
|
|
7096
|
+
const positionIdBytes32 = zeroPadValue(
|
|
7097
|
+
params.positionId.startsWith("0x") ? params.positionId : `0x${params.positionId}`,
|
|
7098
|
+
32
|
|
7099
|
+
);
|
|
7100
|
+
const actionHash = keccak2563(toUtf8Bytes3(RECOVERY_INVALIDATE_ACTION));
|
|
7101
|
+
return solidityPackedKeccak2563(
|
|
7102
|
+
[
|
|
7103
|
+
"bytes32",
|
|
7104
|
+
// positionId
|
|
7105
|
+
"uint256",
|
|
7106
|
+
// timestamp
|
|
7107
|
+
"uint256",
|
|
7108
|
+
// chainId
|
|
7109
|
+
"string",
|
|
7110
|
+
// utxoTxid (display order)
|
|
7111
|
+
"uint32",
|
|
7112
|
+
// utxoVout
|
|
7113
|
+
"bytes32"
|
|
7114
|
+
// actionHash
|
|
7115
|
+
],
|
|
7116
|
+
[
|
|
7117
|
+
positionIdBytes32,
|
|
7118
|
+
params.timestamp,
|
|
7119
|
+
params.chainId,
|
|
7120
|
+
params.utxoTxid,
|
|
7121
|
+
params.utxoVout,
|
|
7122
|
+
actionHash
|
|
7123
|
+
]
|
|
7124
|
+
);
|
|
7125
|
+
}
|
|
7126
|
+
async function buildInvalidateStaleSpendEnvelope(params) {
|
|
7127
|
+
const { positionId, utxoTxid, utxoVout, chainId, signer } = params;
|
|
7128
|
+
const nowSec = Date.now() / 1e3;
|
|
7129
|
+
const currentQuantumStart = Math.floor(nowSec / QUANTUM_SECONDS) * QUANTUM_SECONDS;
|
|
7130
|
+
const secsIntoQuantum = nowSec - currentQuantumStart;
|
|
7131
|
+
let timestamp;
|
|
7132
|
+
if (secsIntoQuantum > QUANTUM_SAFE_THRESHOLD) {
|
|
7133
|
+
const nextQuantumStart = currentQuantumStart + QUANTUM_SECONDS;
|
|
7134
|
+
const waitMs = Math.max(0, (nextQuantumStart - nowSec) * 1e3 + 500);
|
|
7135
|
+
await new Promise((r) => setTimeout(r, waitMs));
|
|
7136
|
+
timestamp = nextQuantumStart;
|
|
7137
|
+
} else {
|
|
7138
|
+
timestamp = currentQuantumStart;
|
|
7139
|
+
}
|
|
7140
|
+
const messageHash = buildInvalidateMessageHash({
|
|
7141
|
+
positionId,
|
|
7142
|
+
timestamp,
|
|
7143
|
+
chainId,
|
|
7144
|
+
utxoTxid,
|
|
7145
|
+
utxoVout
|
|
7146
|
+
});
|
|
7147
|
+
const signature = await signer.signMessage(getBytes3(messageHash));
|
|
7148
|
+
const borrowerAddress = await signer.getAddress();
|
|
7149
|
+
return {
|
|
7150
|
+
positionId,
|
|
7151
|
+
utxoTxid,
|
|
7152
|
+
utxoVout,
|
|
7153
|
+
timestamp,
|
|
7154
|
+
chainId,
|
|
7155
|
+
action: RECOVERY_INVALIDATE_ACTION,
|
|
7156
|
+
signature,
|
|
7157
|
+
borrowerAddress
|
|
7158
|
+
};
|
|
7159
|
+
}
|
|
7160
|
+
|
|
7161
|
+
// src/utils/btc-withdrawal-message.ts
|
|
7162
|
+
import { keccak256 as keccak2564, toUtf8Bytes as toUtf8Bytes4, solidityPackedKeccak256 as solidityPackedKeccak2564, getBytes as getBytes4, zeroPadValue as zeroPadValue2 } from "ethers";
|
|
7163
|
+
var QUANTUM_SECONDS2 = 60;
|
|
7164
|
+
var QUANTUM_SAFE_THRESHOLD2 = 40;
|
|
6959
7165
|
function hashBtcInputSet(utxos) {
|
|
6960
7166
|
if (!Array.isArray(utxos) || utxos.length === 0) {
|
|
6961
7167
|
throw new Error("hashBtcInputSet: at least one input is required");
|
|
@@ -6984,7 +7190,7 @@ function hashBtcInputSet(utxos) {
|
|
|
6984
7190
|
seen.add(key);
|
|
6985
7191
|
}
|
|
6986
7192
|
const canonical = normalized.sort((a, b) => a.txid < b.txid ? -1 : a.txid > b.txid ? 1 : a.vout - b.vout).map((u) => `${u.txid}:${u.vout}:${u.value}`).join("|");
|
|
6987
|
-
return
|
|
7193
|
+
return keccak2564(toUtf8Bytes4(canonical));
|
|
6988
7194
|
}
|
|
6989
7195
|
async function buildBtcExecuteEnvelope(params) {
|
|
6990
7196
|
const {
|
|
@@ -7018,26 +7224,26 @@ async function buildBtcExecuteEnvelope(params) {
|
|
|
7018
7224
|
}
|
|
7019
7225
|
const utxosHash = hashBtcInputSet(utxos);
|
|
7020
7226
|
const nowSec = Date.now() / 1e3;
|
|
7021
|
-
const currentQuantumStart = Math.floor(nowSec /
|
|
7227
|
+
const currentQuantumStart = Math.floor(nowSec / QUANTUM_SECONDS2) * QUANTUM_SECONDS2;
|
|
7022
7228
|
const secsIntoQuantum = nowSec - currentQuantumStart;
|
|
7023
7229
|
let timestamp;
|
|
7024
|
-
if (secsIntoQuantum >
|
|
7025
|
-
const nextQuantumStart = currentQuantumStart +
|
|
7230
|
+
if (secsIntoQuantum > QUANTUM_SAFE_THRESHOLD2) {
|
|
7231
|
+
const nextQuantumStart = currentQuantumStart + QUANTUM_SECONDS2;
|
|
7026
7232
|
const waitMs = Math.max(0, (nextQuantumStart - nowSec) * 1e3 + 500);
|
|
7027
7233
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
7028
7234
|
timestamp = nextQuantumStart;
|
|
7029
7235
|
} else {
|
|
7030
7236
|
timestamp = currentQuantumStart;
|
|
7031
7237
|
}
|
|
7032
|
-
const actionHash =
|
|
7033
|
-
|
|
7238
|
+
const actionHash = keccak2564(
|
|
7239
|
+
toUtf8Bytes4("execute-btc-withdrawal")
|
|
7034
7240
|
);
|
|
7035
7241
|
const utxoIdentifier = `${txid}:${vout}`;
|
|
7036
|
-
const positionIdBytes32 =
|
|
7242
|
+
const positionIdBytes32 = zeroPadValue2(
|
|
7037
7243
|
positionId.startsWith("0x") ? positionId : `0x${positionId}`,
|
|
7038
7244
|
32
|
|
7039
7245
|
);
|
|
7040
|
-
const messageHash =
|
|
7246
|
+
const messageHash = solidityPackedKeccak2564(
|
|
7041
7247
|
[
|
|
7042
7248
|
"bytes32",
|
|
7043
7249
|
// positionId
|
|
@@ -7070,7 +7276,7 @@ async function buildBtcExecuteEnvelope(params) {
|
|
|
7070
7276
|
actionHash
|
|
7071
7277
|
]
|
|
7072
7278
|
);
|
|
7073
|
-
const userSignature = await signer.signMessage(
|
|
7279
|
+
const userSignature = await signer.signMessage(getBytes4(messageHash));
|
|
7074
7280
|
const borrowerAddress = await signer.getAddress();
|
|
7075
7281
|
return {
|
|
7076
7282
|
positionId,
|
|
@@ -7134,7 +7340,7 @@ function maybeTemperSignature(signature, shouldTemper, operationName) {
|
|
|
7134
7340
|
}
|
|
7135
7341
|
|
|
7136
7342
|
// src/utils/address-conversion.utils.ts
|
|
7137
|
-
import { zeroPadValue as
|
|
7343
|
+
import { zeroPadValue as zeroPadValue3 } from "ethers";
|
|
7138
7344
|
|
|
7139
7345
|
// src/utils/chunks/bitcoin-utils.ts
|
|
7140
7346
|
import { sha256 as nobleSha256 } from "@noble/hashes/sha256";
|
|
@@ -8718,7 +8924,7 @@ function createPKPManager(config) {
|
|
|
8718
8924
|
}
|
|
8719
8925
|
|
|
8720
8926
|
// src/modules/loan/loan-creator.module.ts
|
|
8721
|
-
import { Interface as Interface2, zeroPadValue as
|
|
8927
|
+
import { Interface as Interface2, zeroPadValue as zeroPadValue4, toBeHex, SigningKey } from "ethers";
|
|
8722
8928
|
var import_dh_lit_actions = __toESM(require_pkg_src());
|
|
8723
8929
|
var LoanCreator = class {
|
|
8724
8930
|
config;
|
|
@@ -9048,7 +9254,7 @@ var LoanCreator = class {
|
|
|
9048
9254
|
try {
|
|
9049
9255
|
const mainnetVaultAddress = bitcoinAddresses.mainnet;
|
|
9050
9256
|
const regtestVaultAddress = bitcoinAddresses.regtest;
|
|
9051
|
-
const pkpIdBytes32 =
|
|
9257
|
+
const pkpIdBytes32 = zeroPadValue4(
|
|
9052
9258
|
toBeHex(BigInt(pkpData.tokenId)),
|
|
9053
9259
|
32
|
|
9054
9260
|
);
|
|
@@ -9229,7 +9435,7 @@ var LoanCreator = class {
|
|
|
9229
9435
|
}
|
|
9230
9436
|
}
|
|
9231
9437
|
if (this.config.debug) {
|
|
9232
|
-
const pkpIdBytes32Check =
|
|
9438
|
+
const pkpIdBytes32Check = zeroPadValue4(
|
|
9233
9439
|
toBeHex(BigInt(pkpData.tokenId)),
|
|
9234
9440
|
32
|
|
9235
9441
|
);
|
|
@@ -9992,7 +10198,7 @@ function createLoanQuery(config) {
|
|
|
9992
10198
|
}
|
|
9993
10199
|
|
|
9994
10200
|
// src/modules/withdrawal-address/withdrawal-address.module.ts
|
|
9995
|
-
import { keccak256 as
|
|
10201
|
+
import { keccak256 as keccak2565, toUtf8Bytes as toUtf8Bytes5, ZeroAddress as ZeroAddress2 } from "ethers";
|
|
9996
10202
|
var WithdrawalAddressModule = class {
|
|
9997
10203
|
config;
|
|
9998
10204
|
graphClient;
|
|
@@ -10027,7 +10233,7 @@ var WithdrawalAddressModule = class {
|
|
|
10027
10233
|
}
|
|
10028
10234
|
/** keccak256 of the canonicalized address — matches the on-chain key. */
|
|
10029
10235
|
static addressHash(btcAddress) {
|
|
10030
|
-
return
|
|
10236
|
+
return keccak2565(toUtf8Bytes5(normalizeBitcoinAddress(btcAddress)));
|
|
10031
10237
|
}
|
|
10032
10238
|
/**
|
|
10033
10239
|
* Add a Bitcoin address to the caller's allowlist. It becomes usable only
|
|
@@ -17476,8 +17682,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
17476
17682
|
throw new Error(`Position not found: ${request.positionId}`);
|
|
17477
17683
|
}
|
|
17478
17684
|
const currentDebt = position.ucdDebt.toString();
|
|
17479
|
-
const ucdDebtHash =
|
|
17480
|
-
const contractHash =
|
|
17685
|
+
const ucdDebtHash = keccak2566(AbiCoder2.defaultAbiCoder().encode(["uint256"], [currentDebt]));
|
|
17686
|
+
const contractHash = keccak2566(AbiCoder2.defaultAbiCoder().encode(
|
|
17481
17687
|
["address", "address", "address", "address"],
|
|
17482
17688
|
[
|
|
17483
17689
|
positionManagerAddress,
|
|
@@ -17554,7 +17760,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
17554
17760
|
const currentPosition = await positionCore["getPositionDetails"](
|
|
17555
17761
|
positionIdBytes32
|
|
17556
17762
|
);
|
|
17557
|
-
const currentDebtHash =
|
|
17763
|
+
const currentDebtHash = keccak2566(
|
|
17558
17764
|
AbiCoder2.defaultAbiCoder().encode(
|
|
17559
17765
|
["uint256"],
|
|
17560
17766
|
[currentPosition.ucdDebt]
|
|
@@ -21290,7 +21496,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21290
21496
|
* been broadcast to the Bitcoin network. An empty array means no pending withdrawals.
|
|
21291
21497
|
*
|
|
21292
21498
|
* @param positionId - Position identifier
|
|
21293
|
-
* @returns Array of pending withdrawals, each including a pre-computed utxoKey
|
|
21499
|
+
* @returns Array of pending withdrawals, each including a pre-computed utxoKey (the key recoverStaleSpend and the admin clearing path operate on)
|
|
21294
21500
|
*/
|
|
21295
21501
|
async getPendingWithdrawals(positionId) {
|
|
21296
21502
|
this.ensureInitialized();
|
|
@@ -21322,24 +21528,58 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21322
21528
|
targetAddress: spend.targetAddress,
|
|
21323
21529
|
targetAmount: Number(spend.targetAmount),
|
|
21324
21530
|
authorizedAt: Number(spend.authorizedAt),
|
|
21325
|
-
utxoKey:
|
|
21531
|
+
utxoKey: solidityPackedKeccak2565(
|
|
21326
21532
|
["string", "uint32"],
|
|
21327
21533
|
[spend.txid, Number(spend.vout)]
|
|
21328
21534
|
)
|
|
21329
21535
|
}));
|
|
21330
21536
|
}
|
|
21331
21537
|
/**
|
|
21332
|
-
*
|
|
21333
|
-
*
|
|
21334
|
-
*
|
|
21335
|
-
*
|
|
21336
|
-
*
|
|
21538
|
+
* Reconcile every pending withdrawal against BITCOIN truth (incident
|
|
21539
|
+
* 2026-07-22): `getPendingWithdrawals` only reflects on-chain
|
|
21540
|
+
* authorizations, and the contract can never know whether the Phase-2 BTC
|
|
21541
|
+
* broadcast happened. Callers MUST use the returned `status` to decide what
|
|
21542
|
+
* to offer:
|
|
21543
|
+
* - EXECUTABLE → offer Execute (the only status that may).
|
|
21544
|
+
* - EXECUTED → auto-clear; `spendingTxid` is the completion proof —
|
|
21545
|
+
* feed it to recoverStaleSpend to clear the reservation.
|
|
21546
|
+
* - SPENT_MISMATCH → unexecutable; recoverStaleSpend with the spending txid
|
|
21547
|
+
* as invalidator clears the reservation.
|
|
21548
|
+
* - CORRUPT → authorization contradicts the chain (e.g. declared
|
|
21549
|
+
* satoshis ≠ real output value); Execute can only die at
|
|
21550
|
+
* the signer guard, and no borrower-side cancel exists
|
|
21551
|
+
* (audit #62210) — clearing needs the AdminModule
|
|
21552
|
+
* cancelStaleSpendByAdmin operator override.
|
|
21553
|
+
* - UNFUNDED → funding tx unknown/unconfirmed; wait.
|
|
21337
21554
|
*
|
|
21338
|
-
* @param
|
|
21339
|
-
*
|
|
21340
|
-
*
|
|
21341
|
-
* @returns Transaction hash of the cancellation EVM transaction
|
|
21555
|
+
* @param opts.esploraBaseUrl Esplora API base (e.g. the api proxy
|
|
21556
|
+
* `/v1/proxy/esplora/<network>`). Falls back to
|
|
21557
|
+
* `config.bitcoinProviders[0].url`; throws when neither is configured.
|
|
21342
21558
|
*/
|
|
21559
|
+
async reconcilePendingWithdrawals(positionId, opts) {
|
|
21560
|
+
this.ensureInitialized();
|
|
21561
|
+
const esploraBaseUrl = opts?.esploraBaseUrl ?? this.config.bitcoinProviders?.[0]?.url;
|
|
21562
|
+
if (!esploraBaseUrl) {
|
|
21563
|
+
throw new SDKError({
|
|
21564
|
+
code: "SDK_ESPLORA_NOT_CONFIGURED",
|
|
21565
|
+
message: "reconcilePendingWithdrawals requires opts.esploraBaseUrl or config.bitcoinProviders[0].url",
|
|
21566
|
+
category: "CONFIGURATION" /* CONFIGURATION */,
|
|
21567
|
+
severity: "HIGH" /* HIGH */,
|
|
21568
|
+
originalError: new Error("esplora base URL not configured")
|
|
21569
|
+
});
|
|
21570
|
+
}
|
|
21571
|
+
const spends = await this.getPendingWithdrawals(positionId);
|
|
21572
|
+
return Promise.all(
|
|
21573
|
+
spends.map((spend) => reconcileAuthorizedSpend(spend, esploraBaseUrl))
|
|
21574
|
+
);
|
|
21575
|
+
}
|
|
21576
|
+
// NOTE: there is deliberately no cancelPendingWithdrawal. Borrower-callable
|
|
21577
|
+
// cleanup was permanently disabled by audit #62210 (markSpentByOwner /
|
|
21578
|
+
// cancelStaleSpendByOwner revert with BorrowerBTCSpendMutationDisabled — a
|
|
21579
|
+
// borrower cancel could be replayed to mint UCD against the same UTXO).
|
|
21580
|
+
// Reservations clear via recoverStaleSpend below (trustless attestation once
|
|
21581
|
+
// the UTXO is provably consumed) or the AdminModule-timelocked
|
|
21582
|
+
// cancelStaleSpendByAdmin operator override.
|
|
21343
21583
|
/**
|
|
21344
21584
|
* Recover (clear) a stale BTC reservation via LIT-attested proof.
|
|
21345
21585
|
*
|
|
@@ -21491,6 +21731,256 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21491
21731
|
};
|
|
21492
21732
|
}
|
|
21493
21733
|
}
|
|
21734
|
+
/**
|
|
21735
|
+
* Sign + broadcast the PKP vault→vault self-spend that invalidates one
|
|
21736
|
+
* stuck authorized outpoint (btc-utxo-invalidator 1.0.0). Once the returned
|
|
21737
|
+
* `invalidatorTxid` reaches >=6 Bitcoin confirmations, `recoverStaleSpend`
|
|
21738
|
+
* clears the on-chain reservation — the trustless replacement for the
|
|
21739
|
+
* AdminModule cancelStaleSpendByAdmin path on unspent stuck/corrupt
|
|
21740
|
+
* reservations (incident 2026-07-22). Requires the borrower (or position
|
|
21741
|
+
* delegate) signer; the signature is scoped to exactly this outpoint.
|
|
21742
|
+
*/
|
|
21743
|
+
async invalidateStaleSpend(params) {
|
|
21744
|
+
this.ensureInitialized();
|
|
21745
|
+
const chainId = this.config.chainId ?? this.config.networkOverride?.chainId;
|
|
21746
|
+
if (!chainId) {
|
|
21747
|
+
return {
|
|
21748
|
+
success: false,
|
|
21749
|
+
failedStep: "config",
|
|
21750
|
+
error: "chainId required for invalidateStaleSpend"
|
|
21751
|
+
};
|
|
21752
|
+
}
|
|
21753
|
+
const signer = this.getSignerOrThrow();
|
|
21754
|
+
const envelope = await buildInvalidateStaleSpendEnvelope({
|
|
21755
|
+
positionId: params.positionId,
|
|
21756
|
+
utxoTxid: params.utxoTxid,
|
|
21757
|
+
utxoVout: params.utxoVout,
|
|
21758
|
+
chainId,
|
|
21759
|
+
signer
|
|
21760
|
+
});
|
|
21761
|
+
if (this.config.mode === "service" && this.config.serviceEndpoint) {
|
|
21762
|
+
const headers = {
|
|
21763
|
+
"Content-Type": "application/json",
|
|
21764
|
+
...await this.getAuthHeader()
|
|
21765
|
+
};
|
|
21766
|
+
const resp = await fetch(
|
|
21767
|
+
`${this.config.serviceEndpoint}/api/lit/stale-spend/invalidate`,
|
|
21768
|
+
{
|
|
21769
|
+
method: "POST",
|
|
21770
|
+
headers,
|
|
21771
|
+
body: JSON.stringify({
|
|
21772
|
+
positionId: this.toBytes32(params.positionId),
|
|
21773
|
+
utxoTxid: params.utxoTxid,
|
|
21774
|
+
utxoVout: params.utxoVout,
|
|
21775
|
+
chainId,
|
|
21776
|
+
auth: {
|
|
21777
|
+
timestamp: envelope.timestamp,
|
|
21778
|
+
action: envelope.action,
|
|
21779
|
+
signature: envelope.signature
|
|
21780
|
+
}
|
|
21781
|
+
})
|
|
21782
|
+
}
|
|
21783
|
+
);
|
|
21784
|
+
const json = await resp.json();
|
|
21785
|
+
return json.data ?? {
|
|
21786
|
+
success: false,
|
|
21787
|
+
error: json.error ?? "stale-spend invalidate request failed"
|
|
21788
|
+
};
|
|
21789
|
+
}
|
|
21790
|
+
const rpcUrl = params.rpcUrl ?? this.config.ethRpcUrl;
|
|
21791
|
+
if (!rpcUrl) {
|
|
21792
|
+
return {
|
|
21793
|
+
success: false,
|
|
21794
|
+
failedStep: "config",
|
|
21795
|
+
error: "ethRpcUrl required for invalidateStaleSpend \u2014 set DiamondHandsSDKConfig.ethRpcUrl or pass rpcUrl"
|
|
21796
|
+
};
|
|
21797
|
+
}
|
|
21798
|
+
const positionDetails = await this.getPositionDetailsView(
|
|
21799
|
+
params.positionId
|
|
21800
|
+
);
|
|
21801
|
+
if (!positionDetails?.pkpId || positionDetails.pkpId === ZeroHash) {
|
|
21802
|
+
return { success: false, error: "Position has no PKP" };
|
|
21803
|
+
}
|
|
21804
|
+
const pkpCache = this.cacheManager.getCache("pkp-data", {
|
|
21805
|
+
maxSize: 500,
|
|
21806
|
+
ttlMs: 24 * 60 * 60 * 1e3
|
|
21807
|
+
});
|
|
21808
|
+
let publicKey = pkpCache.get(params.positionId)?.publicKey;
|
|
21809
|
+
if (!publicKey) {
|
|
21810
|
+
const derived = await this.litOps.deriveDiamondHandsLoanPkpPublicKey({
|
|
21811
|
+
pkpId: positionDetails.pkpId
|
|
21812
|
+
});
|
|
21813
|
+
publicKey = derived.publicKey;
|
|
21814
|
+
}
|
|
21815
|
+
if (!publicKey) {
|
|
21816
|
+
return { success: false, error: "Failed to resolve PKP public key" };
|
|
21817
|
+
}
|
|
21818
|
+
const chipotlePkpAddress = computeAddress(publicKey);
|
|
21819
|
+
const dc = this.config.contractAddresses || {};
|
|
21820
|
+
const chainName = chainId === 1 ? "ethereum" : chainId === 11155111 ? "sepolia" : chainId === 1337 || chainId === 31337 ? "hardhat" : void 0;
|
|
21821
|
+
if (!chainName) {
|
|
21822
|
+
return {
|
|
21823
|
+
success: false,
|
|
21824
|
+
error: `Unsupported chainId ${chainId} for invalidateStaleSpend (supported: 1, 11155111, 1337, 31337)`
|
|
21825
|
+
};
|
|
21826
|
+
}
|
|
21827
|
+
const signResult = await this.litOps.signBtcUtxoInvalidator({
|
|
21828
|
+
auth: {
|
|
21829
|
+
positionId: this.toBytes32(params.positionId),
|
|
21830
|
+
utxoTxid: params.utxoTxid,
|
|
21831
|
+
utxoVout: params.utxoVout,
|
|
21832
|
+
timestamp: envelope.timestamp,
|
|
21833
|
+
chainId,
|
|
21834
|
+
action: envelope.action,
|
|
21835
|
+
signature: envelope.signature
|
|
21836
|
+
},
|
|
21837
|
+
publicKey,
|
|
21838
|
+
pkpId: chipotlePkpAddress,
|
|
21839
|
+
chain: chainName,
|
|
21840
|
+
rpcUrl,
|
|
21841
|
+
...this.config.bitcoinProviders?.[0]?.url && (chainId === 1337 || chainId === 31337) ? { devBitcoinProviderUrl: this.config.bitcoinProviders[0].url } : {},
|
|
21842
|
+
contractAddresses: {
|
|
21843
|
+
PositionManager: dc.positionManager ?? "",
|
|
21844
|
+
BTCSpendAuthorizer: dc.btcSpendAuthorizer ?? "",
|
|
21845
|
+
...dc.bitcoinProviderRegistry ? { BitcoinProviderRegistry: dc.bitcoinProviderRegistry } : {},
|
|
21846
|
+
...dc.positionDelegateRegistry ? { PositionDelegateRegistry: dc.positionDelegateRegistry } : {}
|
|
21847
|
+
}
|
|
21848
|
+
});
|
|
21849
|
+
if (!signResult.success) {
|
|
21850
|
+
return {
|
|
21851
|
+
success: false,
|
|
21852
|
+
error: signResult.error ?? "btc-utxo-invalidator signing failed",
|
|
21853
|
+
failedStep: signResult.failedStep
|
|
21854
|
+
};
|
|
21855
|
+
}
|
|
21856
|
+
if (signResult.alreadySpent && typeof signResult.alreadySpent === "object") {
|
|
21857
|
+
return {
|
|
21858
|
+
success: true,
|
|
21859
|
+
alreadySpent: { spendingTxid: signResult.alreadySpent.spendingTxid }
|
|
21860
|
+
};
|
|
21861
|
+
}
|
|
21862
|
+
if (!signResult.signatures?.length || !signResult.utxo || !signResult.pkpBtcAddress || typeof signResult.invalidatorAmount !== "number") {
|
|
21863
|
+
return {
|
|
21864
|
+
success: false,
|
|
21865
|
+
error: "btc-utxo-invalidator returned incomplete data (signatures/utxo/pkpBtcAddress/invalidatorAmount)"
|
|
21866
|
+
};
|
|
21867
|
+
}
|
|
21868
|
+
const broadcastUrl = this.config.bitcoinProviders?.[0]?.url;
|
|
21869
|
+
if (!broadcastUrl) {
|
|
21870
|
+
return {
|
|
21871
|
+
success: false,
|
|
21872
|
+
error: "invalidateStaleSpend broadcast requires config.bitcoinProviders[0].url (esplora base)"
|
|
21873
|
+
};
|
|
21874
|
+
}
|
|
21875
|
+
const { txid: invalidatorTxid } = await broadcastSignedBitcoinTransaction({
|
|
21876
|
+
signatures: signResult.signatures,
|
|
21877
|
+
utxos: [signResult.utxo],
|
|
21878
|
+
pkpPublicKey: signResult.pkpPublicKey ?? publicKey,
|
|
21879
|
+
destination: signResult.pkpBtcAddress,
|
|
21880
|
+
userReceivesAmount: signResult.invalidatorAmount,
|
|
21881
|
+
changeAmount: 0,
|
|
21882
|
+
pkpBtcAddress: signResult.pkpBtcAddress,
|
|
21883
|
+
bitcoinNetwork: chainId === 1 ? "mainnet" : "regtest",
|
|
21884
|
+
bitcoinRpcUrl: broadcastUrl
|
|
21885
|
+
});
|
|
21886
|
+
return {
|
|
21887
|
+
success: true,
|
|
21888
|
+
invalidatorTxid,
|
|
21889
|
+
invalidatorAmount: signResult.invalidatorAmount,
|
|
21890
|
+
networkFee: signResult.networkFee,
|
|
21891
|
+
valueMismatch: signResult.valueMismatch
|
|
21892
|
+
};
|
|
21893
|
+
}
|
|
21894
|
+
/**
|
|
21895
|
+
* Resumable, stateless clearing state machine for one stuck reservation —
|
|
21896
|
+
* Bitcoin itself is the state store, so every call either advances the flow
|
|
21897
|
+
* one step or reports where it stands (survives reloads and the multi-day
|
|
21898
|
+
* 6-confirmation wait; no internal polling).
|
|
21899
|
+
*
|
|
21900
|
+
* (a) reservation gone → phase "cleared"
|
|
21901
|
+
* (b) outpoint spent, >=6 confs → recoverStaleSpend → "cleared"/"failed"
|
|
21902
|
+
* outpoint spent, <6 confs → "awaiting-confirmations" (x/6)
|
|
21903
|
+
* (c) outpoint unspent → invalidateStaleSpend → "invalidator-broadcast"
|
|
21904
|
+
*/
|
|
21905
|
+
async clearStuckReservation(params) {
|
|
21906
|
+
this.ensureInitialized();
|
|
21907
|
+
const REQUIRED_CONFS = 6;
|
|
21908
|
+
const esploraBaseUrl = params.esploraBaseUrl ?? this.config.bitcoinProviders?.[0]?.url;
|
|
21909
|
+
const reconciled = await this.reconcilePendingWithdrawals(
|
|
21910
|
+
params.positionId,
|
|
21911
|
+
esploraBaseUrl ? { esploraBaseUrl } : void 0
|
|
21912
|
+
);
|
|
21913
|
+
const row = reconciled.find(
|
|
21914
|
+
(r) => r.spend.txid.toLowerCase() === params.utxoTxid.toLowerCase() && r.spend.vout === params.utxoVout
|
|
21915
|
+
);
|
|
21916
|
+
if (!row) {
|
|
21917
|
+
return { phase: "cleared" };
|
|
21918
|
+
}
|
|
21919
|
+
if (row.spendingTxid && esploraBaseUrl) {
|
|
21920
|
+
const base = esploraBaseUrl.replace(/\/+$/, "");
|
|
21921
|
+
const txRes = await fetch(`${base}/tx/${row.spendingTxid}`);
|
|
21922
|
+
const tipRes = await fetch(`${base}/blocks/tip/height`);
|
|
21923
|
+
let confirmations = 0;
|
|
21924
|
+
if (txRes.ok && tipRes.ok) {
|
|
21925
|
+
const tx = await txRes.json();
|
|
21926
|
+
const tip = Number(await tipRes.text());
|
|
21927
|
+
if (tx.status?.confirmed && typeof tx.status.block_height === "number" && Number.isFinite(tip)) {
|
|
21928
|
+
confirmations = Math.max(0, tip - tx.status.block_height + 1);
|
|
21929
|
+
}
|
|
21930
|
+
}
|
|
21931
|
+
if (confirmations < REQUIRED_CONFS) {
|
|
21932
|
+
return {
|
|
21933
|
+
phase: "awaiting-confirmations",
|
|
21934
|
+
invalidatorTxid: row.spendingTxid,
|
|
21935
|
+
confirmations,
|
|
21936
|
+
required: REQUIRED_CONFS
|
|
21937
|
+
};
|
|
21938
|
+
}
|
|
21939
|
+
const recovery = await this.recoverStaleSpend({
|
|
21940
|
+
positionId: params.positionId,
|
|
21941
|
+
utxoTxid: params.utxoTxid,
|
|
21942
|
+
utxoVout: params.utxoVout,
|
|
21943
|
+
invalidatorTxid: row.spendingTxid
|
|
21944
|
+
});
|
|
21945
|
+
if (recovery.success) {
|
|
21946
|
+
return {
|
|
21947
|
+
phase: "cleared",
|
|
21948
|
+
invalidatorTxid: row.spendingTxid,
|
|
21949
|
+
transactionHash: recovery.transactionHash,
|
|
21950
|
+
classification: recovery.classification
|
|
21951
|
+
};
|
|
21952
|
+
}
|
|
21953
|
+
return {
|
|
21954
|
+
phase: "failed",
|
|
21955
|
+
invalidatorTxid: row.spendingTxid,
|
|
21956
|
+
error: recovery.error ?? "stale-spend recovery failed"
|
|
21957
|
+
};
|
|
21958
|
+
}
|
|
21959
|
+
const invalidation = await this.invalidateStaleSpend({
|
|
21960
|
+
positionId: params.positionId,
|
|
21961
|
+
utxoTxid: params.utxoTxid,
|
|
21962
|
+
utxoVout: params.utxoVout
|
|
21963
|
+
});
|
|
21964
|
+
if (!invalidation.success) {
|
|
21965
|
+
return {
|
|
21966
|
+
phase: "failed",
|
|
21967
|
+
error: invalidation.error ?? "invalidator signing/broadcast failed"
|
|
21968
|
+
};
|
|
21969
|
+
}
|
|
21970
|
+
if (invalidation.alreadySpent) {
|
|
21971
|
+
return {
|
|
21972
|
+
phase: "awaiting-confirmations",
|
|
21973
|
+
invalidatorTxid: invalidation.alreadySpent.spendingTxid,
|
|
21974
|
+
confirmations: 0,
|
|
21975
|
+
required: REQUIRED_CONFS
|
|
21976
|
+
};
|
|
21977
|
+
}
|
|
21978
|
+
return {
|
|
21979
|
+
phase: "invalidator-broadcast",
|
|
21980
|
+
invalidatorTxid: invalidation.invalidatorTxid,
|
|
21981
|
+
valueMismatch: invalidation.valueMismatch
|
|
21982
|
+
};
|
|
21983
|
+
}
|
|
21494
21984
|
/**
|
|
21495
21985
|
* Withdraw BTC and execute transfer (Complete Flow)
|
|
21496
21986
|
*
|
|
@@ -22581,7 +23071,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22581
23071
|
* Convert decimal position ID to bytes32 format
|
|
22582
23072
|
*/
|
|
22583
23073
|
toBytes32(value) {
|
|
22584
|
-
return
|
|
23074
|
+
return zeroPadValue5(toBeHex2(BigInt(value)), 32);
|
|
22585
23075
|
}
|
|
22586
23076
|
/**
|
|
22587
23077
|
* Check if an error indicates a technical failure vs business logic rejection
|
|
@@ -22799,8 +23289,8 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22799
23289
|
const MIN_REVEAL_DELAY = 60;
|
|
22800
23290
|
const MAX_RANDOM_DELAY = 240;
|
|
22801
23291
|
const entropyInput = positionId + quantumTimestamp.toString();
|
|
22802
|
-
const hash =
|
|
22803
|
-
|
|
23292
|
+
const hash = keccak2566(
|
|
23293
|
+
toUtf8Bytes6(entropyInput)
|
|
22804
23294
|
);
|
|
22805
23295
|
const randomValue = BigInt(hash);
|
|
22806
23296
|
const randomDelay = Number(randomValue % BigInt(MAX_RANDOM_DELAY));
|
|
@@ -22825,12 +23315,12 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22825
23315
|
const rawPositionId = /^0x/i.test(params.positionId.trim()) ? params.positionId.trim().slice(2) : params.positionId.trim();
|
|
22826
23316
|
const canonicalPositionId = "0x" + rawPositionId.padStart(64, "0").toLowerCase();
|
|
22827
23317
|
const intentTimestamp = Math.floor(Date.now() / 1e3);
|
|
22828
|
-
const intentActionHash =
|
|
22829
|
-
const intentHash =
|
|
23318
|
+
const intentActionHash = keccak2566(toUtf8Bytes6("liquidate-position"));
|
|
23319
|
+
const intentHash = solidityPackedKeccak2565(
|
|
22830
23320
|
["bytes32", "uint256", "uint256", "address", "bytes32"],
|
|
22831
23321
|
[canonicalPositionId, intentTimestamp, chainId, intentSigner, intentActionHash]
|
|
22832
23322
|
);
|
|
22833
|
-
const intentSignature = await signer.signMessage(
|
|
23323
|
+
const intentSignature = await signer.signMessage(getBytes5(intentHash));
|
|
22834
23324
|
const response = await fetch(endpoint, {
|
|
22835
23325
|
method: "POST",
|
|
22836
23326
|
headers: { "Content-Type": "application/json" },
|
|
@@ -22882,7 +23372,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22882
23372
|
const vrfSeedRaw = await lmResult.value["vrfSeeds"](positionId);
|
|
22883
23373
|
const vrfSeed = BigInt(vrfSeedRaw.toString());
|
|
22884
23374
|
if (vrfSeed !== 0n) {
|
|
22885
|
-
const finalEntropy =
|
|
23375
|
+
const finalEntropy = solidityPackedKeccak2565(
|
|
22886
23376
|
["uint256", "uint256", "bytes32"],
|
|
22887
23377
|
[vrfSeed, BigInt(quantumTimestamp.toString()), positionId]
|
|
22888
23378
|
);
|
|
@@ -23203,6 +23693,7 @@ export {
|
|
|
23203
23693
|
PKPManager,
|
|
23204
23694
|
PaymentType,
|
|
23205
23695
|
PositionStatus,
|
|
23696
|
+
RECOVERY_INVALIDATE_ACTION,
|
|
23206
23697
|
SDKError,
|
|
23207
23698
|
SDK_DEFAULTS,
|
|
23208
23699
|
SEPOLIA_CONTRACTS,
|
|
@@ -23215,6 +23706,8 @@ export {
|
|
|
23215
23706
|
assertProtocolNotPaused,
|
|
23216
23707
|
assertSafeServiceEndpoint,
|
|
23217
23708
|
buildBtcExecuteEnvelope,
|
|
23709
|
+
buildInvalidateMessageHash,
|
|
23710
|
+
buildInvalidateStaleSpendEnvelope,
|
|
23218
23711
|
collectFailures,
|
|
23219
23712
|
collectSuccesses,
|
|
23220
23713
|
combine,
|
|
@@ -23226,6 +23719,7 @@ export {
|
|
|
23226
23719
|
createPKPManager,
|
|
23227
23720
|
createWithdrawalAddressModule,
|
|
23228
23721
|
DiamondHandsSDK as default,
|
|
23722
|
+
defaultHttpGetJson,
|
|
23229
23723
|
failure,
|
|
23230
23724
|
fetchProtocolPauseStatus,
|
|
23231
23725
|
firstSuccess,
|
|
@@ -23249,6 +23743,7 @@ export {
|
|
|
23249
23743
|
mapError,
|
|
23250
23744
|
match,
|
|
23251
23745
|
numericToPositionStatus,
|
|
23746
|
+
reconcileAuthorizedSpend,
|
|
23252
23747
|
setPositionDelegate,
|
|
23253
23748
|
success,
|
|
23254
23749
|
toPromise,
|