@gvnrdao/dh-sdk 0.0.304 → 0.0.306
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/deployments.js +38 -32
- package/dist/deployments.mjs +38 -32
- package/dist/index.js +326 -53
- package/dist/index.mjs +327 -54
- package/dist/interfaces/chunks/config.i.d.ts +12 -0
- package/dist/interfaces/chunks/loan-operations.i.d.ts +10 -0
- package/dist/modules/diamond-hands-sdk.d.ts +19 -0
- package/dist/utils/lane-guard.utils.d.ts +36 -0
- package/dist/utils/quantum-submission.utils.d.ts +8 -0
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -438,9 +438,13 @@ var init_deployment_addresses = __esm({
|
|
|
438
438
|
reason: "Validator gas fix (#420): BitcoinAddressValidator packed-table lookups; VERSION 1.0.0 -> 1.0.1"
|
|
439
439
|
},
|
|
440
440
|
UCDController: {
|
|
441
|
+
previousImplementation: "0x16f94AcC78cE87bEeBCfF90552091B3EfC071E48",
|
|
441
442
|
newImplementation: "0x28B8cAB89D89B3F83581FD2290de2Ce84535bd0A",
|
|
442
|
-
|
|
443
|
-
|
|
443
|
+
upgradedAt: "2026-07-06T10:41:59.000Z",
|
|
444
|
+
upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0xf6609d3fa43b460d953c85d8d7265b59bf177723042db79a6a700571bad777b7 (block 25472962)",
|
|
445
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
|
|
446
|
+
executeTx: "0xf6609d3fa43b460d953c85d8d7265b59bf177723042db79a6a700571bad777b7",
|
|
447
|
+
block: 25472962
|
|
444
448
|
},
|
|
445
449
|
PositionManagerCoreModule: {
|
|
446
450
|
previousImplementation: "0x6C8b0FB93F8d0239254aefaC24d4264cA2F8AAeB",
|
|
@@ -567,45 +571,47 @@ var init_deployment_addresses = __esm({
|
|
|
567
571
|
};
|
|
568
572
|
LOCALHOST_DEPLOYMENT = {
|
|
569
573
|
network: "localhost",
|
|
570
|
-
chainId:
|
|
571
|
-
timestamp: "2026-
|
|
574
|
+
chainId: 1337,
|
|
575
|
+
timestamp: "2026-07-20T19:45:58.514Z",
|
|
572
576
|
deployer: "",
|
|
573
577
|
contracts: {
|
|
574
|
-
MessageHashBuilder: "
|
|
575
|
-
|
|
578
|
+
MessageHashBuilder: "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9",
|
|
579
|
+
LITActionValidator: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853",
|
|
580
|
+
UpgradeValidator: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318",
|
|
576
581
|
UCDToken: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
|
|
577
582
|
UCDController: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
|
|
578
|
-
PositionManagerCoreModule: "
|
|
579
|
-
TermManagerModule: "
|
|
580
|
-
LoanOperationsManagerModule: "
|
|
581
|
-
BTCSpendAuthorizer: "
|
|
582
|
-
CollateralManagerModule: "
|
|
583
|
-
LiquidationManagerModule: "
|
|
584
|
-
CircuitBreakerModule: "
|
|
585
|
-
AdminModule: "
|
|
586
|
-
PositionManagerViews: "
|
|
587
|
-
PositionManager: "
|
|
588
|
-
OperationAuthorizationRegistry: "
|
|
589
|
-
PKPValidation: "
|
|
583
|
+
PositionManagerCoreModule: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
|
|
584
|
+
TermManagerModule: "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82",
|
|
585
|
+
LoanOperationsManagerModule: "0x68B1D87F95878fE05B998F19b66F4baba5De1aed",
|
|
586
|
+
BTCSpendAuthorizer: "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d",
|
|
587
|
+
CollateralManagerModule: "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44",
|
|
588
|
+
LiquidationManagerModule: "0x4A679253410272dd5232B3Ff7cF5dbB88f295319",
|
|
589
|
+
CircuitBreakerModule: "0x0B306BF915C4d645ff596e518fAf3F9669b97016",
|
|
590
|
+
AdminModule: "0x09635F643e140090A9A8Dcd712eD6285858ceBef",
|
|
591
|
+
PositionManagerViews: "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1",
|
|
592
|
+
PositionManager: "0x851356ae760d987E095750cCeb3bC6014560891C",
|
|
593
|
+
OperationAuthorizationRegistry: "0xc96304e3c037f81dA488ed9dEa1D8F2a48278a75",
|
|
594
|
+
PKPValidation: "0xD0141E899a65C95a556fE2B27e5982A6DE7fDD7A"
|
|
590
595
|
}
|
|
591
596
|
};
|
|
592
597
|
LOCALHOST_CONTRACTS = {
|
|
593
|
-
MessageHashBuilder: "
|
|
594
|
-
|
|
598
|
+
MessageHashBuilder: "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9",
|
|
599
|
+
LITActionValidator: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853",
|
|
600
|
+
UpgradeValidator: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318",
|
|
595
601
|
UCDToken: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
|
|
596
602
|
UCDController: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
|
|
597
|
-
PositionManagerCoreModule: "
|
|
598
|
-
TermManagerModule: "
|
|
599
|
-
LoanOperationsManagerModule: "
|
|
600
|
-
BTCSpendAuthorizer: "
|
|
601
|
-
CollateralManagerModule: "
|
|
602
|
-
LiquidationManagerModule: "
|
|
603
|
-
CircuitBreakerModule: "
|
|
604
|
-
AdminModule: "
|
|
605
|
-
PositionManagerViews: "
|
|
606
|
-
PositionManager: "
|
|
607
|
-
OperationAuthorizationRegistry: "
|
|
608
|
-
PKPValidation: "
|
|
603
|
+
PositionManagerCoreModule: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
|
|
604
|
+
TermManagerModule: "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82",
|
|
605
|
+
LoanOperationsManagerModule: "0x68B1D87F95878fE05B998F19b66F4baba5De1aed",
|
|
606
|
+
BTCSpendAuthorizer: "0xc6e7DF5E7b4f2A278906862b61205850344D4e7d",
|
|
607
|
+
CollateralManagerModule: "0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44",
|
|
608
|
+
LiquidationManagerModule: "0x4A679253410272dd5232B3Ff7cF5dbB88f295319",
|
|
609
|
+
CircuitBreakerModule: "0x0B306BF915C4d645ff596e518fAf3F9669b97016",
|
|
610
|
+
AdminModule: "0x09635F643e140090A9A8Dcd712eD6285858ceBef",
|
|
611
|
+
PositionManagerViews: "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1",
|
|
612
|
+
PositionManager: "0x851356ae760d987E095750cCeb3bC6014560891C",
|
|
613
|
+
OperationAuthorizationRegistry: "0xc96304e3c037f81dA488ed9dEa1D8F2a48278a75",
|
|
614
|
+
PKPValidation: "0xD0141E899a65C95a556fE2B27e5982A6DE7fDD7A"
|
|
609
615
|
};
|
|
610
616
|
ALL_DEPLOYMENTS = {
|
|
611
617
|
sepolia: SEPOLIA_DEPLOYMENT,
|
|
@@ -1287,7 +1293,7 @@ var require_pkg_src = __commonJS({
|
|
|
1287
1293
|
description: "Production Pkp Validator",
|
|
1288
1294
|
version: "1.0.0",
|
|
1289
1295
|
deployed: true,
|
|
1290
|
-
deployedAt:
|
|
1296
|
+
deployedAt: 1784569687492,
|
|
1291
1297
|
size: 4140,
|
|
1292
1298
|
hash: "4c0d65bf1f33cdac22a78fd8c4764319cc3fe00650e0fe7fa8a6090d9a318a7b",
|
|
1293
1299
|
validatorWalletAddress: "0x82db9f515957aa380b596c6ea2bd30cef181f236",
|
|
@@ -1364,17 +1370,17 @@ var require_pkg_src = __commonJS({
|
|
|
1364
1370
|
}
|
|
1365
1371
|
},
|
|
1366
1372
|
btcWithdrawal: {
|
|
1367
|
-
cid: "
|
|
1373
|
+
cid: "QmexvpfHPacjsVcqNHHLyJhSnnAPLgbWmWd9N3NDLDvYds",
|
|
1368
1374
|
authorizedCidHex: cidToHex(
|
|
1369
|
-
"
|
|
1375
|
+
"QmexvpfHPacjsVcqNHHLyJhSnnAPLgbWmWd9N3NDLDvYds"
|
|
1370
1376
|
),
|
|
1371
1377
|
name: "Btc Withdrawal",
|
|
1372
1378
|
description: "Production Btc Withdrawal",
|
|
1373
|
-
version: "1.2.
|
|
1379
|
+
version: "1.2.3",
|
|
1374
1380
|
deployed: true,
|
|
1375
|
-
deployedAt:
|
|
1376
|
-
size:
|
|
1377
|
-
hash: "
|
|
1381
|
+
deployedAt: 1784585746901,
|
|
1382
|
+
size: 92027,
|
|
1383
|
+
hash: "7b4f4eab1f0c08b8b5b1c9cdb1064dd403ec13f57765919f62dec67fb8ba3c69",
|
|
1378
1384
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1379
1385
|
pkp: {
|
|
1380
1386
|
publicKey: "0x043616787c5432415c24378c4ef48de2bcd6bb7b575b837e3cff09171802662da7105d79586c7659677a0ecbaddac4cce06cb2a11f69a16fa0c4d7002ac7d51a4d",
|
|
@@ -3746,12 +3752,23 @@ ${auth.clientId}`;
|
|
|
3746
3752
|
if (!data.vout || !Array.isArray(data.vout)) {
|
|
3747
3753
|
return null;
|
|
3748
3754
|
}
|
|
3749
|
-
return data.vout.map((output, index) =>
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
+
return data.vout.map((output, index) => {
|
|
3756
|
+
if (output.value === void 0) {
|
|
3757
|
+
throw new Error(
|
|
3758
|
+
`getTransactionOutputs(${txid}): output ${index} is missing its value \u2014 malformed provider response, refusing to return outputs that cannot be value-verified`
|
|
3759
|
+
);
|
|
3760
|
+
}
|
|
3761
|
+
assertSaneSatoshis(
|
|
3762
|
+
output.value,
|
|
3763
|
+
`getTransactionOutputs(${txid}) output ${index}`
|
|
3764
|
+
);
|
|
3765
|
+
return {
|
|
3766
|
+
index,
|
|
3767
|
+
scriptpubkey: output.scriptpubkey,
|
|
3768
|
+
scriptpubkey_address: output.scriptpubkey_address,
|
|
3769
|
+
value: output.value
|
|
3770
|
+
};
|
|
3771
|
+
});
|
|
3755
3772
|
} catch (error) {
|
|
3756
3773
|
clearTimeout(timeoutId);
|
|
3757
3774
|
if (isAbortError(error)) {
|
|
@@ -5513,7 +5530,12 @@ var init_network_configs = __esm({
|
|
|
5513
5530
|
NETWORK_CONFIGS = {
|
|
5514
5531
|
1: getMainnetConfig(),
|
|
5515
5532
|
11155111: getSepoliaConfig(),
|
|
5516
|
-
1337: getLocalhostConfig()
|
|
5533
|
+
1337: getLocalhostConfig(),
|
|
5534
|
+
// In-process hardhat default chainId — same localhost deployment as 1337
|
|
5535
|
+
// (`npx hardhat node`), so consumers on either local chain resolve config.
|
|
5536
|
+
// chainId overridden so the resolved config self-describes the chain that
|
|
5537
|
+
// was actually looked up (downstream bind/log/validate reads it).
|
|
5538
|
+
31337: { ...getLocalhostConfig(), chainId: 31337 }
|
|
5517
5539
|
};
|
|
5518
5540
|
DEFAULT_NETWORK_CONFIG = {
|
|
5519
5541
|
/** Fallback when chain is unknown — production Chipotle. */
|
|
@@ -6935,12 +6957,20 @@ async function assertSafeQuantumSubmission(opts) {
|
|
|
6935
6957
|
...opts.sleep ? { sleep: opts.sleep } : {}
|
|
6936
6958
|
};
|
|
6937
6959
|
await awaitSafeSubmissionWindow(quantumTimestamp, gateOpts);
|
|
6960
|
+
const SAME_BLOCK_INTERACTION_SELECTOR = "0x3b53d1db";
|
|
6961
|
+
const laneGuardActive = opts.laneGuardActive === true;
|
|
6938
6962
|
for (let sim = 1; ; sim++) {
|
|
6939
6963
|
try {
|
|
6940
6964
|
await provider.call({ to, from, data });
|
|
6941
6965
|
break;
|
|
6942
6966
|
} catch (simError) {
|
|
6943
6967
|
const decoded = decodeQuantumRevert(simError);
|
|
6968
|
+
if (laneGuardActive && decoded.selector === SAME_BLOCK_INTERACTION_SELECTOR) {
|
|
6969
|
+
onDebug(
|
|
6970
|
+
"\u21AA\uFE0F [lane-only] Simulated SameBlockInteraction is a stale-latest-block artifact (the simulated block contains this flow's own previous tx) \u2014 proceeding to broadcast; the on-chain guard stays authoritative"
|
|
6971
|
+
);
|
|
6972
|
+
break;
|
|
6973
|
+
}
|
|
6944
6974
|
if (decoded.selector === DEAD_ZONE_VIOLATION_SELECTOR && sim <= maxResimulations) {
|
|
6945
6975
|
const sigQuantum = Math.floor(quantumTimestamp / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
|
|
6946
6976
|
const realCurrentQuantum = Math.floor(now() / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
|
|
@@ -6976,6 +7006,28 @@ async function assertSafeQuantumSubmission(opts) {
|
|
|
6976
7006
|
await awaitSafeSubmissionWindow(quantumTimestamp, gateOpts);
|
|
6977
7007
|
}
|
|
6978
7008
|
|
|
7009
|
+
// src/utils/lane-guard.utils.ts
|
|
7010
|
+
var HARDHAT_CHAIN_IDS = /* @__PURE__ */ new Set([1337, 31337]);
|
|
7011
|
+
function laneGuardEnvEnabled() {
|
|
7012
|
+
return typeof process !== "undefined" && process.env?.LIT_LANE_GUARD === "1";
|
|
7013
|
+
}
|
|
7014
|
+
async function isLaneGuardActiveOnProvider(provider) {
|
|
7015
|
+
if (!laneGuardEnvEnabled())
|
|
7016
|
+
return false;
|
|
7017
|
+
if (!provider || typeof provider.send !== "function")
|
|
7018
|
+
return false;
|
|
7019
|
+
try {
|
|
7020
|
+
const [chainIdHex, clientVersion] = await Promise.all([
|
|
7021
|
+
provider.send("eth_chainId", []),
|
|
7022
|
+
provider.send("web3_clientVersion", [])
|
|
7023
|
+
]);
|
|
7024
|
+
const chainId = Number(chainIdHex);
|
|
7025
|
+
return HARDHAT_CHAIN_IDS.has(chainId) && typeof clientVersion === "string" && clientVersion.toLowerCase().includes("hardhat");
|
|
7026
|
+
} catch {
|
|
7027
|
+
return false;
|
|
7028
|
+
}
|
|
7029
|
+
}
|
|
7030
|
+
|
|
6979
7031
|
// src/utils/btc-withdrawal-message.ts
|
|
6980
7032
|
var import_ethers6 = require("ethers");
|
|
6981
7033
|
var QUANTUM_SECONDS = 60;
|
|
@@ -16318,6 +16370,10 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
16318
16370
|
this.litOps = new import_dh_lit_ops.LitOps({
|
|
16319
16371
|
mode: config.mode || "standalone",
|
|
16320
16372
|
network: config.litNetwork || DEFAULT_LIT_NETWORK,
|
|
16373
|
+
// Completes the module's chainId resolution chain (jsParams shapes →
|
|
16374
|
+
// config.chainId) — e.g. the lane validator-override chain binding
|
|
16375
|
+
// reads it when a jsParams shape lacks chainId.
|
|
16376
|
+
chainId: config.chainId,
|
|
16321
16377
|
signer: config.mode === "standalone" ? config.litOpsSigner : void 0,
|
|
16322
16378
|
serviceEndpoint: isServiceModeConfig(config) ? config.serviceEndpoint : void 0,
|
|
16323
16379
|
// Hand lit-ops a session-aware auth getter so every request it makes
|
|
@@ -16995,6 +17051,19 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
16995
17051
|
}
|
|
16996
17052
|
const MAX_FULL_RETRIES = 3;
|
|
16997
17053
|
for (let fullRetry = 1; fullRetry <= MAX_FULL_RETRIES; fullRetry++) {
|
|
17054
|
+
if (await this.isLocalLaneGuardActive()) {
|
|
17055
|
+
const phase = Math.floor(Date.now() / 1e3) % 60;
|
|
17056
|
+
if (phase > 30) {
|
|
17057
|
+
const waitMs = (60 - phase + 1) * 1e3;
|
|
17058
|
+
if (this.config.debug) {
|
|
17059
|
+
log.info(
|
|
17060
|
+
`\u23F3 Quantum-phase alignment (lane-only): ${phase}s into the quantum \u2014 waiting ${Math.ceil(waitMs / 1e3)}s for the next boundary before authorization`,
|
|
17061
|
+
{}
|
|
17062
|
+
);
|
|
17063
|
+
}
|
|
17064
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
17065
|
+
}
|
|
17066
|
+
}
|
|
16998
17067
|
if (fullRetry > 1 && this.config.debug) {
|
|
16999
17068
|
log.info(
|
|
17000
17069
|
`\u{1F504} Full mint retry ${fullRetry}/${MAX_FULL_RETRIES} due to timestamp staleness...`,
|
|
@@ -17743,6 +17812,7 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
17743
17812
|
from: fromAddress,
|
|
17744
17813
|
data: mintCalldata,
|
|
17745
17814
|
quantumTimestamp: Number(validationResponse.timestamp),
|
|
17815
|
+
laneGuardActive: await this.isLocalLaneGuardActive(),
|
|
17746
17816
|
onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
|
|
17747
17817
|
});
|
|
17748
17818
|
} catch (simError) {
|
|
@@ -19038,6 +19108,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
19038
19108
|
from: await extendSigner.getAddress(),
|
|
19039
19109
|
data: extendCalldata,
|
|
19040
19110
|
quantumTimestamp: Number(quantumTimestamp),
|
|
19111
|
+
laneGuardActive: await this.isLocalLaneGuardActive(),
|
|
19041
19112
|
onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
|
|
19042
19113
|
});
|
|
19043
19114
|
} catch (simError) {
|
|
@@ -19938,6 +20009,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
19938
20009
|
from: paymentFrom,
|
|
19939
20010
|
data: paymentCalldata,
|
|
19940
20011
|
quantumTimestamp: Number(quantumTimestamp),
|
|
20012
|
+
laneGuardActive: await this.isLocalLaneGuardActive(),
|
|
19941
20013
|
onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
|
|
19942
20014
|
});
|
|
19943
20015
|
} catch (simError) {
|
|
@@ -20677,6 +20749,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
20677
20749
|
from: await withdrawSigner.getAddress(),
|
|
20678
20750
|
data: withdrawCalldata,
|
|
20679
20751
|
quantumTimestamp: Number(withdrawalParams.quantumTimestamp),
|
|
20752
|
+
laneGuardActive: await this.isLocalLaneGuardActive(),
|
|
20680
20753
|
onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
|
|
20681
20754
|
});
|
|
20682
20755
|
tx = await sendEip1559Transaction({
|
|
@@ -20754,7 +20827,9 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
20754
20827
|
gasUsed: receipt.gasUsed?.toString() || "unknown",
|
|
20755
20828
|
utxoTxid: validationResponse.utxoTxid,
|
|
20756
20829
|
utxoVout: validationResponse.utxoVout,
|
|
20757
|
-
utxoSatoshis: validationResponse.utxoSatoshis
|
|
20830
|
+
utxoSatoshis: validationResponse.utxoSatoshis,
|
|
20831
|
+
// Phase B: full consensus-validated spend set (absent on older actions).
|
|
20832
|
+
confirmedUtxos: validationResponse.confirmedUtxos
|
|
20758
20833
|
};
|
|
20759
20834
|
} catch (error) {
|
|
20760
20835
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
@@ -20829,15 +20904,180 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
20829
20904
|
* @param request - Execution request with positionId, utxoIdentifier, and networkFee
|
|
20830
20905
|
* @returns Execution result with Bitcoin txid
|
|
20831
20906
|
*/
|
|
20832
|
-
|
|
20833
|
-
|
|
20834
|
-
|
|
20907
|
+
/**
|
|
20908
|
+
* STANDALONE Phase 2: sign + broadcast the withdrawal Bitcoin transaction
|
|
20909
|
+
* in-process — the same orchestration lit-ops-server performs, composed
|
|
20910
|
+
* from the same primitives (`litOps.signBTCTransaction`, which has always
|
|
20911
|
+
* had a standalone path, and `broadcastSignedBitcoinTransaction`).
|
|
20912
|
+
* Both modes must support withdrawal execution (Master mandate 2026-07-21).
|
|
20913
|
+
*
|
|
20914
|
+
* The service path's post-broadcast CRIT-2 re-verification exists to catch
|
|
20915
|
+
* a LYING SERVER; here the broadcast happens locally from the signatures we
|
|
20916
|
+
* just obtained, so the esplora-returned txid is already first-hand.
|
|
20917
|
+
*/
|
|
20918
|
+
async executeBTCWithdrawalStandalonePhase2(request, envelope, txid, vout, chainId) {
|
|
20919
|
+
const positionDetails = await this.getPositionDetailsView(
|
|
20920
|
+
request.positionId
|
|
20921
|
+
);
|
|
20922
|
+
if (!positionDetails) {
|
|
20923
|
+
return { success: false, error: `Position ${request.positionId} not found` };
|
|
20924
|
+
}
|
|
20925
|
+
const pkpId = positionDetails.pkpId;
|
|
20926
|
+
const vaultAddress = positionDetails.vaultAddress;
|
|
20927
|
+
if (!pkpId || pkpId === import_ethers14.ZeroHash) {
|
|
20928
|
+
return { success: false, error: "Position has no PKP" };
|
|
20929
|
+
}
|
|
20930
|
+
if (!vaultAddress) {
|
|
20931
|
+
return { success: false, error: "Position has no vault address" };
|
|
20932
|
+
}
|
|
20933
|
+
const pkpCache = this.cacheManager.getCache("pkp-data", {
|
|
20934
|
+
maxSize: 500,
|
|
20935
|
+
ttlMs: 24 * 60 * 60 * 1e3
|
|
20936
|
+
});
|
|
20937
|
+
let publicKey = pkpCache.get(request.positionId)?.publicKey;
|
|
20938
|
+
if (!publicKey) {
|
|
20939
|
+
const derived = await this.litOps.deriveDiamondHandsLoanPkpPublicKey({
|
|
20940
|
+
pkpId
|
|
20941
|
+
});
|
|
20942
|
+
publicKey = derived.publicKey;
|
|
20943
|
+
}
|
|
20944
|
+
if (!publicKey) {
|
|
20945
|
+
return { success: false, error: "Failed to resolve PKP public key" };
|
|
20946
|
+
}
|
|
20947
|
+
const chipotlePkpAddress = (0, import_ethers14.computeAddress)(publicKey);
|
|
20948
|
+
const STANDALONE_CHAIN_NAMES = {
|
|
20949
|
+
1: "ethereum",
|
|
20950
|
+
11155111: "sepolia",
|
|
20951
|
+
1337: "hardhat",
|
|
20952
|
+
31337: "hardhat"
|
|
20953
|
+
};
|
|
20954
|
+
const chainName = STANDALONE_CHAIN_NAMES[chainId];
|
|
20955
|
+
if (!chainName) {
|
|
20835
20956
|
return {
|
|
20836
20957
|
success: false,
|
|
20837
|
-
error:
|
|
20958
|
+
error: `Unsupported chainId ${chainId} for standalone BTC withdrawal (supported: 1, 11155111, 1337, 31337)`
|
|
20838
20959
|
};
|
|
20839
20960
|
}
|
|
20840
|
-
|
|
20961
|
+
const bitcoinNetworkName = chainId === 1 ? "mainnet" : "regtest";
|
|
20962
|
+
const bitcoin = await import("bitcoinjs-lib");
|
|
20963
|
+
const bitcoinNetwork = chainId === 1 ? bitcoin.networks.bitcoin : chainId === 1337 || chainId === 31337 ? bitcoin.networks.regtest : bitcoin.networks.testnet;
|
|
20964
|
+
let scriptPubKeyBuffer;
|
|
20965
|
+
if (vaultAddress.startsWith("bcrt1q")) {
|
|
20966
|
+
const bech322 = await import("bech32");
|
|
20967
|
+
const decoded = bech322.bech32.decode(vaultAddress);
|
|
20968
|
+
const witnessProgram = Buffer.from(
|
|
20969
|
+
bech322.bech32.fromWords(decoded.words.slice(1))
|
|
20970
|
+
);
|
|
20971
|
+
if (witnessProgram.length !== 20) {
|
|
20972
|
+
return {
|
|
20973
|
+
success: false,
|
|
20974
|
+
error: `Vault address ${vaultAddress} decodes to a ${witnessProgram.length}-byte witness program \u2014 expected 20-byte P2WPKH`
|
|
20975
|
+
};
|
|
20976
|
+
}
|
|
20977
|
+
scriptPubKeyBuffer = Buffer.concat([
|
|
20978
|
+
Buffer.from([0, witnessProgram.length]),
|
|
20979
|
+
witnessProgram
|
|
20980
|
+
]);
|
|
20981
|
+
} else {
|
|
20982
|
+
scriptPubKeyBuffer = bitcoin.address.toOutputScript(
|
|
20983
|
+
vaultAddress,
|
|
20984
|
+
bitcoinNetwork
|
|
20985
|
+
);
|
|
20986
|
+
}
|
|
20987
|
+
const scriptPubKey = scriptPubKeyBuffer.toString("hex");
|
|
20988
|
+
const executionUtxos = (envelope.utxos && envelope.utxos.length ? envelope.utxos : [{ txid, vout, value: request.utxoSatoshis }]).map((u) => ({ ...u, scriptPubKey }));
|
|
20989
|
+
const dc = this.config.contractAddresses || {};
|
|
20990
|
+
const signerContractAddresses = {
|
|
20991
|
+
LoanOperationsManagerModule: dc.loanOperationsManager ?? "",
|
|
20992
|
+
PositionManager: dc.positionManager ?? "",
|
|
20993
|
+
BTCSpendAuthorizer: dc.btcSpendAuthorizer ?? "",
|
|
20994
|
+
PositionDelegateRegistry: dc.positionDelegateRegistry ?? "",
|
|
20995
|
+
OperationAuthorizationRegistry: dc.operationAuthorizationRegistry ?? "",
|
|
20996
|
+
...dc.bitcoinProviderRegistry ? { BitcoinProviderRegistry: dc.bitcoinProviderRegistry } : {}
|
|
20997
|
+
};
|
|
20998
|
+
const signResult = await this.litOps.signBTCTransaction({
|
|
20999
|
+
auth: {
|
|
21000
|
+
positionId: envelope.positionId,
|
|
21001
|
+
utxo: { txid, vout, value: request.utxoSatoshis, scriptPubKey },
|
|
21002
|
+
utxos: executionUtxos,
|
|
21003
|
+
networkFee: envelope.networkFee,
|
|
21004
|
+
targetAddress: envelope.targetAddress,
|
|
21005
|
+
targetAmount: envelope.targetAmount,
|
|
21006
|
+
timestamp: envelope.timestamp,
|
|
21007
|
+
signature: envelope.userSignature,
|
|
21008
|
+
chainId
|
|
21009
|
+
},
|
|
21010
|
+
publicKey,
|
|
21011
|
+
pkpId: chipotlePkpAddress,
|
|
21012
|
+
chain: chainName,
|
|
21013
|
+
bitcoinNetwork: bitcoinNetworkName,
|
|
21014
|
+
rpcUrl: this.config.ethRpcUrl,
|
|
21015
|
+
contractAddresses: signerContractAddresses
|
|
21016
|
+
});
|
|
21017
|
+
if (!signResult.success) {
|
|
21018
|
+
return {
|
|
21019
|
+
success: false,
|
|
21020
|
+
error: signResult.error ?? "BTC transaction signing failed"
|
|
21021
|
+
};
|
|
21022
|
+
}
|
|
21023
|
+
if (!signResult.unsignedTxHex || !signResult.signatures?.length) {
|
|
21024
|
+
return {
|
|
21025
|
+
success: false,
|
|
21026
|
+
error: "BTC signing returned incomplete data (missing unsignedTxHex/signatures)"
|
|
21027
|
+
};
|
|
21028
|
+
}
|
|
21029
|
+
if (!signResult.userReceivesAmount) {
|
|
21030
|
+
return { success: false, error: "BTC signing did not return userReceivesAmount" };
|
|
21031
|
+
}
|
|
21032
|
+
const broadcastUrl = this.config.bitcoinProviders?.[0]?.url;
|
|
21033
|
+
if (!broadcastUrl) {
|
|
21034
|
+
return {
|
|
21035
|
+
success: false,
|
|
21036
|
+
error: "Standalone Phase 2 broadcast requires config.bitcoinProviders[0].url (esplora base)"
|
|
21037
|
+
};
|
|
21038
|
+
}
|
|
21039
|
+
try {
|
|
21040
|
+
const { txid: btcTxid } = await (0, import_dh_lit_ops.broadcastSignedBitcoinTransaction)({
|
|
21041
|
+
signatures: signResult.signatures,
|
|
21042
|
+
utxos: executionUtxos,
|
|
21043
|
+
pkpPublicKey: signResult.pkpPublicKey ?? publicKey,
|
|
21044
|
+
destination: envelope.targetAddress,
|
|
21045
|
+
userReceivesAmount: signResult.userReceivesAmount,
|
|
21046
|
+
changeAmount: signResult.changeAmount,
|
|
21047
|
+
pkpBtcAddress: signResult.pkpBtcAddress,
|
|
21048
|
+
bitcoinNetwork: bitcoinNetworkName,
|
|
21049
|
+
bitcoinRpcUrl: broadcastUrl,
|
|
21050
|
+
...this.config.debug ? {
|
|
21051
|
+
debug: (level, msg, data) => {
|
|
21052
|
+
if (level === "warn")
|
|
21053
|
+
log.warn(msg, { data });
|
|
21054
|
+
else if (level === "error")
|
|
21055
|
+
log.error(msg, { data });
|
|
21056
|
+
else
|
|
21057
|
+
log.info(msg, { data });
|
|
21058
|
+
}
|
|
21059
|
+
} : {}
|
|
21060
|
+
});
|
|
21061
|
+
if (this.config.debug) {
|
|
21062
|
+
log.info("\u2705 Standalone Phase 2 broadcast successful", { txid: btcTxid });
|
|
21063
|
+
}
|
|
21064
|
+
return {
|
|
21065
|
+
success: true,
|
|
21066
|
+
txid: btcTxid,
|
|
21067
|
+
amountSent: signResult.userReceivesAmount,
|
|
21068
|
+
networkFee: signResult.networkFee,
|
|
21069
|
+
destination: envelope.targetAddress
|
|
21070
|
+
};
|
|
21071
|
+
} catch (broadcastError) {
|
|
21072
|
+
return {
|
|
21073
|
+
success: false,
|
|
21074
|
+
error: `BTC broadcast failed: ${broadcastError instanceof Error ? broadcastError.message : String(broadcastError)}`
|
|
21075
|
+
};
|
|
21076
|
+
}
|
|
21077
|
+
}
|
|
21078
|
+
async executeBTCWithdrawal(request) {
|
|
21079
|
+
this.ensureInitialized();
|
|
21080
|
+
if (this.config.mode === "service" && !this.config.serviceEndpoint) {
|
|
20841
21081
|
return { success: false, error: "serviceEndpoint required for BTC withdrawal" };
|
|
20842
21082
|
}
|
|
20843
21083
|
if (!request.positionId) {
|
|
@@ -20892,6 +21132,15 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
20892
21132
|
signer,
|
|
20893
21133
|
...request.utxos ? { utxos: request.utxos } : {}
|
|
20894
21134
|
});
|
|
21135
|
+
if (this.config.mode !== "service") {
|
|
21136
|
+
return await this.executeBTCWithdrawalStandalonePhase2(
|
|
21137
|
+
request,
|
|
21138
|
+
envelope,
|
|
21139
|
+
txid,
|
|
21140
|
+
vout,
|
|
21141
|
+
chainId
|
|
21142
|
+
);
|
|
21143
|
+
}
|
|
20895
21144
|
const headers = {
|
|
20896
21145
|
"Content-Type": "application/json",
|
|
20897
21146
|
...await this.getAuthHeader()
|
|
@@ -21437,7 +21686,12 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21437
21686
|
log.info("\u{1F50D} Found vault UTXO for execution", { utxoIdentifier });
|
|
21438
21687
|
}
|
|
21439
21688
|
let executionUtxos;
|
|
21440
|
-
const gatheredUtxos =
|
|
21689
|
+
const gatheredUtxos = authResult.confirmedUtxos && authResult.confirmedUtxos.length > 0 ? authResult.confirmedUtxos.map((u) => ({
|
|
21690
|
+
txid: u.txid,
|
|
21691
|
+
vout: Number(u.vout),
|
|
21692
|
+
value: Number(u.value),
|
|
21693
|
+
confirmations: 0
|
|
21694
|
+
})) : await this.fetchConfirmedVaultUtxos(positionId);
|
|
21441
21695
|
if (gatheredUtxos.length > 0) {
|
|
21442
21696
|
const normTxid = (t) => String(t).toLowerCase().replace(/^0x/, "");
|
|
21443
21697
|
const byOutpoint = /* @__PURE__ */ new Map();
|
|
@@ -22204,6 +22458,25 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
22204
22458
|
const chain = this.config.chain?.toLowerCase() || "";
|
|
22205
22459
|
return chain.includes("mainnet");
|
|
22206
22460
|
}
|
|
22461
|
+
/**
|
|
22462
|
+
* Local-lane activation, verified against the LIVE node at call time (not
|
|
22463
|
+
* the chainId cached at init — a provider that switched networks after
|
|
22464
|
+
* init fails the fresh probe). Zero RPC cost outside a lane process tree:
|
|
22465
|
+
* without LIT_LANE_GUARD=1 this short-circuits to false before any probe.
|
|
22466
|
+
*/
|
|
22467
|
+
async isLocalLaneGuardActive() {
|
|
22468
|
+
if (!laneGuardEnvEnabled())
|
|
22469
|
+
return false;
|
|
22470
|
+
let provider;
|
|
22471
|
+
try {
|
|
22472
|
+
provider = this.config.provider ?? this.getProviderOrThrow();
|
|
22473
|
+
} catch {
|
|
22474
|
+
return false;
|
|
22475
|
+
}
|
|
22476
|
+
return isLaneGuardActiveOnProvider(
|
|
22477
|
+
provider
|
|
22478
|
+
);
|
|
22479
|
+
}
|
|
22207
22480
|
/**
|
|
22208
22481
|
* Get Bitcoin network based on chain.
|
|
22209
22482
|
*
|