@gvnrdao/dh-sdk 0.0.299 → 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;
@@ -5839,6 +5843,122 @@ var ServerSession = class {
5839
5843
  }
5840
5844
  };
5841
5845
 
5846
+ // src/utils/quantum-revert.utils.ts
5847
+ import { AbiCoder } from "ethers";
5848
+ var DEAD_ZONE_VIOLATION_SELECTOR = "0xbe4b82c1";
5849
+ var ERROR_STRING_SELECTOR = "0x08c379a0";
5850
+ var PANIC_SELECTOR = "0x4e487b71";
5851
+ var QUANTUM_REVERT_NAMES = {
5852
+ "0xbe4b82c1": "DeadZoneViolation()",
5853
+ "0x137f3b70": "InDeadZone()",
5854
+ "0x131d9a21": "QuantumExpired()",
5855
+ "0x52ce5d58": "QuantumAlreadyUsed()",
5856
+ "0x3e76a3c9": "QuantumOutsideWindow()",
5857
+ "0x8baa579f": "InvalidSignature()",
5858
+ "0x62278171": "InvalidValidatorSignature()",
5859
+ "0x3ee5aeb5": "OperationNotAuthorized()",
5860
+ "0x48f5c3ed": "Unauthorized()",
5861
+ "0x70f4b32a": "UnauthorizedSigner()",
5862
+ "0xd92e233d": "ValidationFailed()",
5863
+ "0x36bfdeea": "StateHashMismatch()",
5864
+ "0xb9d419a7": "DebtUpdateVerificationFailed()",
5865
+ "0xc7e20553": "DebtUpdateVerificationFailedDetailed(bytes32,uint256,uint256,uint256,uint256,uint256)",
5866
+ "0x0cfd2a97": "MintVerificationMismatch(uint256,uint256)",
5867
+ "0x07637bd8": "MintFailed()"
5868
+ };
5869
+ function decodeQuantumRevert(e) {
5870
+ const raw = e?.data ?? e?.info?.error?.data ?? // ethers v6 BrowserProvider (MetaMask)
5871
+ e?.error?.data?.data ?? e?.error?.data ?? e?.error?.error?.data;
5872
+ const data = typeof raw === "string" && raw.startsWith("0x") ? raw : null;
5873
+ const selector = data && data.length >= 10 ? data.slice(0, 10) : 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
+ }
5893
+ return { data, selector, name };
5894
+ }
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
+
5842
5962
  // src/utils/btc-withdrawal-message.ts
5843
5963
  import { keccak256 as keccak2563, toUtf8Bytes as toUtf8Bytes3, solidityPackedKeccak256 as solidityPackedKeccak2563, getBytes as getBytes3, zeroPadValue } from "ethers";
5844
5964
  var QUANTUM_SECONDS = 60;
@@ -16194,8 +16314,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
16194
16314
  throw new Error(`Position not found: ${request.positionId}`);
16195
16315
  }
16196
16316
  const currentDebt = position.ucdDebt.toString();
