@gvnrdao/dh-sdk 0.0.300 → 0.0.301

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/index.mjs CHANGED
@@ -422,6 +422,20 @@ var init_deployment_addresses = __esm({
422
422
  upgradedAt: "2026-06-16T17:53:24.979Z",
423
423
  upgradedBy: "0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f",
424
424
  reason: "Initial PSM deploy / artifact sync"
425
+ },
426
+ CollateralManagerModule: {
427
+ previousImplementation: "0x1919860e528Cd2AbbD31a17858c986b8aCc8C185",
428
+ newImplementation: "0x55110B9dd6B2c9f812564e7a1ef42b3c76E1A3Bb",
429
+ upgradedAt: "2026-07-08T20:33:59Z",
430
+ upgradedBy: "AdminModule generic-op 0x740efe0cfe85044479cbd8ee3058aa244057d90703dd2b37044dd6787cbf3485 (3-of-5 ADMIN_ROLE Safes, 24h timelock; executed by 0xfb3B8cbeFB7D666A6Af34C53e189EdC31862a7be in tx 0x430c8e450f12d3d9359cde356ac5e2ef39661d7a89884b5674a596c1090f6cff)",
431
+ reason: "Validator gas fix (#420): BitcoinAddressValidator packed-table lookups; VERSION 1.3.0 -> 1.3.1"
432
+ },
433
+ BitcoinWithdrawalAddressRegistry: {
434
+ previousImplementation: "0x11731bFEC3068F2cc19D016381dfEA3B93013b1C",
435
+ newImplementation: "0x190c08300Ae650fb88ed8156445BBeb13bE9E8a3",
436
+ upgradedAt: "2026-07-08T20:33:59Z",
437
+ upgradedBy: "AdminModule generic-op 0x2603a486c0f34554b864cc1d909b325c7e105331da10ec0fd7e0b9373fa76207 (3-of-5 ADMIN_ROLE Safes, 24h timelock; executed by 0xfb3B8cbeFB7D666A6Af34C53e189EdC31862a7be in tx 0x430c8e450f12d3d9359cde356ac5e2ef39661d7a89884b5674a596c1090f6cff)",
438
+ reason: "Validator gas fix (#420): BitcoinAddressValidator packed-table lookups; VERSION 1.0.0 -> 1.0.1"
425
439
  }
426
440
  },