16197
- const ucdDebtHash = keccak2565(AbiCoder.defaultAbiCoder().encode(["uint256"], [currentDebt]));
16198
- const contractHash = keccak2565(AbiCoder.defaultAbiCoder().encode(
16317
+ const ucdDebtHash = keccak2565(AbiCoder2.defaultAbiCoder().encode(["uint256"], [currentDebt]));
16318
+ const contractHash = keccak2565(AbiCoder2.defaultAbiCoder().encode(
16199
16319
  ["address", "address", "address", "address"],
16200
16320
  [
16201
16321
  positionManagerAddress,
@@ -16256,188 +16376,6 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
16256
16376
  {}
16257
16377
  );
16258
16378
  }
16259
- if (this.config.debug) {
16260
- log.info("\u{1F50D} PREFLIGHT: Attempting diagnostic gas estimation...", {});
16261
- }
16262
- try {
16263
- const iface = new Interface4(positionManagerAbi);
16264
- const signatureHex = finalSignature.startsWith("0x") ? finalSignature : "0x" + finalSignature;
16265
- if (this.config.debug) {
16266
- log.info("\u{1F50D} DEBUG: Signature details:", {
16267
- finalSignature: finalSignature ? `${finalSignature.substring(0, 20)}... (length: ${finalSignature.length})` : "UNDEFINED/EMPTY",
16268
- signatureHex: signatureHex ? `${signatureHex.substring(0, 22)}... (length: ${signatureHex.length})` : "UNDEFINED/EMPTY"
16269
- });
16270
- }
16271
- const calldata = iface.encodeFunctionData("mintUCD", [
16272
- positionIdBytes32,
16273
- validationResponse.mintAmount,
16274
- validationResponse.mintFee,
16275
- validationResponse.newDebt,
16276
- validationResponse.newCollateral,
16277
- validationResponse.btcPrice,
16278
- validationResponse.authorizedSpendsHash,
16279
- ucdDebtHash,
16280
- contractHash,
16281
- validationResponse.timestamp,
16282
- signatureHex
16283
- ]);
16284
- if (this.config.debug) {
16285
- log.info("\u{1F50D} DEBUG: Encoded calldata:", {
16286
- length: calldata.length,
16287
- signaturePortion: calldata.substring(650, 670)
16288
- });
16289
- }
16290
- const provider = this.getSignerOrThrow().provider;
16291
- if (!provider) {
16292
- throw new Error("Provider not available");
16293
- }
16294
- const signerAddress = await this.getSignerOrThrow().getAddress();
16295
- if (this.config.debug) {
16296
- log.info(" Executing diagnostic eth_call...", {});
16297
- }
16298
- await provider.call({
16299
- to: positionManagerAddress,
16300
- from: signerAddress,
16301
- data: calldata
16302
- });
16303
- if (this.config.debug) {
16304
- log.info(" \u2705 Diagnostic eth_call succeeded!", {});
16305
- }
16306
- } catch (preflightError) {
16307
- if (this.config.debug) {
16308
- log.error(
16309
- "\u274C PREFLIGHT CHECK FAILED - Diagnostic eth_call reverted",
16310
- {}
16311
- );
16312
- log.error(` Error message: ${preflightError?.message}`, {});
16313
- log.error(` Error code: ${preflightError?.code}`, {});
16314
- log.error(` Error data: ${preflightError?.data || "none"}`, {});
16315
- log.error(` Error reason: ${preflightError?.reason || "none"}`, {});
16316
- log.error(
16317
- ` Error.error: ${JSON.stringify(preflightError?.error || {})}`,
16318
- {}
16319
- );
16320
- }
16321
- let errorData = preflightError?.data || preflightError?.error?.data?.data || preflightError?.error?.data || preflightError?.error?.error?.data;
16322
- const errorMessage = preflightError?.reason || preflightError?.error?.reason || preflightError?.error?.error?.reason || preflightError?.message || "Unknown error";
16323
- let requireMessage;
16324
- if (errorData && typeof errorData === "string" && errorData.startsWith("0x") && errorData.length > 138) {
16325
- try {
16326
- const errorSelector = errorData.slice(0, 10);
16327
- if (errorSelector === "0x08c379a0") {
16328
- const iface = new Interface4([
16329
- "function Error(string)"
16330
- ]);
16331
- const decoded = iface.decodeFunctionData("Error", errorData);
16332
- requireMessage = decoded[0];
16333
- }
16334
- } catch (e) {
16335
- }
16336
- }
16337
- if (requireMessage) {
16338
- throw new Error(
16339
- `Preflight check failed - mintUCD would revert:
16340
- ${requireMessage}
16341
- Context: Quantum timestamp=${validationResponse.timestamp}, Position=${request.positionId}`
16342
- );
16343
- }
16344
- if (errorData && typeof errorData === "string" && errorData.startsWith("0x") && errorData.length > 10) {
16345
- const selector = errorData.slice(0, 10);
16346
- const knownErrors = {
16347
- "0x8baa579f": "InvalidSignature()",
16348
- "0xd92e233d": "ValidationFailed()",
16349
- "0x3ee5aeb5": "OperationNotAuthorized()",
16350
- "0x70f4b32a": "UnauthorizedSigner()",
16351
- "0x48f5c3ed": "Unauthorized()",
16352
- "0x36bfdeea": "StateHashMismatch()",
16353
- "0x131d9a21": "QuantumExpired()",
16354
- "0x52ce5d58": "QuantumAlreadyUsed()",
16355
- "0x137f3b70": "InDeadZone()",
16356
- "0xbe4b82c1": "DeadZoneViolation()",
16357
- "0x3e76a3c9": "QuantumOutsideWindow()",
16358
- "0x62278171": "InvalidValidatorSignature()",
16359
- "0xb9d419a7": "DebtUpdateVerificationFailed()",
16360
- "0xc7e20553": "DebtUpdateVerificationFailedDetailed(bytes32,uint256,uint256,uint256,uint256,uint256)",
16361
- "0x0cfd2a97": "MintVerificationMismatch(uint256,uint256)",
16362
- "0x07637bd8": "MintFailed()"
16363
- };
16364
- const errorName = knownErrors[selector] || `Unknown error ${selector}`;
16365
- const mintDebtDiagnostics = selector === "0xb9d419a7" || selector === "0xc7e20553" || selector === "0x0cfd2a97" || selector === "0x07637bd8" ? `
16366
-
16367
- Mint/debt diagnostics (LoanOperationsManager mint path \u2192 increaseDebtFromMint):
16368
- positionId: ${request.positionId}
16369
- on-chain ucdDebt before tx: ${currentDebt}
16370
- Lit newDebt: ${validationResponse.newDebt}
16371
- Lit mintAmount: ${validationResponse.mintAmount}
16372
- Lit mintFee: ${validationResponse.mintFee}
16373
- Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
16374
- Contract expects UCD supply increase to match mintAmount+mintFee and debt update to newDebt.
16375
- ` : "";
16376
- let quantumContext = "";
16377
- if (selector === "0x131d9a21" || selector === "0x52ce5d58" || selector === "0x137f3b70" || selector === "0xbe4b82c1" || selector === "0x3e76a3c9") {
16378
- const currentTime2 = Math.floor(Date.now() / 1e3);
16379
- const currentQuantum = Math.floor(currentTime2 / 60) * 60;
16380
- const sigQuantum2 = Math.floor(validationResponse.timestamp / 60) * 60;
16381
- const timeDiff = currentTime2 - validationResponse.timestamp;
16382
- quantumContext = `
16383
-
16384
- Quantum Timing Analysis:
16385
- Signature created: ${validationResponse.timestamp} (${new Date(
16386
- validationResponse.timestamp * 1e3
16387
- ).toISOString()})
16388
- Current time: ${currentTime2} (${new Date(
16389
- currentTime2 * 1e3
16390
- ).toISOString()})
16391
- Time elapsed: ${timeDiff} seconds
16392
- Signature quantum: ${sigQuantum2}
16393
- Current quantum: ${currentQuantum}
16394
- Quantum window: 60 seconds
16395
- Status: ${sigQuantum2 < currentQuantum ? "\u274C EXPIRED - Signature created in older quantum window" : "\u2705 VALID"}
16396
- Note: QuantumOutsideWindow can also occur if the tx is mined outside the accepted past/current/next quantum set.`;
16397
- }
16398
- throw new Error(
16399
- `Preflight check failed - mintUCD would revert:
16400
- Error: ${errorName}
16401
- Selector: ${selector}
16402
- Message: ${errorMessage}
16403
- Data: ${errorData}${quantumContext}${mintDebtDiagnostics}`
16404
- );
16405
- } else {
16406
- const currentTime2 = Math.floor(Date.now() / 1e3);
16407
- const timeSinceSig = currentTime2 - validationResponse.timestamp;
16408
- const currentQuantum = Math.floor(currentTime2 / 60) * 60;
16409
- const sigQuantum2 = Math.floor(validationResponse.timestamp / 60) * 60;
16410
- throw new Error(
16411
- `Preflight check failed - mintUCD would revert:
16412
- ${errorMessage}
16413
-
16414
- Error Details:
16415
- Error data: ${errorData || "0x (empty - no custom error)"}
16416
- Error code: ${preflightError?.code || "none"}
16417
-
16418
- Quantum Timing Analysis:
16419
- Signature created: ${validationResponse.timestamp} (${new Date(
16420
- validationResponse.timestamp * 1e3
16421
- ).toISOString()})
16422
- Current time: ${currentTime2} (${new Date(
16423
- currentTime2 * 1e3
16424
- ).toISOString()})
16425
- Time elapsed: ${timeSinceSig} seconds
16426
- Signature quantum: ${sigQuantum2}
16427
- Current quantum: ${currentQuantum}
16428
- Status: ${sigQuantum2 < currentQuantum ? "\u274C EXPIRED - Signature too old" : "\u2705 VALID"}
16429
-
16430
- Possible Causes (when error data is empty):
16431
- \u2022 Quantum signature expired (current=${currentQuantum}, signature=${sigQuantum2})
16432
- \u2022 Contract paused or circuit breaker active
16433
- \u2022 Position state changed since LIT Action signed
16434
- \u2022 Authorization registry validation failed
16435
- \u2022 RPC provider not returning detailed revert data
16436
-
16437
- Position: ${request.positionId}`
16438
- );
16439
- }
16440
- }
16441
16379
  try {
16442
16380
  const coreAddress = this.getContractAddressesOrThrow().positionManagerCore;
16443
16381
  if (!coreAddress) {
@@ -16455,7 +16393,7 @@ Position: ${request.positionId}`
16455
16393
  positionIdBytes32
16456
16394
  );
16457
16395
  const currentDebtHash = keccak2565(
16458
- AbiCoder.defaultAbiCoder().encode(
16396
+ AbiCoder2.defaultAbiCoder().encode(
16459
16397
  ["uint256"],
16460
16398
  [currentPosition.ucdDebt]
16461
16399
  )
@@ -16632,137 +16570,122 @@ Position: ${request.positionId}`
16632
16570
  validationResponse.timestamp,
16633
16571
  signatureHexMint
16634
16572
  ]);
16635
- await awaitSafeSubmissionWindow(Number(validationResponse.timestamp));
16636
16573
  const fromAddress = await signer.getAddress();
16637
- const estimatedGas = await estimateContractCallGasWithMargin(
16638
- signerProvider,
16639
- fromAddress,
16640
- positionManagerAddress,
16641
- mintCalldata,
16642
- MINT_UCD_ESTIMATE_MARGIN_BPS,
16643
- MINT_UCD_GAS_CEILING
16644
- );
16645
- let tx;
16646
- if (estimatedGas !== null) {
16647
- if (this.config.debug) {
16648
- log.info(
16649
- `\u{1F4DD} Broadcasting mintUCD (gasLimit=${estimatedGas.toString()})`,
16650
- {}
16651
- );
16652
- }
16653
- tx = await sendEip1559Transaction({
16654
- signer,
16574
+ try {
16575
+ await assertSafeQuantumSubmission({
16576
+ provider: signerProvider,
16655
16577
  to: positionManagerAddress,
16578
+ from: fromAddress,
16656
16579
  data: mintCalldata,
16657
- gasLimit: estimatedGas
16580
+ quantumTimestamp: Number(validationResponse.timestamp),
16581
+ onDebug: this.config.debug ? (msg) => log.info(msg, {}) : void 0
16658
16582
  });
16659
- } else {
16660
- if (this.config.debug) {
16661
- log.info(
16662
- "\u{1F50D} eth_estimateGas failed for mintUCD; running static call",
16663
- {}
16664
- );
16665
- }
16666
- try {
16667
- await signerProvider.call({
16668
- to: positionManagerAddress,
16669
- from: fromAddress,
16670
- data: mintCalldata
16671
- });
16672
- } catch (callError) {
16673
- if (this.config.debug) {
16674
- log.info("\u{1F50D} DEBUG: mintUCD static call failed", {});
16675
- log.info(` Error message: ${callError?.message}`, {});
16676
- log.info(` Error code: ${callError?.code}`, {});
16677
- log.info(` Error data type: ${typeof callError?.data}`, {});
16678
- log.info(` Error data value: ${callError?.data}`, {});
16679
- }
16680
- let errorData = callError?.data || callError?.error?.data?.data || callError?.error?.data;
16681
- const errorMessage = callError?.reason || callError?.message || "Unknown error";
16682
- if (errorData && typeof errorData === "string" && errorData.startsWith("0x") && errorData.length > 10) {
16683
- const selector = errorData.slice(0, 10);
16684
- const knownErrors = {
16685
- "0x8baa579f": "InvalidSignature()",
16686
- "0xd92e233d": "ValidationFailed()",
16687
- "0x3ee5aeb5": "OperationNotAuthorized()",
16688
- "0x70f4b32a": "UnauthorizedSigner()",
16689
- "0x48f5c3ed": "Unauthorized()",
16690
- "0x36bfdeea": "StateHashMismatch()",
16691
- "0x131d9a21": "QuantumExpired()",
16692
- "0x52ce5d58": "QuantumAlreadyUsed()",
16693
- "0x137f3b70": "InDeadZone()",
16694
- "0xbe4b82c1": "DeadZoneViolation()",
16695
- "0x3e76a3c9": "QuantumOutsideWindow()",
16696
- "0x62278171": "InvalidValidatorSignature()",
16697
- "0xb9d419a7": "DebtUpdateVerificationFailed()",
16698
- "0xc7e20553": "DebtUpdateVerificationFailedDetailed(bytes32,uint256,uint256,uint256,uint256,uint256)",
16699
- "0x0cfd2a97": "MintVerificationMismatch(uint256,uint256)",
16700
- "0x07637bd8": "MintFailed()"
16701
- };
16702
- const errorName = knownErrors[selector] || `Unknown error ${selector}`;
16703
- throw new Error(
16704
- `mintUCD reverted: ${errorName}
16705
- Error selector: ${selector}
16706
- Error data: ${errorData}
16707
- Call path: mintUCD -> mintUCDWithAuthorization -> increaseDebtFromMint -> finalizeMintDebtIncrease
16708
- Message: ${errorMessage}`
16709
- );
16710
- }
16711
- if (errorData === "0x" || !errorData) {
16712
- const viewsAddress = this.config.contractAddresses?.positionManagerViews;
16713
- if (viewsAddress) {
16714
- try {
16715
- const views = new Contract6(
16716
- viewsAddress,
16717
- [
16718
- "function diagnoseMintUCD(bytes32,uint256,uint256,uint256,uint256,uint256,bytes32,bytes32,bytes32,uint256,bytes) view returns (string)"
16719
- ],
16720
- signerProvider
16721
- );
16722
- const diagnosis = await views["diagnoseMintUCD"](
16723
- request.positionId,
16724
- validationResponse.mintAmount,
16725
- validationResponse.mintFee,
16726
- validationResponse.newDebt,
16727
- validationResponse.newCollateral,
16728
- validationResponse.btcPrice,
16729
- validationResponse.authorizedSpendsHash,
16730
- ucdDebtHash,
16731
- contractHash,
16732
- validationResponse.timestamp,
16733
- signatureHexMint
16734
- );
16735
- throw new Error(
16736
- `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
+ {}
16737
16619
  );
16738
- } catch (diagErr) {
16739
- if (this.config.debug) {
16740
- log.info(
16741
- ` diagnoseMintUCD unavailable or failed: ${diagErr?.message || diagErr}`,
16742
- {}
16743
- );
16744
- }
16745
16620
  }
16746
16621
  }
16622
+ if (diagnosis !== null) {
16623
+ throw new Error(
16624
+ `mintUCD reverted with empty data; diagnosis: ${diagnosis}`
16625
+ );
16626
+ }
16747
16627
  }
16748
16628
  throw new Error(
16749
- `mintUCD reverted: ${errorMessage}
16750
- Error data: ${errorData || "none"}`
16629
+ `mintUCD would revert (pre-send simulation): ${causeMessage}
16630
+ Error data: none`
16751
16631
  );
16752
16632
  }
16753
- if (this.config.debug) {
16754
- log.info(
16755
- "\u2705 Static call OK \u2014 broadcasting mintUCD with fixed gas ceiling",
16756
- {}
16757
- );
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.`;
16758
16674
  }
16759
- tx = await sendEip1559Transaction({
16760
- signer,
16761
- to: positionManagerAddress,
16762
- data: mintCalldata,
16763
- gasLimit: MINT_UCD_GAS_CEILING
16764
- });
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
+ );
16765
16682
  }
16683
+ const tx = await sendEip1559Transaction({
16684
+ signer,
16685
+ to: positionManagerAddress,
16686
+ data: mintCalldata,
16687
+ gasLimit: MINT_UCD_GAS_CEILING
16688
+ });
16766
16689
  const receipt = await signerProvider.waitForTransaction(
16767
16690
  tx.hash,
16768
16691
  1,
@@ -17926,26 +17849,43 @@ Error data: ${errorData || "none"}`
17926
17849
  if (!signatureBytes.startsWith("0x")) {
17927
17850
  signatureBytes = "0x" + signatureBytes;
17928
17851
  }
17929
- const contractManager = this.getContractManager();
17930
- const positionManagerResult = contractManager.getPositionManager();
17931
- if (!positionManagerResult.success) {
17932
- throw new Error(
17933
- `Failed to get PositionManager: ${positionManagerResult.error.message}`
17934
- );
17935
- }
17936
- const positionManager = positionManagerResult.value;
17937
- await awaitSafeSubmissionWindow(Number(quantumTimestamp));
17938
- const extendPositionFn = positionManager["extendPosition"];
17939
- 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", [
17940
17856
  positionIdBytes32,
17941
17857
  BigInt(_selectedTerm),
17942
17858
  quantumTimestamp,
17943
17859
  btcPrice,
17944
17860
  availableBTCBalance,
17945
17861
  proRataRenewalFee,
17946
- signatureBytes,
17947
- { gasLimit: EXTEND_POSITION_GAS_CEILING }
17948
- );
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
+ });
17949
17889
  const txWithProvider = tx;
17950
17890
  if (!txWithProvider.provider) {
17951
17891
  const signer2 = this.getSignerOrThrow();
@@ -18805,7 +18745,6 @@ Error data: ${errorData || "none"}`
18805
18745
  quantumTimestamp: quantumTimestamp.toString()
18806
18746
  });
18807
18747
  }
18808
- await awaitSafeSubmissionWindow(Number(quantumTimestamp));
18809
18748
  const paymentAmountStr = paymentAmountWei.toString();
18810
18749
  const quantumTimestampStr = quantumTimestamp.toString();
18811
18750
  const btcPriceStr = btcPrice.toString();
@@ -18823,71 +18762,30 @@ Error data: ${errorData || "none"}`
18823
18762
  btcPriceStr,
18824
18763
  signature
18825
18764
  ]);
18826
- const decodePaymentRevert = (e) => {
18827
- const raw = e?.data ?? e?.info?.error?.data ?? e?.error?.data?.data ?? e?.error?.data;
18828
- const data = typeof raw === "string" ? raw : void 0;
18829
- if (!data || !data.startsWith("0x") || data.length < 10)
18830
- return null;
18831
- const selector = data.slice(0, 10);
18832
- const map2 = {
18833
- "0xbe4b82c1": "DeadZoneViolation()",
18834
- "0x137f3b70": "InDeadZone()",
18835
- "0x52ce5d58": "QuantumAlreadyUsed()",
18836
- "0x3e76a3c9": "QuantumOutsideWindow()",
18837
- "0x131d9a21": "QuantumExpired()",
18838
- "0x8baa579f": "InvalidSignature()",
18839
- "0x62278171": "InvalidValidatorSignature()",
18840
- "0x3ee5aeb5": "OperationNotAuthorized()",
18841
- "0x48f5c3ed": "Unauthorized()"
18842
- };
18843
- return map2[selector] ?? `Unknown error ${selector}`;
18844
- };
18845
- const MAX_DEADZONE_RESIMULATIONS = 3;
18846
- for (let sim = 1; ; sim++) {
18847
- try {
18848
- await paymentProvider.call({
18849
- to: paymentContractAddress,
18850
- from: paymentFrom,
18851
- data: paymentCalldata
18852
- });
18853
- break;
18854
- } catch (simError) {
18855
- const decoded = decodePaymentRevert(simError);
18856
- if (decoded === "DeadZoneViolation()" && sim <= MAX_DEADZONE_RESIMULATIONS) {
18857
- const nowSec = Math.floor(Date.now() / 1e3);
18858
- const sigQuantum = Math.floor(Number(quantumTimestamp) / 60) * 60;
18859
- const realCurrentQuantum = Math.floor(nowSec / 60) * 60;
18860
- if (sigQuantum === realCurrentQuantum) {
18861
- if (this.config.debug) {
18862
- log.info(
18863
- "\u21AA\uFE0F Simulated DeadZoneViolation is a stale-latest-block artifact (signature is the current quantum) \u2014 proceeding to broadcast",
18864
- {}
18865
- );
18866
- }
18867
- break;
18868
- }
18869
- const gate = await awaitSafeSubmissionWindow(Number(quantumTimestamp));
18870
- if (gate.waited) {
18871
- if (this.config.debug) {
18872
- log.info(
18873
- `\u{1F501} Re-simulating makePayment after crossing the quantum boundary (attempt ${sim}/${MAX_DEADZONE_RESIMULATIONS})`,
18874
- {}
18875
- );
18876
- }
18877
- continue;
18878
- }
18879
- }
18880
- const label = decoded ?? simError?.reason ?? simError?.shortMessage ?? (simError instanceof Error ? simError.message : String(simError));
18881
- if (this.config.debug) {
18882
- log.error("\u274C makePayment pre-send simulation reverted", { error: label });
18883
- }
18884
- return {
18885
- success: false,
18886
- error: `Payment would revert (pre-send simulation): ${label}`,
18887
- positionId: request.positionId,
18888
- paymentAmountUCD: request.paymentAmount
18889
- };
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 });
18890
18782
  }
18783
+ return {
18784
+ success: false,
18785
+ error: `Payment would revert (pre-send simulation): ${label}`,
18786
+ positionId: request.positionId,
18787
+ paymentAmountUCD: request.paymentAmount
18788
+ };
18891
18789
  }
18892
18790
  const tx = await sendEip1559Transaction({
18893
18791
  signer: paymentSigner,
@@ -19561,13 +19459,6 @@ Error data: ${errorData || "none"}`
19561
19459
  error: "PositionManager address not configured"
19562
19460
  };
19563
19461
  }
19564
- const positionManagerContract = new Contract6(
19565
- positionManagerAddress,
19566
- [
19567
- "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)"
19568
- ],
19569
- this.getSignerOrThrow()
19570
- );
19571
19462
  const withdrawalParams = {
19572
19463
  positionId: positionIdBytes32,
19573
19464
  actionHash: validationResponse.actionHash,
@@ -19600,82 +19491,56 @@ Error data: ${errorData || "none"}`
19600
19491
  utxoVout: withdrawalParams.utxoVout
19601
19492
  });
19602
19493
  }
19603
- 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();
19604
19503
  let tx;
19605
19504
  try {
19606
- tx = await positionManagerContract["withdrawBTC"](
19607
- withdrawalParams,
19608
- signatureBytes,
19609
- btcSpendAuthBytes
19610
- );
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
+ });
19611
19519
  } catch (txError) {
19612
- 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);
19613
19521
  if (this.config.debug) {
19614
- log.warn(
19615
- "\u26A0\uFE0F withdrawBTC contract interface failed, falling back to raw transaction",
19616
- { error: txErrorMsg }
19617
- );
19522
+ log.error("\u274C withdrawBTC transaction submission failed", {
19523
+ error: txErrorMsg,
19524
+ withdrawalParams,
19525
+ signatureBytesLength: signatureBytes.length
19526
+ });
19618
19527
  }
19619
19528
  try {
19620
- const contractAddress = this.getContractAddressesOrThrow().positionManager;
19621
- const iface = new Interface4([
19622
- "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)"
19623
- ]);
19624
- const calldata = iface.encodeFunctionData("withdrawBTC", [
19625
- withdrawalParams,
19626
- signatureBytes,
19627
- btcSpendAuthBytes
19628
- ]);
19629
- const signer = this.getSignerOrThrow();
19630
- const fromAddress = await signer.getAddress();
19631
- const estimatedGas = await estimateContractCallGasWithMargin(
19632
- this.getProviderOrThrow(),
19633
- fromAddress,
19634
- contractAddress,
19635
- calldata,
19636
- MINT_UCD_ESTIMATE_MARGIN_BPS,
19637
- WITHDRAW_BTC_GAS_CEILING
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
19638
19536
  );
19639
- const gasLimit = estimatedGas ?? WITHDRAW_BTC_GAS_CEILING;
19640
- tx = await signer.sendTransaction({
19641
- to: contractAddress,
19642
- data: calldata,
19643
- value: "0x0",
19644
- gasLimit
19645
- });
19646
- if (this.config.debug) {
19647
- log.info("\u2705 withdrawBTC raw transaction fallback succeeded", {
19648
- txHash: tx.hash,
19649
- gasLimit: gasLimit.toString(),
19650
- estimated: estimatedGas !== null
19651
- });
19652
- }
19653
- } catch (rawError) {
19654
- const rawErrorMsg = rawError instanceof Error ? rawError.message : String(rawError);
19655
- if (this.config.debug) {
19656
- log.error("\u274C withdrawBTC transaction failed (raw fallback also failed)", {
19657
- error: rawErrorMsg,
19658
- withdrawalParams,
19659
- signatureBytesLength: signatureBytes.length
19660
- });
19661
- }
19662
- try {
19663
- const position = await this.getPosition(positionId);
19664
- const btcAmount = (Number(withdrawalAmount) / 1e8).toFixed(8);
19665
- const message = formatBTCWithdrawalFailureMessage(
19666
- btcAmount,
19667
- position?.vaultAddress || "unknown",
19668
- withdrawalAddress,
19669
- positionId
19670
- );
19671
- await this._relayNotification(message);
19672
- } catch {
19673
- }
19674
- return {
19675
- success: false,
19676
- error: `Transaction submission failed: ${rawErrorMsg}`
19677
- };
19537
+ await this._relayNotification(message);
19538
+ } catch {
19678
19539
  }
19540
+ return {
19541
+ success: false,
19542
+ error: `Transaction submission failed: ${txErrorMsg}`
19543
+ };
19679
19544
  }
19680
19545
  if (this.config.debug) {
19681
19546
  log.info("\u2705 Transaction submitted, waiting for confirmation:", {
@@ -19698,6 +19563,12 @@ Error data: ${errorData || "none"}`
19698
19563
  error: `Transaction confirmation failed: ${waitErrorMsg}`
19699
19564
  };
19700
19565
  }
19566
+ if (!receipt) {
19567
+ return {
19568
+ success: false,
19569
+ error: `Transaction confirmation returned no receipt. Hash: ${tx.hash}`
19570
+ };
19571
+ }
19701
19572
  if (this.config.debug) {
19702
19573
  log.info("\u2705 BTC withdrawal confirmed", {
19703
19574
  blockNumber: receipt.blockNumber,