427
441
  latestEnv: {
@@ -1196,17 +1210,17 @@ var require_pkg_src = __commonJS({
1196
1210
  }
1197
1211
  },
1198
1212
  btcTransactionSigner: {
1199
- cid: "QmZs56sVTCJv7mgvyRYfpYHFvFLcSeXa4H44qUYT7z14QP",
1213
+ cid: "QmcPG9C5Yx9JGrBKCfSVWbKuiRXn8XFwb91yNxQqY4VSJD",
1200
1214
  authorizedCidHex: cidToHex(
1201
- "QmZs56sVTCJv7mgvyRYfpYHFvFLcSeXa4H44qUYT7z14QP"
1215
+ "QmcPG9C5Yx9JGrBKCfSVWbKuiRXn8XFwb91yNxQqY4VSJD"
1202
1216
  ),
1203
1217
  name: "Btc Transaction Signer",
1204
1218
  description: "Production Btc Transaction Signer",
1205
- version: "1.0.0",
1219
+ version: "1.0.1",
1206
1220
  deployed: true,
1207
- deployedAt: 1781542204806,
1208
- size: 42376,
1209
- hash: "f4aed3d88f6fbff6623ed2047436cecd4f561a74883171ea6f4ebe0a1129973c"
1221
+ deployedAt: 1783451891948,
1222
+ size: 43688,
1223
+ hash: "1ea0f0ac66cc68fc9b098dabb3c5e3ba96c25f13b133b95a06eac477028fdb1a"
1210
1224
  },
1211
1225
  ucdMintValidator: {
1212
1226
  cid: "QmPir8F8jmJ4ssQpEC3xFxkEnZxMHcgS6VxBQE1jkTLj5K",
@@ -2146,7 +2160,7 @@ ${errorReport}`);
2146
2160
  }
2147
2161
  init_debug_logger();
2148
2162
  init_session_signature_cache();
2149
- var import_ethers15 = __require("ethers");
2163
+ var import_ethers16 = __require("ethers");
2150
2164
  var EXPIRED_LOAN_MIN_LIQUIDATION_THRESHOLD_BPS = 11e3;
2151
2165
  var GRACE_PERIOD_DAYS = 30;
2152
2166
  var SATOSHIS_PER_BITCOIN = 100000000n;
@@ -4381,12 +4395,12 @@ ${auth.clientId}`;
4381
4395
  }
4382
4396
  async function executeVaultSnapshot(params) {
4383
4397
  global.ethers = {
4384
- ...import_ethers15.ethers,
4398
+ ...import_ethers16.ethers,
4385
4399
  providers: {
4386
- StaticJsonRpcProvider: import_ethers15.ethers.JsonRpcProvider
4400
+ StaticJsonRpcProvider: import_ethers16.ethers.JsonRpcProvider
4387
4401
  },
4388
- Contract: import_ethers15.ethers.Contract,
4389
- utils: import_ethers15.ethers
4402
+ Contract: import_ethers16.ethers.Contract,
4403
+ utils: import_ethers16.ethers
4390
4404
  // v6 moved utils to top level
4391
4405
  };
4392
4406
  global.Lit = {
@@ -4837,7 +4851,7 @@ var init_network_configs = __esm({
4837
4851
  });
4838
4852
 
4839
4853
  // src/modules/diamond-hands-sdk.ts
4840
- import { Contract as Contract6, Interface as Interface4, JsonRpcProvider, AbiCoder, keccak256 as keccak2565, solidityPackedKeccak256 as solidityPackedKeccak2564, toUtf8Bytes as toUtf8Bytes5, zeroPadValue as zeroPadValue4, toBeHex as toBeHex2, parseEther, formatEther, formatUnits, computeAddress, Signature, ZeroHash, ZeroAddress as ZeroAddress3, MaxUint256 } from "ethers";
4854
+ import { Contract as Contract6, Interface as Interface4, JsonRpcProvider, AbiCoder as AbiCoder2, keccak256 as keccak2565, solidityPackedKeccak256 as solidityPackedKeccak2564, toUtf8Bytes as toUtf8Bytes5, zeroPadValue as zeroPadValue4, toBeHex as toBeHex2, parseEther, formatEther, formatUnits, computeAddress, Signature, ZeroHash, ZeroAddress as ZeroAddress3, MaxUint256 } from "ethers";
4841
4855
 
4842
4856
  // src/types/result.ts
4843
4857
  function success(value) {
@@ -5221,22 +5235,12 @@ var SDKError = class _SDKError extends Error {
5221
5235
 
5222
5236
  // src/utils/chunks/eip1559-broadcast.utils.ts
5223
5237
  import { parseUnits } from "ethers";
5224
- var MINT_UCD_GAS_CEILING = BigInt(2e6);
5238
+ var MINT_UCD_GAS_CEILING = BigInt(13e5);
5225
5239
  var WITHDRAW_BTC_GAS_CEILING = BigInt(3e6);
5226
5240
  var MAKE_PAYMENT_GAS_CEILING = BigInt(1e6);
5227
5241
  var EXTEND_POSITION_GAS_CEILING = BigInt(1e6);
5228
5242
  var LIQUIDATION_COMMIT_GAS_CEILING = BigInt(1e6);
5229
5243
  var LIQUIDATION_REVEAL_GAS_CEILING = BigInt(2e6);
5230
- var MINT_UCD_ESTIMATE_MARGIN_BPS = 2500;
5231
- async function estimateContractCallGasWithMargin(provider, from, to, data, marginBps, ceiling) {
5232
- try {
5233
- const estimated = await provider.estimateGas({ from, to, data });
5234
- const withMargin = estimated * BigInt(1e4 + marginBps) / 10000n;
5235
- return withMargin > ceiling ? ceiling : withMargin;
5236
- } catch {
5237
- return null;
5238
- }
5239
- }
5240
5244
  async function resolveEip1559FeeFields(provider) {
5241
5245
  const feeData = await provider.getFeeData();
5242
5246
  let maxFeePerGas = feeData.maxFeePerGas ?? void 0;
@@ -5840,7 +5844,10 @@ var ServerSession = class {
5840
5844
  };
5841
5845
 
5842
5846
  // src/utils/quantum-revert.utils.ts
5847
+ import { AbiCoder } from "ethers";
5843
5848
  var DEAD_ZONE_VIOLATION_SELECTOR = "0xbe4b82c1";
5849
+ var ERROR_STRING_SELECTOR = "0x08c379a0";
5850
+ var PANIC_SELECTOR = "0x4e487b71";
5844
5851
  var QUANTUM_REVERT_NAMES = {
5845
5852
  "0xbe4b82c1": "DeadZoneViolation()",
5846
5853
  "0x137f3b70": "InDeadZone()",
@@ -5864,10 +5871,94 @@ function decodeQuantumRevert(e) {
5864
5871
  e?.error?.data?.data ?? e?.error?.data ?? e?.error?.error?.data;
5865
5872
  const data = typeof raw === "string" && raw.startsWith("0x") ? raw : null;
5866
5873
  const selector = data && data.length >= 10 ? data.slice(0, 10) : null;
5867
- const name = selector ? QUANTUM_REVERT_NAMES[selector] ?? null : null;
5874
+ let name = selector ? QUANTUM_REVERT_NAMES[selector] ?? null : null;
5875
+ if (!name && selector && data) {
5876
+ try {
5877
+ if (selector === ERROR_STRING_SELECTOR) {
5878
+ const [message] = AbiCoder.defaultAbiCoder().decode(
5879
+ ["string"],
5880
+ "0x" + data.slice(10)
5881
+ );
5882
+ name = `Error("${message}")`;
5883
+ } else if (selector === PANIC_SELECTOR) {
5884
+ const [code] = AbiCoder.defaultAbiCoder().decode(
5885
+ ["uint256"],
5886
+ "0x" + data.slice(10)
5887
+ );
5888
+ name = `Panic(0x${code.toString(16)})`;
5889
+ }
5890
+ } catch {
5891
+ }
5892
+ }
5868
5893
  return { data, selector, name };
5869
5894
  }
5870
5895
 
5896
+ // src/utils/quantum-submission.utils.ts
5897
+ var MAX_DEADZONE_RESIMULATIONS = 3;
5898
+ var QuantumRevertError = class extends Error {
5899
+ constructor(errorName, selector, data, cause) {
5900
+ super(
5901
+ `Pre-send simulation reverted: ${errorName ?? selector ?? "unknown"}`
5902
+ );
5903
+ this.errorName = errorName;
5904
+ this.selector = selector;
5905
+ this.data = data;
5906
+ this.cause = cause;
5907
+ this.name = "QuantumRevertError";
5908
+ }
5909
+ };
5910
+ async function assertSafeQuantumSubmission(opts) {
5911
+ const { provider, to, from, data, quantumTimestamp } = opts;
5912
+ const maxResimulations = opts.maxResimulations ?? MAX_DEADZONE_RESIMULATIONS;
5913
+ const now = opts.now ?? (() => Math.floor(Date.now() / 1e3));
5914
+ const onDebug = opts.onDebug ?? (() => {
5915
+ });
5916
+ const gateOpts = {
5917
+ now,
5918
+ ...opts.sleep ? { sleep: opts.sleep } : {}
5919
+ };
5920
+ await awaitSafeSubmissionWindow(quantumTimestamp, gateOpts);
5921
+ for (let sim = 1; ; sim++) {
5922
+ try {
5923
+ await provider.call({ to, from, data });
5924
+ break;
5925
+ } catch (simError) {
5926
+ const decoded = decodeQuantumRevert(simError);
5927
+ if (decoded.selector === DEAD_ZONE_VIOLATION_SELECTOR && sim <= maxResimulations) {
5928
+ const sigQuantum = Math.floor(quantumTimestamp / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
5929
+ const realCurrentQuantum = Math.floor(now() / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
5930
+ if (sigQuantum === realCurrentQuantum) {
5931
+ onDebug(
5932
+ "\u21AA\uFE0F Simulated DeadZoneViolation is a stale-latest-block artifact (signature is the current quantum) \u2014 proceeding to broadcast"
5933
+ );
5934
+ break;
5935
+ }
5936
+ const gate = await awaitSafeSubmissionWindow(quantumTimestamp, gateOpts);
5937
+ if (gate.waited) {
5938
+ onDebug(
5939
+ `\u{1F501} Re-simulating after crossing the quantum boundary (attempt ${sim}/${maxResimulations})`
5940
+ );
5941
+ continue;
5942
+ }
5943
+ }
5944
+ if (!decoded.data) {
5945
+ const label = simError instanceof Error ? simError.message : String(simError);
5946
+ onDebug(
5947
+ `\u26A0\uFE0F Pre-send simulation failed without revert data (${label}) \u2014 proceeding to broadcast`
5948
+ );
5949
+ break;
5950
+ }
5951
+ throw new QuantumRevertError(
5952
+ decoded.name,
5953
+ decoded.selector,
5954
+ decoded.data,
5955
+ simError
5956
+ );
5957
+ }
5958
+ }
5959
+ await awaitSafeSubmissionWindow(quantumTimestamp, gateOpts);
5960
+ }
5961
+
5871
5962
  // src/utils/btc-withdrawal-message.ts
5872
5963
  import { keccak256 as keccak2563, toUtf8Bytes as toUtf8Bytes3, solidityPackedKeccak256 as solidityPackedKeccak2563, getBytes as getBytes3, zeroPadValue } from "ethers";
5873
5964
  var QUANTUM_SECONDS = 60;
@@ -16223,8 +16314,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
16223
16314
  throw new Error(`Position not found: ${request.positionId}`);
16224
16315
  }
16225
16316
  const currentDebt = position.ucdDebt.toString();
16226
- const ucdDebtHash = keccak2565(AbiCoder.defaultAbiCoder().encode(["uint256"], [currentDebt]));
16227
- const contractHash = keccak2565(AbiCoder.defaultAbiCoder().encode(
16317
+ const ucdDebtHash = keccak2565(AbiCoder2.defaultAbiCoder().encode(["uint256"], [currentDebt]));
16318
+ const contractHash = keccak2565(AbiCoder2.defaultAbiCoder().encode(
16228
16319
  ["address", "address", "address", "address"],
16229
16320
  [
16230
16321
  positionManagerAddress,
@@ -16285,214 +16376,6 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
16285
16376
  {}
16286
16377
  );
16287
16378
  }
16288
- if (this.config.debug) {
16289
- log.info("\u{1F50D} PREFLIGHT: Attempting diagnostic gas estimation...", {});
16290
- }
16291
- try {
16292
- const iface = new Interface4(positionManagerAbi);
16293
- const signatureHex = finalSignature.startsWith("0x") ? finalSignature : "0x" + finalSignature;
16294
- if (this.config.debug) {
16295
- log.info("\u{1F50D} DEBUG: Signature details:", {
16296
- finalSignature: finalSignature ? `${finalSignature.substring(0, 20)}... (length: ${finalSignature.length})` : "UNDEFINED/EMPTY",
16297
- signatureHex: signatureHex ? `${signatureHex.substring(0, 22)}... (length: ${signatureHex.length})` : "UNDEFINED/EMPTY"
16298
- });
16299
- }
16300
- const calldata = iface.encodeFunctionData("mintUCD", [
16301
- positionIdBytes32,
16302
- validationResponse.mintAmount,
16303
- validationResponse.mintFee,
16304
- validationResponse.newDebt,
16305
- validationResponse.newCollateral,
16306
- validationResponse.btcPrice,
16307
- validationResponse.authorizedSpendsHash,
16308
- ucdDebtHash,
16309
- contractHash,
16310
- validationResponse.timestamp,
16311
- signatureHex
16312
- ]);
16313
- if (this.config.debug) {
16314
- log.info("\u{1F50D} DEBUG: Encoded calldata:", {
16315
- length: calldata.length,
16316
- signaturePortion: calldata.substring(650, 670)
16317
- });
16318
- }
16319
- const provider = this.getSignerOrThrow().provider;
16320
- if (!provider) {
16321
- throw new Error("Provider not available");
16322
- }
16323
- const signerAddress = await this.getSignerOrThrow().getAddress();
16324
- if (this.config.debug) {
16325
- log.info(" Executing diagnostic eth_call...", {});
16326
- }
16327
- await provider.call({
16328
- to: positionManagerAddress,
16329
- from: signerAddress,
16330
- data: calldata
16331
- });
16332
- if (this.config.debug) {
16333
- log.info(" \u2705 Diagnostic eth_call succeeded!", {});
16334
- }
16335
- } catch (preflightError) {
16336
- if (this.config.debug) {
16337
- log.error(
16338
- "\u274C PREFLIGHT CHECK FAILED - Diagnostic eth_call reverted",
16339
- {}
16340
- );
16341
- log.error(` Error message: ${preflightError?.message}`, {});
16342
- log.error(` Error code: ${preflightError?.code}`, {});
16343
- log.error(` Error data: ${preflightError?.data || "none"}`, {});
16344
- log.error(` Error reason: ${preflightError?.reason || "none"}`, {});
16345
- log.error(
16346
- ` Error.error: ${JSON.stringify(preflightError?.error || {})}`,
16347
- {}
16348
- );
16349
- }
16350
- let errorData = preflightError?.data || preflightError?.info?.error?.data || preflightError?.error?.data?.data || preflightError?.error?.data || preflightError?.error?.error?.data;
16351
- const errorMessage = preflightError?.reason || preflightError?.error?.reason || preflightError?.error?.error?.reason || preflightError?.message || "Unknown error";
16352
- let requireMessage;
16353
- if (errorData && typeof errorData === "string" && errorData.startsWith("0x") && errorData.length > 138) {
16354
- try {
16355
- const errorSelector = errorData.slice(0, 10);
16356
- if (errorSelector === "0x08c379a0") {
16357
- const iface = new Interface4([
16358
- "function Error(string)"
16359
- ]);
16360
- const decoded = iface.decodeFunctionData("Error", errorData);
16361
- requireMessage = decoded[0];
16362
- }
16363
- } catch (e) {
16364
- }
16365
- }
16366
- const isDeadZonePreflightArtifact = decodeQuantumRevert(preflightError).selector === DEAD_ZONE_VIOLATION_SELECTOR;
16367
- if (isDeadZonePreflightArtifact) {
16368
- const nowSec = Math.floor(Date.now() / 1e3);
16369
- const sigQuantum2 = Math.floor(Number(validationResponse.timestamp) / 60) * 60;
16370
- const realCurrentQuantum = Math.floor(nowSec / 60) * 60;
16371
- if (sigQuantum2 === realCurrentQuantum) {
16372
- if (this.config.debug) {
16373
- log.info(
16374
- "\u21AA\uFE0F Preflight DeadZoneViolation is a stale-latest-block artifact (signature is the current quantum) \u2014 proceeding to broadcast",
16375
- {}
16376
- );
16377
- }
16378
- } else {
16379
- if (this.config.debug) {
16380
- log.info(
16381
- "\u{1F501} Preflight DeadZoneViolation near the boundary \u2014 deferring across it, then proceeding",
16382
- {}
16383
- );
16384
- }
16385
- await awaitSafeSubmissionWindow(
16386
- Number(validationResponse.timestamp)
16387
- );
16388
- }
16389
- }
16390
- if (requireMessage && !isDeadZonePreflightArtifact) {
16391
- throw new Error(
16392
- `Preflight check failed - mintUCD would revert:
16393
- ${requireMessage}
16394
- Context: Quantum timestamp=${validationResponse.timestamp}, Position=${request.positionId}`
16395
- );
16396
- }
16397
- if (errorData && typeof errorData === "string" && errorData.startsWith("0x") && errorData.length > 10) {
16398
- const selector = errorData.slice(0, 10);
16399
- const knownErrors = {
16400
- "0x8baa579f": "InvalidSignature()",
16401
- "0xd92e233d": "ValidationFailed()",
16402
- "0x3ee5aeb5": "OperationNotAuthorized()",
16403
- "0x70f4b32a": "UnauthorizedSigner()",
16404
- "0x48f5c3ed": "Unauthorized()",
16405
- "0x36bfdeea": "StateHashMismatch()",
16406
- "0x131d9a21": "QuantumExpired()",
16407
- "0x52ce5d58": "QuantumAlreadyUsed()",
16408
- "0x137f3b70": "InDeadZone()",
16409
- "0xbe4b82c1": "DeadZoneViolation()",
16410
- "0x3e76a3c9": "QuantumOutsideWindow()",
16411
- "0x62278171": "InvalidValidatorSignature()",
16412
- "0xb9d419a7": "DebtUpdateVerificationFailed()",
16413
- "0xc7e20553": "DebtUpdateVerificationFailedDetailed(bytes32,uint256,uint256,uint256,uint256,uint256)",
16414
- "0x0cfd2a97": "MintVerificationMismatch(uint256,uint256)",
16415
- "0x07637bd8": "MintFailed()"
16416
- };
16417
- const errorName = knownErrors[selector] || `Unknown error ${selector}`;
16418
- const mintDebtDiagnostics = selector === "0xb9d419a7" || selector === "0xc7e20553" || selector === "0x0cfd2a97" || selector === "0x07637bd8" ? `
16419
-
16420
- Mint/debt diagnostics (LoanOperationsManager mint path \u2192 increaseDebtFromMint):
16421
- positionId: ${request.positionId}
16422
- on-chain ucdDebt before tx: ${currentDebt}
16423
- Lit newDebt: ${validationResponse.newDebt}
16424
- Lit mintAmount: ${validationResponse.mintAmount}
16425
- Lit mintFee: ${validationResponse.mintFee}
16426
- Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
16427
- Contract expects UCD supply increase to match mintAmount+mintFee and debt update to newDebt.
16428
- ` : "";
16429
- let quantumContext = "";
16430
- if (selector === "0x131d9a21" || selector === "0x52ce5d58" || selector === "0x137f3b70" || selector === "0xbe4b82c1" || selector === "0x3e76a3c9") {
16431
- const currentTime2 = Math.floor(Date.now() / 1e3);
16432
- const currentQuantum = Math.floor(currentTime2 / 60) * 60;
16433
- const sigQuantum2 = Math.floor(validationResponse.timestamp / 60) * 60;
16434
- const timeDiff = currentTime2 - validationResponse.timestamp;
16435
- quantumContext = `
16436
-
16437
- Quantum Timing Analysis:
16438
- Signature created: ${validationResponse.timestamp} (${new Date(
16439
- validationResponse.timestamp * 1e3
16440
- ).toISOString()})
16441
- Current time: ${currentTime2} (${new Date(
16442
- currentTime2 * 1e3
16443
- ).toISOString()})
16444
- Time elapsed: ${timeDiff} seconds
16445
- Signature quantum: ${sigQuantum2}
16446
- Current quantum: ${currentQuantum}
16447
- Quantum window: 60 seconds
16448
- Status: ${sigQuantum2 < currentQuantum ? "\u274C EXPIRED - Signature created in older quantum window" : "\u2705 VALID"}
16449
- Note: QuantumOutsideWindow can also occur if the tx is mined outside the accepted past/current/next quantum set.`;
16450
- }
16451
- if (!isDeadZonePreflightArtifact) {
16452
- throw new Error(
16453
- `Preflight check failed - mintUCD would revert:
16454
- Error: ${errorName}
16455
- Selector: ${selector}
16456
- Message: ${errorMessage}
16457
- Data: ${errorData}${quantumContext}${mintDebtDiagnostics}`
16458
- );
16459
- }
16460
- } else {
16461
- const currentTime2 = Math.floor(Date.now() / 1e3);
16462
- const timeSinceSig = currentTime2 - validationResponse.timestamp;
16463
- const currentQuantum = Math.floor(currentTime2 / 60) * 60;
16464
- const sigQuantum2 = Math.floor(validationResponse.timestamp / 60) * 60;
16465
- throw new Error(
16466
- `Preflight check failed - mintUCD would revert:
16467
- ${errorMessage}
16468
-
16469
- Error Details:
16470
- Error data: ${errorData || "0x (empty - no custom error)"}
16471
- Error code: ${preflightError?.code || "none"}
16472
-
16473
- Quantum Timing Analysis:
16474
- Signature created: ${validationResponse.timestamp} (${new Date(
16475
- validationResponse.timestamp * 1e3
16476
- ).toISOString()})
16477
- Current time: ${currentTime2} (${new Date(
16478
- currentTime2 * 1e3
16479
- ).toISOString()})
16480
- Time elapsed: ${timeSinceSig} seconds
16481
- Signature quantum: ${sigQuantum2}
16482
- Current quantum: ${currentQuantum}
16483
- Status: ${sigQuantum2 < currentQuantum ? "\u274C EXPIRED - Signature too old" : "\u2705 VALID"}
16484
-
16485
- Possible Causes (when error data is empty):
16486
- \u2022 Quantum signature expired (current=${currentQuantum}, signature=${sigQuantum2})
16487
- \u2022 Contract paused or circuit breaker active
16488
- \u2022 Position state changed since LIT Action signed
16489
- \u2022 Authorization registry validation failed
16490
- \u2022 RPC provider not returning detailed revert data
16491
-
16492
- Position: ${request.positionId}`
16493
- );
16494
- }
16495
- }
16496
16379
  try {
16497
16380
  const coreAddress = this.getContractAddressesOrThrow().positionManagerCore;
16498
16381
  if (!coreAddress) {
@@ -16510,7 +16393,7 @@ Position: ${request.positionId}`
16510
16393
  positionIdBytes32
16511
16394
  );
16512
16395
  const currentDebtHash = keccak2565(
16513
- AbiCoder.defaultAbiCoder().encode(
16396
+ AbiCoder2.defaultAbiCoder().encode(
16514
16397
  ["uint256"],
16515
16398
  [currentPosition.ucdDebt]
16516
16399
  )
@@ -16688,146 +16571,121 @@ Position: ${request.positionId}`
16688
16571
  signatureHexMint
16689
16572
  ]);
16690
16573
  const fromAddress = await signer.getAddress();
16691
- const estimatedGas = await estimateContractCallGasWithMargin(
16692
- signerProvider,
16693
- fromAddress,
16694
- positionManagerAddress,
16695
- mintCalldata,
16696
- MINT_UCD_ESTIMATE_MARGIN_BPS,
16697
- MINT_UCD_GAS_CEILING
16698
- );
16699
- let tx;
16700
- if (estimatedGas !== null) {
16701
- if (this.config.debug) {
16702
- log.info(
16703
- `\u{1F4DD} Broadcasting mintUCD (gasLimit=${estimatedGas.toString()})`,
16704
- {}
16705
- );
16706
- }
16707
- await awaitSafeSubmissionWindow(Number(validationResponse.timestamp));
16708
- tx = await sendEip1559Transaction({
16709
- signer,
16574
+ try {
16575
+ await assertSafeQuantumSubmission({
16576
+ provider: signerProvider,
16710
16577
  to: positionManagerAddress,
16578
+ from: fromAddress,
16711
16579
  data: mintCalldata,
16712
- gasLimit: estimatedGas
16580
+ quantumTimestamp: Number(validationResponse.timestamp),
16581
+ onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
16713
16582
  });
16714
- } else {
16715
- if (this.config.debug) {
16716
- log.info(
16717
- "\u{1F50D} eth_estimateGas failed for mintUCD; running static call",
16718
- {}
16719
- );
16720
- }
16721
- try {
16722
- await signerProvider.call({
16723
- to: positionManagerAddress,
16724
- from: fromAddress,
16725
- data: mintCalldata
16726
- });
16727
- } catch (callError) {
16728
- if (this.config.debug) {
16729
- log.info("\u{1F50D} DEBUG: mintUCD static call failed", {});
16730
- log.info(` Error message: ${callError?.message}`, {});
16731
- log.info(` Error code: ${callError?.code}`, {});
16732
- log.info(` Error data type: ${typeof callError?.data}`, {});
16733
- log.info(` Error data value: ${callError?.data}`, {});
16734
- }
16735
- let errorData = callError?.data || callError?.info?.error?.data || callError?.error?.data?.data || callError?.error?.data;
16736
- const errorMessage = callError?.reason || callError?.message || "Unknown error";
16737
- const isDeadZoneArtifact = decodeQuantumRevert(callError).selector === DEAD_ZONE_VIOLATION_SELECTOR;
16738
- if (isDeadZoneArtifact && this.config.debug) {
16739
- log.info(
16740
- "\u{1F536} mintUCD static-call DeadZoneViolation is a stale-latest-block artifact; broadcasting anyway (gated)",
16741
- {}
16742
- );
16743
- }
16744
- if (!isDeadZoneArtifact && errorData && typeof errorData === "string" && errorData.startsWith("0x") && errorData.length > 10) {
16745
- const selector = errorData.slice(0, 10);
16746
- const knownErrors = {
16747
- "0x8baa579f": "InvalidSignature()",
16748
- "0xd92e233d": "ValidationFailed()",
16749
- "0x3ee5aeb5": "OperationNotAuthorized()",
16750
- "0x70f4b32a": "UnauthorizedSigner()",
16751
- "0x48f5c3ed": "Unauthorized()",
16752
- "0x36bfdeea": "StateHashMismatch()",
16753
- "0x131d9a21": "QuantumExpired()",
16754
- "0x52ce5d58": "QuantumAlreadyUsed()",
16755
- "0x137f3b70": "InDeadZone()",
16756
- "0xbe4b82c1": "DeadZoneViolation()",
16757
- "0x3e76a3c9": "QuantumOutsideWindow()",
16758
- "0x62278171": "InvalidValidatorSignature()",
16759
- "0xb9d419a7": "DebtUpdateVerificationFailed()",
16760
- "0xc7e20553": "DebtUpdateVerificationFailedDetailed(bytes32,uint256,uint256,uint256,uint256,uint256)",
16761
- "0x0cfd2a97": "MintVerificationMismatch(uint256,uint256)",
16762
- "0x07637bd8": "MintFailed()"
16763
- };
16764
- const errorName = knownErrors[selector] || `Unknown error ${selector}`;
16765
- throw new Error(
16766
- `mintUCD reverted: ${errorName}
16767
- Error selector: ${selector}
16768
- Error data: ${errorData}
16769
- Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
16770
- Message: ${errorMessage}`
16771
- );
16772
- }
16773
- if (errorData === "0x" || !errorData) {
16774
- const viewsAddress = this.config.contractAddresses?.positionManagerViews;
16775
- if (viewsAddress) {
16776
- try {
16777
- const views = new Contract6(
16778
- viewsAddress,
16779
- [
16780
- "function diagnoseMintUCD(bytes32,uint256,uint256,uint256,uint256,uint256,bytes32,bytes32,bytes32,uint256,bytes) view returns (string)"
16781
- ],
16782
- signerProvider
16783
- );
16784
- const diagnosis = await views["diagnoseMintUCD"](
16785
- request.positionId,
16786
- validationResponse.mintAmount,
16787
- validationResponse.mintFee,
16788
- validationResponse.newDebt,
16789
- validationResponse.newCollateral,
16790
- validationResponse.btcPrice,
16791
- validationResponse.authorizedSpendsHash,
16792
- ucdDebtHash,
16793
- contractHash,
16794
- validationResponse.timestamp,
16795
- signatureHexMint
16796
- );
16797
- throw new Error(
16798
- `mintUCD reverted with empty data; diagnosis: ${diagnosis}`
16583
+ } catch (simError) {
16584
+ if (!(simError instanceof QuantumRevertError)) {
16585
+ throw simError;
16586
+ }
16587
+ const simCause = simError.cause;
16588
+ const causeMessage = simCause?.reason ?? simCause?.shortMessage ?? (simCause instanceof Error ? simCause.message : String(simCause));
16589
+ if (!simError.data) {
16590
+ const viewsAddress = this.config.contractAddresses?.positionManagerViews;
16591
+ if (viewsAddress) {
16592
+ let diagnosis = null;
16593
+ try {
16594
+ const views = new Contract6(
16595
+ viewsAddress,
16596
+ [
16597
+ "function diagnoseMintUCD(bytes32,uint256,uint256,uint256,uint256,uint256,bytes32,bytes32,bytes32,uint256,bytes) view returns (string)"
16598
+ ],
16599
+ signerProvider
16600
+ );
16601
+ diagnosis = await views["diagnoseMintUCD"](
16602
+ request.positionId,
16603
+ validationResponse.mintAmount,
16604
+ validationResponse.mintFee,
16605
+ validationResponse.newDebt,
16606
+ validationResponse.newCollateral,
16607
+ validationResponse.btcPrice,
16608
+ validationResponse.authorizedSpendsHash,
16609
+ ucdDebtHash,
16610
+ contractHash,
16611
+ validationResponse.timestamp,
16612
+ signatureHexMint
16613
+ );
16614
+ } catch (diagErr) {
16615
+ if (this.config.debug) {
16616
+ log.info(
16617
+ ` diagnoseMintUCD unavailable or failed: ${diagErr?.message || diagErr}`,
16618
+ {}
16799
16619
  );
16800
- } catch (diagErr) {
16801
- if (this.config.debug) {
16802
- log.info(
16803
- ` diagnoseMintUCD unavailable or failed: ${diagErr?.message || diagErr}`,
16804
- {}
16805
- );
16806
- }
16807
16620
  }
16808
16621
  }
16622
+ if (diagnosis !== null) {
16623
+ throw new Error(
16624
+ `mintUCD reverted with empty data; diagnosis: ${diagnosis}`
16625
+ );
16626
+ }
16809
16627
  }
16810
- if (!isDeadZoneArtifact) {
16811
- throw new Error(
16812
- `mintUCD reverted: ${errorMessage}
16813
- Error data: ${errorData || "none"}`
16814
- );
16815
- }
16816
- }
16817
- if (this.config.debug) {
16818
- log.info(
16819
- "\u2705 Static call clear \u2014 broadcasting mintUCD with fixed gas ceiling",
16820
- {}
16628
+ throw new Error(
16629
+ `mintUCD would revert (pre-send simulation): ${causeMessage}
16630
+ Error data: none`
16821
16631
  );
16822
16632
  }
16823
- await awaitSafeSubmissionWindow(Number(validationResponse.timestamp));
16824
- tx = await sendEip1559Transaction({
16825
- signer,
16826
- to: positionManagerAddress,
16827
- data: mintCalldata,
16828
- gasLimit: MINT_UCD_GAS_CEILING
16829
- });
16633
+ const selector = simError.selector;
16634
+ const errorName = simError.errorName ?? (selector ? `Unknown error ${selector}` : "unknown");
16635
+ const mintDebtDiagnostics = selector === "0xb9d419a7" || // DebtUpdateVerificationFailed()
16636
+ selector === "0xc7e20553" || // DebtUpdateVerificationFailedDetailed(...)
16637
+ selector === "0x0cfd2a97" || // MintVerificationMismatch(uint256,uint256)
16638
+ selector === "0x07637bd8" ? `
16639
+
16640
+ Mint/debt diagnostics (LoanOperationsManager mint path \u2192 increaseDebtFromMint):
16641
+ positionId: ${request.positionId}
16642
+ on-chain ucdDebt before tx: ${currentDebt}
16643
+ Lit newDebt: ${validationResponse.newDebt}
16644
+ Lit mintAmount: ${validationResponse.mintAmount}
16645
+ Lit mintFee: ${validationResponse.mintFee}
16646
+ Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
16647
+ Contract expects UCD supply increase to match mintAmount+mintFee and debt update to newDebt.
16648
+ ` : "";
16649
+ let quantumContext = "";
16650
+ if (selector === "0x131d9a21" || // QuantumExpired()
16651
+ selector === "0x52ce5d58" || // QuantumAlreadyUsed()
16652
+ selector === "0x137f3b70" || // InDeadZone()
16653
+ selector === "0xbe4b82c1" || // DeadZoneViolation()
16654
+ selector === "0x3e76a3c9") {
16655
+ const nowSecs = Math.floor(Date.now() / 1e3);
16656
+ const nowQuantum = Math.floor(nowSecs / 60) * 60;
16657
+ const signedQuantum = Math.floor(Number(validationResponse.timestamp) / 60) * 60;
16658
+ const timeDiff = nowSecs - Number(validationResponse.timestamp);
16659
+ quantumContext = `
16660
+
16661
+ Quantum Timing Analysis:
16662
+ Signature created: ${validationResponse.timestamp} (${new Date(
16663
+ Number(validationResponse.timestamp) * 1e3
16664
+ ).toISOString()})
16665
+ Current time: ${nowSecs} (${new Date(
16666
+ nowSecs * 1e3
16667
+ ).toISOString()})
16668
+ Time elapsed: ${timeDiff} seconds
16669
+ Signature quantum: ${signedQuantum}
16670
+ Current quantum: ${nowQuantum}
16671
+ Quantum window: 60 seconds
16672
+ Status: ${signedQuantum < nowQuantum ? "\u274C EXPIRED - Signature created in older quantum window" : "\u2705 VALID"}
16673
+ Note: QuantumOutsideWindow can also occur if the tx is mined outside the accepted past/current/next quantum set.`;
16674
+ }
16675
+ throw new Error(
16676
+ `mintUCD would revert (pre-send simulation): ${errorName}
16677
+ Error selector: ${selector}
16678
+ Error data: ${simError.data}
16679
+ Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
16680
+ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
16681
+ );
16830
16682
  }
16683
+ const tx = await sendEip1559Transaction({
16684
+ signer,
16685
+ to: positionManagerAddress,
16686
+ data: mintCalldata,
16687
+ gasLimit: MINT_UCD_GAS_CEILING
16688
+ });
16831
16689
  const receipt = await signerProvider.waitForTransaction(
16832
16690
  tx.hash,
16833
16691
  1,
@@ -17991,26 +17849,43 @@ Error data: ${errorData || "none"}`
17991
17849
  if (!signatureBytes.startsWith("0x")) {
17992
17850
  signatureBytes = "0x" + signatureBytes;
17993
17851
  }
17994
- const contractManager = this.getContractManager();
17995
- const positionManagerResult = contractManager.getPositionManager();
17996
- if (!positionManagerResult.success) {
17997
- throw new Error(
17998
- `Failed to get PositionManager: ${positionManagerResult.error.message}`
17999
- );
18000
- }
18001
- const positionManager = positionManagerResult.value;
18002
- await awaitSafeSubmissionWindow(Number(quantumTimestamp));
18003
- const extendPositionFn = positionManager["extendPosition"];
18004
- const tx = await extendPositionFn(
17852
+ const extendIface = new Interface4([
17853
+ "function extendPosition(bytes32 positionId, uint256 selectedTerm, uint256 quantumTimestamp, uint256 btcPrice, uint256 availableBTCBalance, uint256 proRataRenewalFee, bytes calldata extensionValidatorSignature) external returns (bool)"
17854
+ ]);
17855
+ const extendCalldata = extendIface.encodeFunctionData("extendPosition", [
18005
17856
  positionIdBytes32,
18006
17857
  BigInt(_selectedTerm),
18007
17858
  quantumTimestamp,
18008
17859
  btcPrice,
18009
17860
  availableBTCBalance,
18010
17861
  proRataRenewalFee,
18011
- signatureBytes,
18012
- { gasLimit: EXTEND_POSITION_GAS_CEILING }
18013
- );
17862
+ signatureBytes
17863
+ ]);
17864
+ const extendTo = this.getContractAddressesOrThrow().positionManager;
17865
+ const extendSigner = this.getSignerOrThrow();
17866
+ try {
17867
+ await assertSafeQuantumSubmission({
17868
+ provider: this.getProviderOrThrow(),
17869
+ to: extendTo,
17870
+ from: await extendSigner.getAddress(),
17871
+ data: extendCalldata,
17872
+ quantumTimestamp: Number(quantumTimestamp),
17873
+ onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
17874
+ });
17875
+ } catch (simError) {
17876
+ if (!(simError instanceof QuantumRevertError)) {
17877
+ throw simError;
17878
+ }
17879
+ throw new Error(
17880
+ `extendPosition would revert (pre-send simulation): ${simError.errorName ?? simError.selector ?? "unknown"}`
17881
+ );
17882
+ }
17883
+ const tx = await sendEip1559Transaction({
17884
+ signer: extendSigner,
17885
+ to: extendTo,
17886
+ data: extendCalldata,
17887
+ gasLimit: EXTEND_POSITION_GAS_CEILING
17888
+ });
18014
17889
  const txWithProvider = tx;
18015
17890
  if (!txWithProvider.provider) {
18016
17891
  const signer2 = this.getSignerOrThrow();
@@ -18870,7 +18745,6 @@ Error data: ${errorData || "none"}`
18870
18745
  quantumTimestamp: quantumTimestamp.toString()
18871
18746
  });
18872
18747
  }
18873
- await awaitSafeSubmissionWindow(Number(quantumTimestamp));
18874
18748
  const paymentAmountStr = paymentAmountWei.toString();
18875
18749
  const quantumTimestampStr = quantumTimestamp.toString();
18876
18750
  const btcPriceStr = btcPrice.toString();
@@ -18888,58 +18762,30 @@ Error data: ${errorData || "none"}`
18888
18762
  btcPriceStr,
18889
18763
  signature
18890
18764
  ]);
18891
- const decodePaymentRevert = (e) => {
18892
- const { selector, name } = decodeQuantumRevert(e);
18893
- if (!selector)
18894
- return null;
18895
- return name ?? `Unknown error ${selector}`;
18896
- };
18897
- const MAX_DEADZONE_RESIMULATIONS = 3;
18898
- for (let sim = 1; ; sim++) {
18899
- try {
18900
- await paymentProvider.call({
18901
- to: paymentContractAddress,
18902
- from: paymentFrom,
18903
- data: paymentCalldata
18904
- });
18905
- break;
18906
- } catch (simError) {
18907
- const decoded = decodePaymentRevert(simError);
18908
- if (decoded === "DeadZoneViolation()" && sim <= MAX_DEADZONE_RESIMULATIONS) {
18909
- const nowSec = Math.floor(Date.now() / 1e3);
18910
- const sigQuantum = Math.floor(Number(quantumTimestamp) / 60) * 60;
18911
- const realCurrentQuantum = Math.floor(nowSec / 60) * 60;
18912
- if (sigQuantum === realCurrentQuantum) {
18913
- if (this.config.debug) {
18914
- log.info(
18915
- "\u21AA\uFE0F Simulated DeadZoneViolation is a stale-latest-block artifact (signature is the current quantum) \u2014 proceeding to broadcast",
18916
- {}
18917
- );
18918
- }
18919
- break;
18920
- }
18921
- const gate = await awaitSafeSubmissionWindow(Number(quantumTimestamp));
18922
- if (gate.waited) {
18923
- if (this.config.debug) {
18924
- log.info(
18925
- `\u{1F501} Re-simulating makePayment after crossing the quantum boundary (attempt ${sim}/${MAX_DEADZONE_RESIMULATIONS})`,
18926
- {}
18927
- );
18928
- }
18929
- continue;
18930
- }
18931
- }
18932
- const label = decoded ?? simError?.reason ?? simError?.shortMessage ?? (simError instanceof Error ? simError.message : String(simError));
18933
- if (this.config.debug) {
18934
- log.error("\u274C makePayment pre-send simulation reverted", { error: label });
18935
- }
18936
- return {
18937
- success: false,
18938
- error: `Payment would revert (pre-send simulation): ${label}`,
18939
- positionId: request.positionId,
18940
- paymentAmountUCD: request.paymentAmount
18941
- };
18765
+ try {
18766
+ await assertSafeQuantumSubmission({
18767
+ provider: paymentProvider,
18768
+ to: paymentContractAddress,
18769
+ from: paymentFrom,
18770
+ data: paymentCalldata,
18771
+ quantumTimestamp: Number(quantumTimestamp),
18772
+ onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
18773
+ });
18774
+ } catch (simError) {
18775
+ if (!(simError instanceof QuantumRevertError)) {
18776
+ throw simError;
18777
+ }
18778
+ const simCause = simError.cause;
18779
+ const label = simError.errorName ?? (simError.selector ? `Unknown error ${simError.selector}` : null) ?? simCause?.reason ?? simCause?.shortMessage ?? (simCause instanceof Error ? simCause.message : String(simCause));
18780
+ if (this.config.debug) {
18781
+ log.error("\u274C makePayment pre-send simulation reverted", { error: label });
18942
18782
  }
18783
+ return {
18784
+ success: false,
18785
+ error: `Payment would revert (pre-send simulation): ${label}`,
18786
+ positionId: request.positionId,
18787
+ paymentAmountUCD: request.paymentAmount
18788
+ };
18943
18789
  }
18944
18790
  const tx = await sendEip1559Transaction({
18945
18791
  signer: paymentSigner,
@@ -19613,13 +19459,6 @@ Error data: ${errorData || "none"}`
19613
19459
  error: "PositionManager address not configured"
19614
19460
  };
19615
19461
  }
19616
- const positionManagerContract = new Contract6(
19617
- positionManagerAddress,
19618
- [
19619
- "function withdrawBTC((bytes32 positionId, bytes32 actionHash, bytes32 authorizedSpendsHash, bytes32 ucdDebtHash, bytes32 contractBundleHash, string withdrawalAddress, uint256 totalDeduction, uint256 newCollateral, uint256 quantumTimestamp, uint256 btcPrice, string utxoTxid, uint32 utxoVout) params, bytes withdrawalValidatorSignature, bytes btcSpendAuthSignature) external returns (bool)"
19620
- ],
19621
- this.getSignerOrThrow()
19622
- );
19623
19462
  const withdrawalParams = {
19624
19463
  positionId: positionIdBytes32,
19625
19464
  actionHash: validationResponse.actionHash,
@@ -19652,86 +19491,56 @@ Error data: ${errorData || "none"}`
19652
19491
  utxoVout: withdrawalParams.utxoVout
19653
19492
  });
19654
19493
  }
19655
- await awaitSafeSubmissionWindow(Number(withdrawalParams.quantumTimestamp));
19494
+ const withdrawIface = new Interface4([
19495
+ "function withdrawBTC((bytes32 positionId, bytes32 actionHash, bytes32 authorizedSpendsHash, bytes32 ucdDebtHash, bytes32 contractBundleHash, string withdrawalAddress, uint256 totalDeduction, uint256 newCollateral, uint256 quantumTimestamp, uint256 btcPrice, string utxoTxid, uint32 utxoVout) params, bytes withdrawalValidatorSignature, bytes btcSpendAuthSignature) external returns (bool)"
19496
+ ]);
19497
+ const withdrawCalldata = withdrawIface.encodeFunctionData("withdrawBTC", [
19498
+ withdrawalParams,
19499
+ signatureBytes,
19500
+ btcSpendAuthBytes
19501
+ ]);
19502
+ const withdrawSigner = this.getSignerOrThrow();
19656
19503
  let tx;
19657
19504
  try {
19658
- tx = await positionManagerContract["withdrawBTC"](
19659
- withdrawalParams,
19660
- signatureBytes,
19661
- btcSpendAuthBytes,
19662
- { gasLimit: WITHDRAW_BTC_GAS_CEILING }
19663
- );
19505
+ await assertSafeQuantumSubmission({
19506
+ provider: this.getProviderOrThrow(),
19507
+ to: positionManagerAddress,
19508
+ from: await withdrawSigner.getAddress(),
19509
+ data: withdrawCalldata,
19510
+ quantumTimestamp: Number(withdrawalParams.quantumTimestamp),
19511
+ onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
19512
+ });
19513
+ tx = await sendEip1559Transaction({
19514
+ signer: withdrawSigner,
19515
+ to: positionManagerAddress,
19516
+ data: withdrawCalldata,
19517
+ gasLimit: WITHDRAW_BTC_GAS_CEILING
19518
+ });
19664
19519
  } catch (txError) {
19665
- const txErrorMsg = txError instanceof Error ? txError.message : String(txError);
19520
+ const txErrorMsg = txError instanceof QuantumRevertError ? `Withdrawal would revert (pre-send simulation): ${txError.errorName ?? txError.selector ?? "unknown"}` : txError instanceof Error ? txError.message : String(txError);
19666
19521
  if (this.config.debug) {
19667
- log.warn(
19668
- "\u26A0\uFE0F withdrawBTC contract interface failed, falling back to raw transaction",
19669
- { error: txErrorMsg }
19670
- );
19522
+ log.error("\u274C withdrawBTC transaction submission failed", {
19523
+ error: txErrorMsg,
19524
+ withdrawalParams,
19525
+ signatureBytesLength: signatureBytes.length
19526
+ });
19671
19527
  }
19672
19528
  try {
19673
- const contractAddress = this.getContractAddressesOrThrow().positionManager;
19674
- const iface = new Interface4([
19675
- "function withdrawBTC((bytes32 positionId, bytes32 actionHash, bytes32 authorizedSpendsHash, bytes32 ucdDebtHash, bytes32 contractBundleHash, string withdrawalAddress, uint256 totalDeduction, uint256 newCollateral, uint256 quantumTimestamp, uint256 btcPrice, string utxoTxid, uint32 utxoVout) params, bytes withdrawalValidatorSignature, bytes btcSpendAuthSignature) external returns (bool)"
19676
- ]);
19677
- const calldata = iface.encodeFunctionData("withdrawBTC", [
19678
- withdrawalParams,
19679
- signatureBytes,
19680
- btcSpendAuthBytes
19681
- ]);
19682
- const signer = this.getSignerOrThrow();
19683
- const fromAddress = await signer.getAddress();
19684
- const estimatedGas = await estimateContractCallGasWithMargin(
19685
- this.getProviderOrThrow(),
19686
- fromAddress,
19687
- contractAddress,
19688
- calldata,
19689
- MINT_UCD_ESTIMATE_MARGIN_BPS,
19690
- WITHDRAW_BTC_GAS_CEILING
19691
- );
19692
- const gasLimit = estimatedGas ?? WITHDRAW_BTC_GAS_CEILING;
19693
- await awaitSafeSubmissionWindow(
19694
- Number(withdrawalParams.quantumTimestamp)
19529
+ const position = await this.getPosition(positionId);
19530
+ const btcAmount = (Number(withdrawalAmount) / 1e8).toFixed(8);
19531
+ const message = formatBTCWithdrawalFailureMessage(
19532
+ btcAmount,
19533
+ position?.vaultAddress || "unknown",
19534
+ withdrawalAddress,
19535
+ positionId
19695
19536
  );
19696
- tx = await signer.sendTransaction({
19697
- to: contractAddress,
19698
- data: calldata,
19699
- value: "0x0",
19700
- gasLimit
19701
- });
19702
- if (this.config.debug) {
19703
- log.info("\u2705 withdrawBTC raw transaction fallback succeeded", {
19704
- txHash: tx.hash,
19705
- gasLimit: gasLimit.toString(),
19706
- estimated: estimatedGas !== null
19707
- });
19708
- }
19709
- } catch (rawError) {
19710
- const rawErrorMsg = rawError instanceof Error ? rawError.message : String(rawError);
19711
- if (this.config.debug) {
19712
- log.error("\u274C withdrawBTC transaction failed (raw fallback also failed)", {
19713
- error: rawErrorMsg,
19714
- withdrawalParams,
19715
- signatureBytesLength: signatureBytes.length
19716
- });
19717
- }
19718
- try {
19719
- const position = await this.getPosition(positionId);
19720
- const btcAmount = (Number(withdrawalAmount) / 1e8).toFixed(8);
19721
- const message = formatBTCWithdrawalFailureMessage(
19722
- btcAmount,
19723
- position?.vaultAddress || "unknown",
19724
- withdrawalAddress,
19725
- positionId
19726
- );
19727
- await this._relayNotification(message);
19728
- } catch {
19729
- }
19730
- return {
19731
- success: false,
19732
- error: `Transaction submission failed: ${rawErrorMsg}`
19733
- };
19537
+ await this._relayNotification(message);
19538
+ } catch {
19734
19539
  }
19540
+ return {
19541
+ success: false,
19542
+ error: `Transaction submission failed: ${txErrorMsg}`
19543
+ };
19735
19544
  }
19736
19545
  if (this.config.debug) {
19737
19546
  log.info("\u2705 Transaction submitted, waiting for confirmation:", {
@@ -19754,6 +19563,12 @@ Error data: ${errorData || "none"}`
19754
19563
  error: `Transaction confirmation failed: ${waitErrorMsg}`
19755
19564
  };
19756
19565
  }
19566
+ if (!receipt) {
19567
+ return {
19568
+ success: false,
19569
+ error: `Transaction confirmation returned no receipt. Hash: ${tx.hash}`
19570
+ };
19571
+ }
19757
19572
  if (this.config.debug) {
19758
19573
  log.info("\u2705 BTC withdrawal confirmed", {
19759
19574
  blockNumber: receipt.blockNumber,