@gvnrdao/dh-lit-actions 0.0.282 → 0.0.283
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/address-probe-signer.meta.json +1 -1
- package/dist/address-probe.meta.json +1 -1
- package/dist/admin-liquidation-validator.meta.json +5 -5
- package/dist/always-signer.meta.json +1 -1
- package/dist/authorization-dummy-b.meta.json +1 -1
- package/dist/authorization-dummy.meta.json +1 -1
- package/dist/btc-transaction-signer.meta.json +5 -5
- package/dist/btc-withdrawal.action.js +1 -1
- package/dist/btc-withdrawal.hash +1 -1
- package/dist/btc-withdrawal.meta.json +5 -5
- package/dist/encrypt-parameter.meta.json +1 -1
- package/dist/extend-position-validator.meta.json +5 -5
- package/dist/liquidation-validator.meta.json +5 -5
- package/dist/loan-vault-btc-balance.meta.json +5 -5
- package/dist/pkp-validator.meta.json +5 -5
- package/dist/price-oracle.meta.json +5 -5
- package/dist/process-payment-sign-only.meta.json +1 -1
- package/dist/process-payment-validator.meta.json +5 -5
- package/dist/ucd-mint-validator.meta.json +5 -5
- package/package.json +1 -1
- package/pkg-dist/pkg-src/constants/chunks/lit-actions-registry.js +206 -0
- package/pkg-dist/pkg-src/constants/chunks/lit-actions-registry.js.map +1 -0
- package/pkg-dist/pkg-src/constants/chunks/package-registry.js +12 -0
- package/pkg-dist/pkg-src/constants/chunks/package-registry.js.map +1 -0
- package/pkg-dist/pkg-src/constants/index.js +23 -0
- package/pkg-dist/pkg-src/constants/index.js.map +1 -0
- package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.js +77 -0
- package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.js.map +1 -0
- package/pkg-dist/pkg-src/executors/chunks/vault-snapshot-executor.js +104 -0
- package/pkg-dist/pkg-src/executors/chunks/vault-snapshot-executor.js.map +1 -0
- package/pkg-dist/pkg-src/executors/index.js +88 -0
- package/pkg-dist/pkg-src/executors/index.js.map +1 -0
- package/pkg-dist/pkg-src/index.js +36 -1587
- package/pkg-dist/pkg-src/index.js.map +1 -7
- package/pkg-dist/pkg-src/interfaces/chunks/diamond-hands-lit-actions.i.js +3 -0
- package/pkg-dist/pkg-src/interfaces/chunks/diamond-hands-lit-actions.i.js.map +1 -0
- package/pkg-dist/pkg-src/interfaces/chunks/lit-action-config.i.js +3 -0
- package/pkg-dist/pkg-src/interfaces/chunks/lit-action-config.i.js.map +1 -0
- package/pkg-dist/pkg-src/interfaces/chunks/lit-action-name.i.js +3 -0
- package/pkg-dist/pkg-src/interfaces/chunks/lit-action-name.i.js.map +1 -0
- package/pkg-dist/pkg-src/interfaces/chunks/lit-action-registry.i.js +3 -0
- package/pkg-dist/pkg-src/interfaces/chunks/lit-action-registry.i.js.map +1 -0
- package/pkg-dist/pkg-src/interfaces/chunks/pkp-info.i.js +3 -0
- package/pkg-dist/pkg-src/interfaces/chunks/pkp-info.i.js.map +1 -0
- package/pkg-dist/pkg-src/interfaces/index.js +25 -0
- package/pkg-dist/pkg-src/interfaces/index.js.map +1 -0
- package/pkg-dist/pkg-src/utils/chunks/cid-utils.js +37 -0
- package/pkg-dist/pkg-src/utils/chunks/cid-utils.js.map +1 -0
- package/pkg-dist/pkg-src/utils/chunks/connection-helpers.js +354 -0
- package/pkg-dist/pkg-src/utils/chunks/connection-helpers.js.map +1 -0
- package/pkg-dist/pkg-src/utils/chunks/debug-logger.js +264 -0
- package/pkg-dist/pkg-src/utils/chunks/debug-logger.js.map +1 -0
- package/pkg-dist/pkg-src/utils/chunks/error-classification.js +385 -0
- package/pkg-dist/pkg-src/utils/chunks/error-classification.js.map +1 -0
- package/pkg-dist/pkg-src/utils/chunks/lit-action-helpers.js +62 -0
- package/pkg-dist/pkg-src/utils/chunks/lit-action-helpers.js.map +1 -0
- package/pkg-dist/pkg-src/utils/chunks/pkp-setup.js +21 -0
- package/pkg-dist/pkg-src/utils/chunks/pkp-setup.js.map +1 -0
- package/pkg-dist/pkg-src/utils/chunks/session-signature-cache.js +194 -0
- package/pkg-dist/pkg-src/utils/chunks/session-signature-cache.js.map +1 -0
- package/pkg-dist/pkg-src/utils/index.js +28 -0
- package/pkg-dist/pkg-src/utils/index.js.map +1 -0
- package/pkg-dist/src/constants/chunks/bitcoin-network-config.js +236 -0
- package/pkg-dist/src/constants/chunks/bitcoin-network-config.js.map +1 -0
- package/pkg-dist/src/constants/chunks/bitcoin.js +87 -0
- package/pkg-dist/src/constants/chunks/bitcoin.js.map +1 -0
- package/pkg-dist/src/constants/chunks/decimals.js +47 -0
- package/pkg-dist/src/constants/chunks/decimals.js.map +1 -0
- package/pkg-dist/src/constants/chunks/liquidation.js +46 -0
- package/pkg-dist/src/constants/chunks/liquidation.js.map +1 -0
- package/pkg-dist/src/constants/chunks/lit-action-runtime.js +10 -0
- package/pkg-dist/src/constants/chunks/lit-action-runtime.js.map +1 -0
- package/pkg-dist/src/constants/chunks/loan-status.js +95 -0
- package/pkg-dist/src/constants/chunks/loan-status.js.map +1 -0
- package/pkg-dist/src/constants/chunks/price-oracle.js +49 -0
- package/pkg-dist/src/constants/chunks/price-oracle.js.map +1 -0
- package/pkg-dist/src/constants/chunks/quantum-time.js +81 -0
- package/pkg-dist/src/constants/chunks/quantum-time.js.map +1 -0
- package/pkg-dist/src/constants/chunks/time.js +53 -0
- package/pkg-dist/src/constants/chunks/time.js.map +1 -0
- package/pkg-dist/src/constants/index.js +88 -0
- package/pkg-dist/src/constants/index.js.map +1 -0
- package/pkg-dist/src/constants/message-hash-domains.js +39 -0
- package/pkg-dist/src/constants/message-hash-domains.js.map +1 -0
- package/pkg-dist/src/interfaces/chunks/authorization.i.js +3 -0
- package/pkg-dist/src/interfaces/chunks/authorization.i.js.map +1 -0
- package/pkg-dist/src/interfaces/chunks/bitcoin-data-provider.i.js +3 -0
- package/pkg-dist/src/interfaces/chunks/bitcoin-data-provider.i.js.map +1 -0
- package/pkg-dist/src/interfaces/chunks/circuit-breaker.i.js +8 -0
- package/pkg-dist/src/interfaces/chunks/circuit-breaker.i.js.map +1 -0
- package/pkg-dist/src/interfaces/chunks/mint-authorization.i.js +5 -0
- package/pkg-dist/src/interfaces/chunks/mint-authorization.i.js.map +1 -0
- package/pkg-dist/src/interfaces/chunks/owner-authorization.js +8 -0
- package/pkg-dist/src/interfaces/chunks/owner-authorization.js.map +1 -0
- package/pkg-dist/src/interfaces/chunks/price-oracle.i.js +3 -0
- package/pkg-dist/src/interfaces/chunks/price-oracle.i.js.map +1 -0
- package/pkg-dist/src/interfaces/chunks/vault-balance-module.i.js +3 -0
- package/pkg-dist/src/interfaces/chunks/vault-balance-module.i.js.map +1 -0
- package/pkg-dist/src/interfaces/chunks/vault-balance.i.js +3 -0
- package/pkg-dist/src/interfaces/chunks/vault-balance.i.js.map +1 -0
- package/pkg-dist/src/interfaces/chunks/vault-snapshot.i.js +3 -0
- package/pkg-dist/src/interfaces/chunks/vault-snapshot.i.js.map +1 -0
- package/pkg-dist/src/interfaces/index.js +30 -0
- package/pkg-dist/src/interfaces/index.js.map +1 -0
- package/pkg-dist/src/modules/authorization.module.js +479 -0
- package/pkg-dist/src/modules/authorization.module.js.map +1 -0
- package/pkg-dist/src/modules/bitcoin/address.js +323 -0
- package/pkg-dist/src/modules/bitcoin/address.js.map +1 -0
- package/pkg-dist/src/modules/bitcoin/provider.js +67 -0
- package/pkg-dist/src/modules/bitcoin/provider.js.map +1 -0
- package/pkg-dist/src/modules/bitcoin/signature.js +64 -0
- package/pkg-dist/src/modules/bitcoin/signature.js.map +1 -0
- package/pkg-dist/src/modules/bitcoin/transaction.js +274 -0
- package/pkg-dist/src/modules/bitcoin/transaction.js.map +1 -0
- package/pkg-dist/src/modules/bitcoin-data-provider.module.js +534 -0
- package/pkg-dist/src/modules/bitcoin-data-provider.module.js.map +1 -0
- package/pkg-dist/src/modules/bitcoin-provider-registry.js +165 -0
- package/pkg-dist/src/modules/bitcoin-provider-registry.js.map +1 -0
- package/pkg-dist/src/modules/business-rules-math.module.js +316 -0
- package/pkg-dist/src/modules/business-rules-math.module.js.map +1 -0
- package/pkg-dist/src/modules/chain-policy.js +90 -0
- package/pkg-dist/src/modules/chain-policy.js.map +1 -0
- package/pkg-dist/src/modules/dev-action-guard.js +47 -0
- package/pkg-dist/src/modules/dev-action-guard.js.map +1 -0
- package/pkg-dist/src/modules/lit-action-debug.js +85 -0
- package/pkg-dist/src/modules/lit-action-debug.js.map +1 -0
- package/pkg-dist/src/modules/lit-action-logger.module.js +190 -0
- package/pkg-dist/src/modules/lit-action-logger.module.js.map +1 -0
- package/pkg-dist/src/modules/lit-global-bootstrap.js +65 -0
- package/pkg-dist/src/modules/lit-global-bootstrap.js.map +1 -0
- package/pkg-dist/src/modules/position-id.js +40 -0
- package/pkg-dist/src/modules/position-id.js.map +1 -0
- package/pkg-dist/src/modules/price-oracle.module.js +504 -0
- package/pkg-dist/src/modules/price-oracle.module.js.map +1 -0
- package/pkg-dist/src/modules/pro-rata-fee.module.js +66 -0
- package/pkg-dist/src/modules/pro-rata-fee.module.js.map +1 -0
- package/pkg-dist/src/modules/protocol-parameters.module.js +216 -0
- package/pkg-dist/src/modules/protocol-parameters.module.js.map +1 -0
- package/pkg-dist/src/modules/protocol-pause.module.js +227 -0
- package/pkg-dist/src/modules/protocol-pause.module.js.map +1 -0
- package/pkg-dist/src/modules/quantum-time.module.js +320 -0
- package/pkg-dist/src/modules/quantum-time.module.js.map +1 -0
- package/pkg-dist/src/modules/vault-balance.module.js +428 -0
- package/pkg-dist/src/modules/vault-balance.module.js.map +1 -0
- package/pkg-dist/src/modules/vault-snapshot.js +517 -0
- package/pkg-dist/src/modules/vault-snapshot.js.map +1 -0
- package/pkg-dist/package.json +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "admin-liquidation-validator",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 110063,
|
|
4
|
+
"minifiedSize": 47354,
|
|
5
|
+
"compressionRatio": 0.5697555036660822,
|
|
6
|
+
"hash": "012055ef5492592ba081c74a9420bae18f7e2c7ce053426d0e23919eed658b4f",
|
|
7
|
+
"buildTime": 1776708630455,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "btc-transaction-signer",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 59888,
|
|
4
|
+
"minifiedSize": 26488,
|
|
5
|
+
"compressionRatio": 0.5577077210793482,
|
|
6
|
+
"hash": "eaaed046805bc36516fdc33dbde216a680e12209de03fb86c9d58375021a5c1f",
|
|
7
|
+
"buildTime": 1776708630602,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var _LIT_ACTION_=(()=>{var t=Object.defineProperty,e=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,i={};((e,r)=>{for(var o in r)t(e,o,{get:r[o],enumerable:!0})})(i,{main:()=>main});var a=11e3,n=100000000n,s=1000000000000000000n,c=1000000000000n,l=100000000000000n,u=546n,d=3,h=500,p=100,g=8e3;function m(t){switch(t){case 0:return"PENDING_DEPOSIT";case 1:return"PENDING_MINT";case 2:return"ACTIVE";case 3:return"EXPIRED";case 4:return"LIQUIDATABLE";case 5:return"LIQUIDATED";case 6:return"REPAID";case 7:return"CLOSED";default:return`UNKNOWN(${t})`}}var w="DiamondHands.Protocol",f="1",y=(t=>(t.SEPOLIA="sepolia",t.ETHEREUM="ethereum",t.HARDHAT="hardhat",t))(y||{}),b={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},T=["infura.io","drpc.org"],v={ethereum:1,sepolia:11155111,hardhat:1337};function S(t){const e=t.toLowerCase().trim();if("sepolia"===e)return"sepolia";if("ethereum"===e||"mainnet"===e)return"ethereum";if("hardhat"===e)return"hardhat";throw new Error(`Unsupported EVM chain: "${t}". Supported chains: ${Object.values(y).join(", ")}`)}function P(t){return v[t]}function E(t){return Math.floor(t/p)*p}async function $(){const t=Math.floor(Date.now()/1e3),e=t%p;if(!function(t){const e=(t??Math.floor(Date.now()/1e3))%p;return e<8||e>=92}(t))return;let r;r=e>=92?p-e+8:8-e,console.log(`[Quantum] Waiting ${r}s for safe moment (current time in quantum: ${e}s)`),await new Promise((t=>setTimeout(t,1e3*r))),console.log(`[Quantum] Safe quantum ready: ${E(Math.floor(Date.now()/1e3))}`)}var A=class t{static verifyAmountPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.amount||null===t.amount)throw new Error("auth.amount is required");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");return t}static async recoverSigner(t,e){try{const r=ethers.utils.arrayify(t),o=ethers.utils.hashMessage(r);return ethers.utils.recoverAddress(o,e)}catch(t){throw new Error(`Failed to recover signer: ${t instanceof Error?t.message:String(t)}`)}}static async checkEIP1271(t,e,r,o){try{const i=new ethers.Contract(t,["function isValidSignature(bytes32,bytes) view returns (bytes4)"],o);return"0x1626ba7e"===await i.isValidSignature(e,r)}catch{return!1}}static async verifyActionAuthorization(t,e,r,o){const i=Math.floor(Date.now()/1e3);await $();const a=o?.strictCurrentQuantum?i:void 0;try{!function(t,e,r){const o=e??Math.floor(Date.now()/1e3),i=E(o),a=E(t);if(r?.strictCurrentQuantum){if(a!==i)throw new Error(`[Quantum] Signature must come from current quantum (strict mode). Signature quantum: ${a}, current quantum: ${i}`);return}const n=i-p,s=i+p;if(a!==n&&a!==i&&a!==s)throw new Error(`[Quantum] Signature outside 3-quantum window. Signature quantum: ${a}, Valid range: [${n} (past), ${i} (current), ${s} (next)]`);if(a===n){if(o<n+8)throw new Error(`[Quantum] Dead zone violation: Current time (${o}) is in first 8 seconds of PAST quantum (${n}). This is unsafe due to boundary instability.`)}else if(a===s&&o>=s+92)throw new Error(`[Quantum] Dead zone violation: Current time (${o}) is in last 8 seconds of NEXT quantum (${s}). This is unsafe due to boundary instability.`)}(t.timestamp,a,{strictCurrentQuantum:!0===o?.strictCurrentQuantum})}catch{return!1}const n=e(),s=await this.recoverSigner(n,t.signature);return await r(s,n,t.signature)}static async verifyAmountPositionActionAuthorization(e,r,o){return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),r="string"==typeof e.amount?BigInt(e.amount):e.amount,o=[e.positionId,e.timestamp,e.chainId,r.toString(),t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],o)}),(async(e,i,a)=>e.toLowerCase()===r.toLowerCase()||!!o&&t.checkEIP1271(r,i,a,o)))}static async verifyMintAuthorization(t,e,r){return"mint-ucd"===t.action&&this.verifyAmountPositionActionAuthorization(t,e,r)}static async verifyWithdrawAuthorization(e,r,o){if("withdraw-btc"!==e.action)return!1;return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),r="string"==typeof e.amount?BigInt(e.amount):e.amount,o=[e.positionId,e.timestamp,e.chainId,r.toString(),e.destinationAddress,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","string","bytes32"],o)}),(async(e,i,a)=>e.toLowerCase()===r.toLowerCase()||!!o&&t.checkEIP1271(r,i,a,o)))}static verifyTermPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.newTerm||null===t.newTerm)throw new Error("auth.newTerm is required");if("number"!=typeof t.newTerm)throw new Error("auth.newTerm must be a number");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");return t}static async verifyTermPositionActionAuthorization(e,r,o){return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),r=[e.positionId,e.timestamp,e.chainId,e.newTerm,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],r)}),(async(e,i,a)=>e.toLowerCase()===r.toLowerCase()||!!o&&t.checkEIP1271(r,i,a,o)))}static async verifyExtendAuthorization(t,e,r){return"extend-position"===t.action&&this.verifyTermPositionActionAuthorization(t,e,r)}static async verifyBtcExecutionAuthorization(e,r,o){if("execute-btc-withdrawal"!==e.action)return!1;return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),r=[e.positionId,e.timestamp,e.chainId,e.utxoIdentifier,e.networkFee,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","string","uint256","bytes32"],r)}),(async(e,i,a)=>e.toLowerCase()===r.toLowerCase()||!!o&&t.checkEIP1271(r,i,a,o)),{strictCurrentQuantum:!0})}};function I(t){if(!0===t)return!0;if(!1===t)return!1;const e=globalThis;return!0===e.debugAction||(!0===e.LIT_ACTION_DEBUG||("true"===e.LIT_ACTION_DEBUG||function(){try{if("undefined"==typeof process||!process.env)return!1;const t=process.env.LIT_ACTION_DEBUG,e=process.env.DEBUG;return"1"===t||"true"===t||"1"===e||"true"===e}catch{return!1}}()))}function C(...t){I()&&console.log(...t)}var B=class{stepStart(t,e){}stepEnd(t,e){}log(t,e,...r){}warn(t,e){}error(t,e){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},U=class{constructor(t){this.stepStartTimes=new Map,this.stepDurations=new Map,this.TIMEOUT_MS=3e4,this.actionName=t,this.executionStartTime=Date.now();const e=Date.now(),r=Math.random().toString(36).substring(2,9);this.executionId=`exec_${e}_${r}`,this.logHeader()}logHeader(){console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution started`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`);void 0!==globalThis.litNodeContext?(console.log(`[${this.actionName}] Context: LIT Node`),console.log(`[${this.actionName}] Node: ${globalThis.litNodeContext?.nodeAddress||"unknown"}`)):console.log(`[${this.actionName}] Context: Browser/Test`),console.log(`[${this.actionName}] ========================================`)}stepStart(t,e){this.stepStartTimes.set(t,Date.now());const r=Date.now()-this.executionStartTime,o=this.TIMEOUT_MS-r;console.log(`[Step ${t}] ${e}...`),console.log(`[Step ${t}] Elapsed: ${r}ms | Remaining: ${o}ms`)}stepEnd(t,e=5e3){const r=this.stepStartTimes.get(t);if(!r)return console.warn(`[Step ${t}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const o=Date.now()-r;this.stepDurations.set(t,o),console.log(`[Step ${t}] Duration: ${o}ms`),o>e&&console.warn(`\u26a0\ufe0f [Step ${t}] Took ${o}ms (> ${e}ms threshold)`);const i=Date.now()-this.executionStartTime,a=this.TIMEOUT_MS-i;a<5e3&&console.warn(`\u26a0\ufe0f [Step ${t}] Time remaining: ${a}ms (approaching timeout)`)}log(t,e,...r){r.length>0?console.log(`[Step ${t}] ${e}`,...r):console.log(`[Step ${t}] ${e}`)}warn(t,e){console.warn(`\u26a0\ufe0f [Step ${t}] ${e}`)}error(t,e){console.error(`\u274c [Step ${t}] ${e}`)}getElapsed(){return Date.now()-this.executionStartTime}getRemaining(){return this.TIMEOUT_MS-this.getElapsed()}summary(){const t=Date.now()-this.executionStartTime,e=this.TIMEOUT_MS-t;if(console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution Summary`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`),console.log(`[${this.actionName}] Total time: ${(t/1e3).toFixed(2)}s`),console.log(`[${this.actionName}] Time remaining: ${e}ms`),this.stepDurations.size>0){console.log(`[${this.actionName}] Step breakdown:`);for(const[e,r]of this.stepDurations.entries()){const o=(r/t*100).toFixed(1);console.log(`[${this.actionName}] Step ${e}: ${r}ms (${o}%)`)}}t>2e4&&console.warn(`\u26a0\ufe0f [${this.actionName}] Slow execution: ${(t/1e3).toFixed(2)}s (${e}ms remaining)`),e<5e3&&console.warn(`\u26a0\ufe0f [${this.actionName}] CRITICAL: Only ${e}ms remaining before timeout!`),console.log(`[${this.actionName}] ========================================`)}getExecutionId(){return this.executionId}isDebugEnabled(){return!0}},M=class{static create(t="LIT Action",e){return I(e)?new U(t):new B}};function x(){const t=globalThis,e=t.jsParams;return null==e||"object"!=typeof e||Array.isArray(e)?t:e}function D(t){if(t instanceof Error)return t.message;if("string"==typeof t)return t;try{return JSON.stringify(t)}catch{return String(t)}}function O(t){return t instanceof Error&&"AbortError"===t.name}var k=class{constructor(t){this.config=t,this.timeout=t.timeout||15e3,this.logger=M.create("BitcoinDataProvider")}async getCurrentBlockHeight(){if(this.logger.stepStart("0","getCurrentBlockHeight"),this.config.rpcHelper){const t=await this.config.rpcHelper.getBlockCount();return this.logger.stepEnd("0"),t}const t=new AbortController,e=setTimeout((()=>t.abort()),this.timeout);try{const r=Date.now(),o=await fetch(`${this.config.providerUrl}/blocks/tip/height`,{signal:t.signal,headers:{Accept:"text/plain","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(e),C(`[BitcoinDataProvider] blocks/tip/height ${Date.now()-r}ms (timeout budget ${this.timeout}ms)`),!o.ok)throw new Error(`Failed to fetch block height: ${o.status} ${o.statusText}`);const i=await o.text();if(i.trim().startsWith("<!DOCTYPE")||i.trim().startsWith("<html"))throw new Error("Bitcoin RPC endpoint returned HTML instead of block height. URL may be pointing to web interface instead of API endpoint. Expected: plain text number, Got: HTML document. Check that URL ends with /api/esplora (not root /)");const a=parseInt(i.trim(),10);if(isNaN(a)){const t=i.length>200?i.substring(0,200)+"... (truncated)":i;throw new Error(`Invalid block height response: ${t}`)}return this.logger.stepEnd("0"),a}catch(t){if(clearTimeout(e),this.logger.stepEnd("0"),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOs(t){this.logger.log("0",`Original address: "${t}"`);const e=this.stripNetworkPrefix(t);if(this.logger.log("0",`Cleaned address: "${e}"`),this.config.rpcHelper)return await this.fetchUTXOsFromRPC(e);try{return await this.fetchUTXOsFromProvider(this.config.providerUrl,e)}catch(t){if(this.logger.warn("0",`Primary provider failed: ${D(t)}`),this.config.fallbackProviders&&this.config.fallbackProviders.length>0){const t=[...this.config.fallbackProviders].sort(((t,e)=>t.priority-e.priority));for(const r of t)try{return this.logger.log("0",`Trying fallback: ${r.name} (${r.url})`),await this.fetchUTXOsFromProvider(r.url,e)}catch(t){this.logger.warn("0",`Fallback ${r.name} failed: ${D(t)}`);continue}}throw new Error(`Failed to fetch UTXOs: ${D(t)}`)}}stripNetworkPrefix(t){return t.replace(/^(REGTEST_|TESTNET_|MAINNET_)/,"")}async fetchUTXOsFromRPC(t){if(!this.config.rpcHelper)throw new Error("RPC helper not configured");const e=this.config.rpcWallet||"";return(await this.config.rpcHelper.listUnspent(e,t)).map((t=>({txid:t.txid,vout:t.vout,satoshis:BigInt(Math.round(1e8*t.amount)),confirmations:t.confirmations})))}parseBlockstreamUTXOs(t,e){if(!Array.isArray(t))throw new Error("Invalid Blockstream response format: expected an array");const r=t,o=[];for(const t of r){let r=0;t.status&&"object"==typeof t.status?t.status.confirmed&&(r=void 0!==t.status.block_height&&t.status.block_height>0?e-t.status.block_height+1:1):void 0!==t.confirmations&&(r=t.confirmations);const i=t.txid,a=void 0!==t.vout?t.vout:t.n,n=void 0!==t.value?t.value:t.satoshis;if(void 0===i||void 0===a||void 0===n)throw new Error("Invalid UTXO row: missing txid, vout/n, or value/satoshis");o.push({txid:i,vout:a,satoshis:BigInt(n),confirmations:r})}return o}async fetchUTXOsFromProvider(t,e){this.logger.stepStart("1","fetchUTXOsFromProvider");const r=`${t}/address/${e}/utxos`;this.logger.log("1",`Fetching UTXOs from ${r}`);const o=new AbortController,i=setTimeout((()=>o.abort()),this.timeout);try{const t=Date.now(),[a,n]=await Promise.all([this.getCurrentBlockHeight(),fetch(r,{signal:o.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}})]);if(clearTimeout(i),C(`[BitcoinDataProvider] parallel(tipHeight + GET utxos) wall ${Date.now()-t}ms endpoint tail .../${e.slice(0,12)}.../utxos (timeout budget ${this.timeout}ms)`),this.logger.log("1",`Current block height: ${a}`),!n.ok)throw new Error(`Bitcoin provider error: ${n.status} ${n.statusText}`);const s=await n.text();if(s.trim().startsWith("<!DOCTYPE")||s.trim().startsWith("<html"))throw new Error("Bitcoin RPC endpoint returned HTML instead of JSON. URL may be pointing to web interface instead of API endpoint. Expected: JSON array of UTXOs, Got: HTML document. Check that URL ends with /api/esplora (not root /)");let c;try{c=JSON.parse(s)}catch(t){const e=s.length>200?s.substring(0,200)+"... (truncated)":s;throw new Error(`Failed to parse JSON response: ${D(t)}. Response: ${e}`)}this.logger.log("1",`Raw API Response: ${JSON.stringify(c,null,2)}`);const l=this.parseBlockstreamUTXOs(c,a);return this.logger.stepEnd("1"),l}catch(t){if(clearTimeout(i),this.logger.stepEnd("1"),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOSet(t,e){let r=[],o=null;const i=d,a=h;for(let e=1;e<=i;e++)try{this.logger.log("2",`UTXO query attempt ${e}/${i}...`),r=await this.getUTXOs(t),this.logger.log("2",`UTXO query succeeded on attempt ${e}`),o=null;break}catch(t){o=t instanceof Error?t:new Error(D(t)),this.logger.error("2",`UTXO query failed on attempt ${e}: ${D(t)}`),e<i&&(this.logger.log("2",`Waiting ${a}ms before retry...`),await new Promise((t=>setTimeout(t,a))))}if(o)throw new Error(`Failed to query UTXOs after ${i} attempts: ${o.message}`);const n=r.reduce(((t,e)=>t+e.satoshis),0n),s=r.filter((t=>t.confirmations>=e)).reduce(((t,e)=>t+e.satoshis),0n),c=n-s;return{utxos:r,totalBalance:n,totalUTXOs:r.length,confirmedBalance:s,unconfirmedBalance:c}}async getBalance(t){const e=this.stripNetworkPrefix(t),r=`${this.config.providerUrl}/address/${e}`;this.logger.log("3",`Fetching balance from ${r}`);const o=new AbortController,i=setTimeout((()=>o.abort()),this.timeout);try{const t=Date.now(),e=await fetch(r,{signal:o.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(i),C(`[BitcoinDataProvider] GET address chain_stats ${Date.now()-t}ms (timeout budget ${this.timeout}ms)`),!e.ok)throw new Error(`Bitcoin provider error: ${e.status} ${e.statusText}`);const a=await e.json();if(!a.chain_stats)throw new Error("Invalid response: missing chain_stats");const n=a.chain_stats.funded_txo_sum,s=a.chain_stats.spent_txo_sum;if(void 0===n||void 0===s)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${n}, spent_txo_sum: ${s}`);const c=BigInt(n-s);return this.logger.log("3",`Balance: ${c.toString()} sats`),this.logger.log("3",` - Funded: ${n} sats, Spent: ${s} sats`),c}catch(t){if(clearTimeout(i),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);const r=D(t);throw this.logger.log("3",`Balance fetch failed: ${r}`),new Error(`Failed to fetch Bitcoin balance for address ${e}: ${r}`)}}async getTransaction(t){if(this.config.rpcHelper)try{const e=this.config.rpcWallet||"",r=await this.config.rpcHelper.getTransaction(e,t);return{txid:r.txid,confirmations:r.confirmations||0}}catch(t){const e=D(t),r=null!==t&&"object"==typeof t&&"code"in t&&"number"==typeof t.code?t.code:void 0;if(e.includes("Invalid or non-wallet transaction")||-5===r)return null;throw t}const e=new AbortController,r=setTimeout((()=>e.abort()),this.timeout);try{const o=Date.now(),i=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(r),C(`[BitcoinDataProvider] GET tx/${t.slice(0,10)}\u2026 ${Date.now()-o}ms (timeout budget ${this.timeout}ms)`),!i.ok){if(404===i.status)return null;throw new Error(`Bitcoin provider error: ${i.status} ${i.statusText}`)}const a=await i.json();return a.status?{txid:a.txid??t,confirmations:a.status.confirmed&&a.status.block_height?1:0}:null}catch(t){if(clearTimeout(r),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);if(D(t).includes("404"))return null;throw t}}async getTransactionOutputs(t){if(!this.config.providerUrl)return null;const e=new AbortController,r=setTimeout((()=>e.abort()),this.timeout);try{const o=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(r),!o.ok){if(404===o.status)return null;throw new Error(`Bitcoin provider error: ${o.status} ${o.statusText}`)}const i=await o.json();return i.vout&&Array.isArray(i.vout)?i.vout.map(((t,e)=>({index:e,scriptpubkey:t.scriptpubkey,scriptpubkey_address:t.scriptpubkey_address,value:t.value??0}))):null}catch(t){if(clearTimeout(r),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}},N=class t{constructor(e,r,o){if(r&&Array.isArray(r))this.sources=r;else{if(!Array.isArray(e))throw new Error("Price oracle requires a priceProviders array \u2014 defaults and fallbacks have been removed. Pass 3 distinct {name, apiKey} entries from the supported set: "+t.ACTIVE_PROVIDER_NAMES.join(", "));this.sources=this.buildSources(e)}this.sources.sort(((t,e)=>t.priority-e.priority)),this.validateProviderCount()}validateProviderCount(){if(this.sources.length<t.MIN_DISTINCT_PROVIDERS)throw new Error(`Price oracle requires at least ${t.MIN_DISTINCT_PROVIDERS} eligible providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported active providers: ${t.ACTIVE_PROVIDER_NAMES.join(", ")}`)}buildSources(e){if(0===e.length)throw new Error("priceProviders must be a non-empty array of 3 distinct providers, each with an apiKey.");const r=[],o=new Set;let i=1;for(const a of e){if(!a||"string"!=typeof a.name)throw new Error("priceProviders entries must be {name, apiKey, apiSecret?} objects");const e=a.name.toLowerCase();if(!t.ACTIVE_PROVIDER_NAMES.includes(e))throw new Error(`Unsupported price provider "${a.name}". Supported: ${t.ACTIVE_PROVIDER_NAMES.join(", ")}`);if(o.has(e))throw new Error(`Duplicate price provider "${e}" \u2014 the oracle requires 3 DISTINCT providers so a single upstream failure or manipulation cannot compromise consensus. Use 3 different provider names from: ${t.ACTIVE_PROVIDER_NAMES.join(", ")}`);switch(o.add(e),e){case"binance":if(!a.apiKey)throw new Error("Binance provider requires an apiKey");r.push(this.createBinanceSource(a.apiKey,i++));break;case"coinbase":if(!a.apiKey)throw new Error("Coinbase provider requires an apiKey");r.push(this.createCoinbaseSource(a.apiKey,i++));break;case"cryptocompare":if(!a.apiKey)throw new Error("CryptoCompare provider requires an apiKey");r.push(this.createCryptoCompareSource(a.apiKey,i++));break;case"coinmarketcap":if(!a.apiKey)throw new Error("CoinMarketCap provider requires an apiKey");r.push(this.createCoinMarketCapSource(a.apiKey,i++));break;case"coingecko":if(!a.apiKey)throw new Error("CoinGecko provider requires an apiKey (Demo or Pro). Unauthenticated CoinGecko access is not accepted \u2014 the free tier is rate-limited and edge-cached, which breaks median consensus under load.");r.push(this.createCoinGeckoSource(a.apiKey,i++));break;default:throw new Error(`Unhandled provider: ${e}`)}}if(r.length<t.MIN_DISTINCT_PROVIDERS)throw new Error(`Insufficient price providers: need at least ${t.MIN_DISTINCT_PROVIDERS} distinct entries, got ${r.length}.`);return this.selectSampledSources(r)}createCryptoCompareSource(t,e){return{name:"CryptoCompare",fetchPrice:async()=>{const e=new URL("https://min-api.cryptocompare.com/data/price");e.searchParams.set("fsym","BTC"),e.searchParams.set("tsyms","USDT"),e.searchParams.set("api_key",t);const r=await(async t=>{const e=new AbortController,r=setTimeout((()=>e.abort()),5e3);try{const o=await fetch(t,{signal:e.signal,headers:{Accept:"application/json"}});if(clearTimeout(r),!o.ok)throw new Error(`HTTP ${o.status} ${o.statusText}`);return await o.json()}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error("Request timeout after 5000ms");throw t}})(e.toString()),o=Number(r?.USDT);if(!Number.isFinite(o)||o<=0)throw new Error("Invalid CryptoCompare price payload");return o},priority:e}}createFetchJson(){return async(t,e)=>{const r=new AbortController,o=setTimeout((()=>r.abort()),5e3);try{const o=await fetch(t,{headers:{Accept:"application/json",...e||{}},signal:r.signal});if(!o.ok)throw new Error(`HTTP ${o.status} ${o.statusText}`);return await o.json()}finally{clearTimeout(o)}}}getSelectionSeed(){const t="undefined"!=typeof process?process.env.PRICE_PROVIDER_SELECTION_SEED:void 0,e=t?Number(t):NaN;return Number.isFinite(e)?e:("undefined"!=typeof process,0,Math.floor(Date.now()+1e6*Math.random()))}selectSampledSources(t){const e=[...t];let r=this.getSelectionSeed()>>>0;for(let t=e.length-1;t>0;t--){const o=Math.floor((r=1664525*r+1013904223>>>0,r/4294967296*(t+1)));[e[t],e[o]]=[e[o],e[t]]}return e.slice(0,3).map(((t,e)=>({...t,priority:e+1})))}createCoinGeckoSource(t,e){if(!t)throw new Error("createCoinGeckoSource requires an apiKey");const r=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const e=t.startsWith("pro:"),o=e?t.slice(4):t,i=e?{"x-cg-pro-api-key":o}:{"x-cg-demo-api-key":o},a=new URL("https://api.coingecko.com/api/v3/simple/price");a.searchParams.set("ids","bitcoin"),a.searchParams.set("vs_currencies","usd");const n=await r(a.toString(),i),s=Number(n?.bitcoin?.usd);if(!Number.isFinite(s)||s<=0)throw new Error("Invalid CoinGecko price payload");return s},priority:e}}createBinanceSource(t,e){if(!t)throw new Error("createBinanceSource requires an apiKey");const r=this.createFetchJson();return{name:"Binance",fetchPrice:async()=>{const e=new URL("https://api.binance.com/api/v3/ticker/price");e.searchParams.set("symbol","BTCUSDT");const o=await r(e.toString(),{"X-MBX-APIKEY":t}),i=Number(o?.price);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid Binance price payload");return i},priority:e}}createCoinbaseSource(t,e){if(!t)throw new Error("createCoinbaseSource requires an apiKey");const r=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const e=await r("https://api.coinbase.com/v2/prices/BTC-USDT/spot",{"CB-ACCESS-KEY":t}),o=Number(e?.data?.amount);if(!Number.isFinite(o)||o<=0)throw new Error("Invalid Coinbase price payload");return o},priority:e}}createCoinMarketCapSource(t,e){const r=this.createFetchJson();return{name:"CoinMarketCap",fetchPrice:async()=>{const e=new URL("https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest");e.searchParams.set("symbol","BTC"),e.searchParams.set("convert","USDT");const o=await r(e.toString(),{"X-CMC_PRO_API_KEY":t}),i=Number(o?.data?.BTC?.[0]?.quote?.USDT?.price??o?.data?.BTC?.quote?.USDT?.price);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CoinMarketCap price payload");return i},priority:e}}async getBTCPrice(){const t=Date.now();if(3!==this.sources.length)throw new Error(`Price oracle requires exactly 3 providers. Currently configured: ${this.sources.length}`);const e=new Promise(((t,e)=>{setTimeout((()=>{e(new Error("Price oracle global timeout after 8000ms"))}),8e3)})),r=Promise.all(this.sources.map((async t=>{const e=Date.now(),r=await t.fetchPrice();return console.log(`[Price Oracle] \u2705 [+${Date.now()-e}ms] ${t.name}: $${r.toLocaleString()}`),r}))),o=[...await Promise.race([r,e])].sort(((t,e)=>t-e)),i=o[1];if((o[2]-o[0])/o[0]>.01){const t=this.sources.map((t=>t.name)).join(", "),e=o.map((t=>`$${t.toLocaleString()}`)).join(", ");throw new Error(`Price consensus failed: prices not within 1% tolerance. Providers: [${t}]. Prices: [${e}]`)}const a=Math.round(100*i),n=1000000n*BigInt(a),s=Date.now()-t;return console.log(`[Price Oracle] Median price: $${i.toLocaleString()} \u2014 total time: ${s}ms`),console.log(`[Price Oracle] Price with 8 decimals: ${n}`),n}async getBTCPriceConsensus(){console.log("[Price Oracle] Fetching BTC price with consensus...");const t=(await Promise.allSettled(this.sources.map((async t=>{const e=await t.fetchPrice();return{source:t.name,price:e}})))).filter((t=>"fulfilled"===t.status)).map((t=>t.value));if(0===t.length)throw new Error("No price sources returned data");console.log(`[Price Oracle] Got prices from ${t.length}/${this.sources.length} sources:`),t.forEach((t=>{console.log(` ${t.source}: $${t.price.toLocaleString()}`)}));const e=t.map((t=>t.price));e.sort(((t,e)=>t-e));const r=e[Math.floor(e.length/2)],o=e[0],i=e[e.length-1]/o,a=t.filter((t=>Math.abs(t.price-r)/r<=.02));if(i>1.05&&a.length===t.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(i-1)).toFixed(1)}% spread)`);if(a.length<t.length){const e=t.filter((t=>!a.find((e=>e.source===t.source))));if(console.log(`[Price Oracle] \u26a0\ufe0f Detected ${e.length} outlier(s):`),e.forEach((t=>{const e=Math.abs(t.price-r)/r;console.log(` ${t.source}: $${t.price.toLocaleString()} (${(100*e).toFixed(1)}% deviation)`)})),a.length<2)throw new Error("Price consensus failed: insufficient valid sources after outlier removal");console.log(`[Price Oracle] \u2705 Outliers filtered, continuing with ${a.length} valid sources`);const o=a.map((t=>t.price));o.sort(((t,e)=>t-e));const i=o[Math.floor(o.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${i.toLocaleString()}`);const n=Math.round(100*i);return 1000000n*BigInt(n)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${r.toLocaleString()}`);const n=Math.round(100*r);return 1000000n*BigInt(n)}};N.ACTIVE_PROVIDER_NAMES=["cryptocompare","coinbase","binance","coinmarketcap","coingecko"],N.MIN_DISTINCT_PROVIDERS=3;var R=N,q=class{constructor(t){this.config=t,this.bitcoinProvider=t.bitcoinProvider}async calculateBalance(t,e){const r=await this.bitcoinProvider.getBalance(e),o=await this.getAuthorizedSpendsFromContract(t),i=o.reduce(((t,e)=>t+e.satoshis),0n),a=r>i?r-i:0n;return{totalUTXOs:[],totalBalance:r,authorizedUTXOs:o,authorizedBalance:i,authorizedSpendsHash:this.computeAuthorizedSpendsHash(t,o),availableUTXOs:[],availableBalance:a,vaultAddress:e,positionId:t,timestamp:Date.now()}}async calculateTrustedBalance(t,e,r){if(r)return await this.calculateBalance(t,e);const o=this.config.minConfirmations||6,i=await this.bitcoinProvider.getUTXOSet(e,o),a=await this.getAuthorizedSpendsFromContract(t);for(const t of a){if(i.utxos.some((e=>e.txid===t.txid&&e.vout===t.vout)))throw new Error(`Authorized UTXO ${t.txid}:${t.vout} not yet spent. Transaction was authorized in smart contract but not signed and broadcasted to Bitcoin network. Complete the transaction by signing and broadcasting it.`)}const n=a.reduce(((t,e)=>t+e.satoshis),0n),s=i.utxos.filter((t=>!this.isUTXOAuthorized(t,a))),c=s.reduce(((t,e)=>t+e.satoshis),0n),l=this.computeAuthorizedSpendsHash(t,a);return{totalUTXOs:i.utxos,totalBalance:i.totalBalance,authorizedUTXOs:a,authorizedBalance:n,authorizedSpendsHash:l,availableUTXOs:s,availableBalance:c,vaultAddress:e,positionId:t,timestamp:Date.now()}}async getTrustedBalance(t,e){return(await this.calculateTrustedBalance(t,e)).availableBalance}async getAvailableUTXOs(t,e){return(await this.calculateTrustedBalance(t,e)).availableUTXOs}async isUTXOAvailable(t,e,r,o){return(await this.getAvailableUTXOs(t,e)).some((t=>t.txid===r&&t.vout===o))}isUTXOAuthorized(t,e){return e.some((e=>e.txid===t.txid&&e.vout===t.vout))}async getAuthorizedSpendsFromContract(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let r;r=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const o=new ethers.providers.StaticJsonRpcProvider({url:r,timeout:g},this.config.chainId),i=new ethers.Contract(this.config.contractAddress,[{inputs:[{internalType:"bytes32",name:"positionId",type:"bytes32"}],name:"getAuthorizedSpends",outputs:[{components:[{internalType:"string",name:"txid",type:"string"},{internalType:"uint32",name:"vout",type:"uint32"},{internalType:"uint256",name:"satoshis",type:"uint256"},{internalType:"string",name:"targetAddress",type:"string"},{internalType:"uint256",name:"targetAmount",type:"uint256"},{internalType:"uint256",name:"authorizedAt",type:"uint256"}],internalType:"struct LoanOperationsManager.AuthorizedSpend[]",name:"",type:"tuple[]"}],stateMutability:"view",type:"function"}],o);return(await i.getAuthorizedSpends(e)).map((e=>({txid:e.txid,vout:Number(e.vout),satoshis:BigInt(e.satoshis.toString()),positionId:t,targetAddress:e.targetAddress,targetAmount:BigInt(e.targetAmount.toString()),timestamp:Number(e.authorizedAt)})))}computeAuthorizedSpendsHash(t,e){const r=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;if(0===e.length)return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,[]]));const o=e.map((t=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.txid)),r=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.targetAddress));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","uint32","uint256","bytes32","uint256","uint256"],[e,t.vout,t.satoshis.toString(),r,t.targetAmount.toString(),t.timestamp]))}));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,o]))}};function L(t,e){if(0===t)return{termDurationDays:0,termLengthDays:30*e,isExpired:!1,daysUntilExpiry:30*e,daysIntoGracePeriod:0};const r=30*e,o=Math.floor(Date.now()/1e3)-t,i=Math.floor(o/86400);return{termDurationDays:i,termLengthDays:r,isExpired:i>r,daysUntilExpiry:Math.max(0,r-i),daysIntoGracePeriod:Math.max(0,i-r)}}function F(t,e,r,o){if(!t)return r??13e3;const i=o??a,n=Math.min(e,30);return a+n*n*(i-a)/900}function _(t,e,r){const o=t*e/(100000000n*n);if(0n===r)return{collateralValueUsd:o,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:o,collateralRatioBps:Number(o*s*10000n/r)}}function X(t,e){if(t<0n)throw new Error("BTC amount cannot be negative");if(e<=0n)throw new Error("BTC price must be positive");return t*e/n}function z(t,e){if(t<0n)throw new Error("Collateral value cannot be negative");if(e<=0n)throw new Error("Debt must be positive to calculate ratio");const r=Number(10000n*(10000000000n*t)/e);if(r<0)throw new Error(`Collateral ratio out of bounds: ${r} bps (${r/100}%)`);return!isFinite(r)||r>Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:r}function H(t){return t/100}var V=class{constructor(t){this.termManagerAddress=t.termManagerAddress,this.loanOpsManagerAddress=t.loanOpsManagerAddress,this.liquidationManagerAddress=t.liquidationManagerAddress,this.chain=t.chain,this.chainId=t.chainId,this.rpcUrl=t.rpcUrl}async getLiquidationThreshold(){try{let t;t=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const e=new ethers.providers.StaticJsonRpcProvider({url:t,timeout:g},{name:"any",chainId:this.chainId}),r=[{inputs:[],name:"liquidationThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],o=new ethers.Contract(this.loanOpsManagerAddress,r,e),i=await o.liquidationThreshold();return Number(i.toString())}catch(t){throw console.error("[ProtocolParameters] Error fetching liquidation threshold:",t.message),new Error(`Failed to fetch liquidation threshold: ${t.message}`)}}async getTermFees(t){try{let e;e=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const r=new ethers.providers.StaticJsonRpcProvider({url:e,timeout:g},{name:"any",chainId:this.chainId}),o=[{inputs:[{internalType:"uint256",name:"_termMonths",type:"uint256"}],name:"getTermFees",outputs:[{internalType:"uint88",name:"originationFee",type:"uint88"},{internalType:"uint88",name:"extensionFee",type:"uint88"}],stateMutability:"view",type:"function"}],i=new ethers.Contract(this.termManagerAddress,o,r),a=await i.getTermFees(t);return{originationFeeBps:Number(a.originationFee?.toString?.()??a[0]?.toString?.()??a[0]),extensionFeeBps:Number(a.extensionFee?.toString?.()??a[1]?.toString?.()??a[1])}}catch(t){throw console.error("[ProtocolParameters] Error fetching term fees:",t.message),new Error(`Failed to fetch term fees: ${t.message}`)}}async getAuthorizedSpendsHash(t){try{const e=this.rpcUrl||await Lit.Actions.getRpcUrl({chain:this.chain}),r=new ethers.providers.StaticJsonRpcProvider({url:e,timeout:g},{name:"any",chainId:this.chainId}),o=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`,i=new ethers.Contract(this.loanOpsManagerAddress,[{inputs:[{name:"positionId",type:"bytes32"}],name:"getAuthorizedSpendsHash",outputs:[{name:"",type:"bytes32"}],stateMutability:"view",type:"function"}],r);return await i.getAuthorizedSpendsHash(o)}catch(t){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",t.message),new Error(`Failed to fetch authorized spends hash: ${t.message}`)}}async getMaxEscalatedThreshold(){try{let t;t=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const e=new ethers.providers.StaticJsonRpcProvider({url:t,timeout:g},{name:"any",chainId:this.chainId}),r=[{inputs:[],name:"maxEscalatedThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],o=new ethers.Contract(this.liquidationManagerAddress,r,e),i=await o.maxEscalatedThreshold();return Number(i.toString())}catch(t){throw console.error("[ProtocolParameters] Error fetching max escalated threshold:",t.message),new Error(`Failed to fetch max escalated threshold: ${t.message}`)}}};function W(t){return new V(t)}var j=class{constructor(t){this.config=t}async getVaultSnapshot(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const r=!0===this.config.debugOverrides?.useStubbedBtcData,[o,i]=r?this.buildStubbedBtcSnapshotInputs(e):await Promise.all([this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress),this.config.priceOracle.getBTCPriceConsensus()]),a=W({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,liquidationManagerAddress:this.config.liquidationManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),n=this.config.termOverride??e.selectedTerm,[s,c,l]=await Promise.all([a.getLiquidationThreshold(),a.getMaxEscalatedThreshold(),a.getTermFees(n)]),u=L(e.termStartTimestamp,e.selectedTerm),d=F(u.isExpired,u.daysIntoGracePeriod,s,c),h=_(o.availableBalance,i,e.ucdDebt),p=h.collateralRatioBps<d,g=h.collateralRatioBps-d;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,previousExpiryAt:e.previousExpiryAt,totalTerm:e.totalTerm,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:i,collateralValueUsd:h.collateralValueUsd,collateralRatioBps:h.collateralRatioBps,termDurationDays:u.termDurationDays,termLengthDays:u.termLengthDays,isExpired:u.isExpired,daysUntilExpiry:u.daysUntilExpiry,daysIntoGracePeriod:u.daysIntoGracePeriod,currentLiquidationThreshold:d,isLiquidatable:p,marginToLiquidationBps:g,liquidationThresholdBps:s,originationFeeBps:l.originationFeeBps,extensionFeeBps:l.extensionFeeBps,timestamp:Date.now()}}async getVaultSnapshotFast(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const r=!0===this.config.debugOverrides?.useStubbedBtcData,[o,i]=r?this.buildStubbedBtcSnapshotInputs(e):await Promise.all([this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress,!0),this.config.priceOracle.getBTCPriceConsensus()]),a=W({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,liquidationManagerAddress:this.config.liquidationManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),n=this.config.termOverride??e.selectedTerm,[s,c,l]=await Promise.all([a.getLiquidationThreshold(),a.getMaxEscalatedThreshold(),a.getTermFees(n)]),u=L(e.termStartTimestamp,e.selectedTerm),d=F(u.isExpired,u.daysIntoGracePeriod,s,c),h=_(o.availableBalance,i,e.ucdDebt),p=h.collateralRatioBps<d,g=h.collateralRatioBps-d;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,previousExpiryAt:e.previousExpiryAt,totalTerm:e.totalTerm,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:i,collateralValueUsd:h.collateralValueUsd,collateralRatioBps:h.collateralRatioBps,termDurationDays:u.termDurationDays,termLengthDays:u.termLengthDays,isExpired:u.isExpired,daysUntilExpiry:u.daysUntilExpiry,daysIntoGracePeriod:u.daysIntoGracePeriod,currentLiquidationThreshold:d,isLiquidatable:p,marginToLiquidationBps:g,liquidationThresholdBps:s,originationFeeBps:l.originationFeeBps,extensionFeeBps:l.extensionFeeBps,timestamp:Date.now()}}buildStubbedBtcSnapshotInputs(t){const e=BigInt(this.config.debugOverrides?.stubbedAvailableBtcSats??"10000000"),r=BigInt(this.config.debugOverrides?.stubbedBtcPriceUsd??"5000000000000");return[{totalUTXOs:[],totalBalance:e,authorizedUTXOs:[],authorizedBalance:0n,authorizedSpendsHash:this.config.debugOverrides?.stubbedAuthorizedSpendsHash??ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[t.positionId,[]])),availableUTXOs:[],availableBalance:e,vaultAddress:t.vaultAddress,positionId:t.positionId,timestamp:Date.now()},r]}async queryPositionState(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let r;r=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const o=new ethers.providers.StaticJsonRpcProvider({url:r,timeout:g},this.config.chainId),i=new ethers.Contract(this.config.contractAddress,[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],o),a=await i.core(),n=new ethers.Contract(a,[{inputs:[{internalType:"bytes32",name:"positionId",type:"bytes32"}],name:"getPositionDetails",outputs:[{components:[{internalType:"bytes32",name:"positionId",type:"bytes32"},{internalType:"bytes32",name:"pkpId",type:"bytes32"},{internalType:"uint256",name:"ucdDebt",type:"uint256"},{internalType:"string",name:"vaultAddress",type:"string"},{internalType:"address",name:"borrower",type:"address"},{internalType:"uint40",name:"createdAt",type:"uint40"},{internalType:"uint40",name:"lastUpdated",type:"uint40"},{internalType:"uint16",name:"selectedTerm",type:"uint16"},{internalType:"uint40",name:"expiryAt",type:"uint40"},{internalType:"enum LoanStatusLib.LoanStatus",name:"status",type:"uint8"},{internalType:"uint40",name:"previousExpiryAt",type:"uint40"},{internalType:"uint16",name:"totalTerm",type:"uint16"}],internalType:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],o),s=await n.getPositionDetails(e),c=Number(s.status);if(c<0||c>7)throw console.error("[VaultSnapshot] INVALID STATUS - ABI decoding error detected"),console.error(" Position ID:",e),console.error(" Contract:",this.config.contractAddress),console.error(" Status decoded:",c,"(expected 0-7)"),console.error(" Full position:",JSON.stringify(s,null,2)),new Error(`Invalid position status decoded from contract: ${c} (expected 0-7). This indicates an ABI decoding issue. Position: ${e}`);const l=Number(s.expiryAt),u=Number(s.selectedTerm),d=Number(s.totalTerm),h=Number(s.previousExpiryAt),p=(w=u,0===(m=l)?0:m-30*w*86400);var m,w;return{positionId:s.positionId,pkpId:s.pkpId,borrower:s.borrower,vaultAddress:this.parseVaultAddress(s.vaultAddress,S(this.config.chain)),ucdDebt:BigInt(s.ucdDebt.toString()),termStartTimestamp:p,selectedTerm:u,status:c,expiryAt:l,previousExpiryAt:h,totalTerm:d}}parseVaultAddress(t,e){try{const r=JSON.parse(t);if("object"==typeof r&&null!==r){if("sepolia"===e.toLowerCase())return r.regtest||r.testnet||r.mainnet;const t=function(t){return b[t]}(e);return"testnet"===t&&(r.testnet||r.regtest)||r.mainnet}}catch(t){}return t}async isLiquidatable(t){return(await this.getVaultSnapshot(t)).isLiquidatable}async getCollateralRatio(t){return(await this.getVaultSnapshot(t)).collateralRatioBps}async getAvailableBalance(t){return(await this.getVaultSnapshot(t)).availableBTCSats}async hasSufficientCollateral(t,e,r){const o=await this.getVaultSnapshot(t);return function(t,e,r,o){const i=e+r;return 0n===i||Number(t*s*10000n/i)>=o}(o.collateralValueUsd,o.ucdDebt,e,r)}};var G="function getProtocolPauseStatus() view returns (tuple(bool positionManagerPaused, bool positionManagerLiquidationsPaused, bool positionManagerCorePaused, bool loanOperationsPaused, bool collateralManagerPaused, bool liquidationManagerPaused, bool circuitBreakerPaused, bool ucdControllerPaused, bool ucdTokenPaused, bool simplePsmPaused))",K={mintUcd:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],withdrawBtc:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],processPayment:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused"],extendPosition:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],liquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],adminLiquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],btcTransactionSign:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused"]};function J(t){return{positionManagerPaused:t.positionManagerPaused,positionManagerLiquidationsPaused:t.positionManagerLiquidationsPaused,positionManagerCorePaused:t.positionManagerCorePaused,loanOperationsPaused:t.loanOperationsPaused,collateralManagerPaused:t.collateralManagerPaused,liquidationManagerPaused:t.liquidationManagerPaused,circuitBreakerPaused:t.circuitBreakerPaused,ucdControllerPaused:t.ucdControllerPaused,ucdTokenPaused:t.ucdTokenPaused,simplePsmPaused:t.simplePsmPaused}}var Q=new ethers.utils.Interface(["function views() view returns (address)","function paused() view returns (bool)","function liquidationPaused() view returns (bool)","function simplePsm() view returns (address)"]),Y=new ethers.utils.Interface(["function core() view returns (address)","function loanOps() view returns (address)","function collateralManager() view returns (address)","function liquidationManager() view returns (address)","function circuitBreaker() view returns (address)"]),Z=new ethers.utils.Interface(["function getUcdController() view returns (address)","function ucdToken() view returns (address)"]),tt=new ethers.utils.Interface(["function paused() view returns (bool)"]);async function et(t,e,r){return t.call({to:e,data:r})}async function rt(t,e){const r=new ethers.utils.Interface([G]).encodeFunctionData("getProtocolPauseStatus",[]);let o;try{return function(t){const e=new ethers.utils.Interface([G]).decodeFunctionResult("getProtocolPauseStatus",t)[0];return Array.isArray(e)?J({positionManagerPaused:e[0],positionManagerLiquidationsPaused:e[1],positionManagerCorePaused:e[2],loanOperationsPaused:e[3],collateralManagerPaused:e[4],liquidationManagerPaused:e[5],circuitBreakerPaused:e[6],ucdControllerPaused:e[7],ucdTokenPaused:e[8],simplePsmPaused:e[9]}):J(e)}(await t.call({to:e,data:r}))}catch(t){o=t}try{return await async function(t,e){const r=await et(t,e,Q.encodeFunctionData("views",[])),[o]=Q.decodeFunctionResult("views",r),[i,a,n,s,c,l,u,d]=await Promise.all([et(t,e,Q.encodeFunctionData("paused",[])),et(t,e,Q.encodeFunctionData("liquidationPaused",[])),et(t,e,Q.encodeFunctionData("simplePsm",[])),et(t,o,Y.encodeFunctionData("core",[])),et(t,o,Y.encodeFunctionData("loanOps",[])),et(t,o,Y.encodeFunctionData("collateralManager",[])),et(t,o,Y.encodeFunctionData("liquidationManager",[])),et(t,o,Y.encodeFunctionData("circuitBreaker",[]))]),h=Q.decodeFunctionResult("paused",i)[0],p=Q.decodeFunctionResult("liquidationPaused",a)[0],g=Q.decodeFunctionResult("simplePsm",n)[0],m=Y.decodeFunctionResult("core",s)[0],w=Y.decodeFunctionResult("loanOps",c)[0],f=Y.decodeFunctionResult("collateralManager",l)[0],y=Y.decodeFunctionResult("liquidationManager",u)[0],b=Y.decodeFunctionResult("circuitBreaker",d)[0],T=await et(t,w,Z.encodeFunctionData("getUcdController",[])),v=await et(t,w,Z.encodeFunctionData("ucdToken",[])),S=Z.decodeFunctionResult("getUcdController",T)[0],P=Z.decodeFunctionResult("ucdToken",v)[0],E=async e=>{if(!e||e===ethers.constants.AddressZero)return!1;try{const r=await et(t,e,tt.encodeFunctionData("paused",[]));return tt.decodeFunctionResult("paused",r)[0]}catch{return!1}},[$,A,I,C,B,U,M,x]=await Promise.all([E(m),E(w),E(f),E(y),E(b),E(S),E(P),E(g)]);return J({positionManagerPaused:h,positionManagerLiquidationsPaused:p,positionManagerCorePaused:$,loanOperationsPaused:A,collateralManagerPaused:I,liquidationManagerPaused:C,circuitBreakerPaused:B,ucdControllerPaused:U,ucdTokenPaused:M,simplePsmPaused:x})}(t,e)}catch(t){const e=o instanceof Error?o.message:String(o),r=t instanceof Error?t.message:String(t);throw new Error(`getProtocolPauseStatus failed (aggregate: ${e}; decomposed: ${r})`)}}function ot(t){if("string"!=typeof t)throw new Error("positionId must be a string");const e=t.trim(),r=/^0x/i.test(e)?e.slice(2):e;if(0===r.length)throw new Error("positionId is empty");if(!/^[0-9a-fA-F]+$/.test(r))throw new Error("positionId must be hexadecimal (bytes32)");if(r.length>64)throw new Error("positionId exceeds 32 bytes");return"0x"+r.padStart(64,"0").toLowerCase()}var it={hardhat:{chain:"hardhat",chainId:v.hardhat,bitcoinNetwork:b.hardhat,allowArbitraryBitcoinProvider:!0,allowArbitraryEvmRpc:!0,minBitcoinConfirmations:1},sepolia:{chain:"sepolia",chainId:v.sepolia,bitcoinNetwork:b.sepolia,allowArbitraryBitcoinProvider:!1,allowArbitraryEvmRpc:!1,minBitcoinConfirmations:1},ethereum:{chain:"ethereum",chainId:v.ethereum,bitcoinNetwork:b.ethereum,allowArbitraryBitcoinProvider:!1,allowArbitraryEvmRpc:!1,minBitcoinConfirmations:6}};var at,nt=[{inputs:[{internalType:"uint256",name:"chainId",type:"uint256"}],name:"getProviders",outputs:[{internalType:"bytes32[]",name:"ids",type:"bytes32[]"},{components:[{internalType:"bytes",name:"ciphertext",type:"bytes"},{internalType:"bytes32",name:"pkpId",type:"bytes32"},{internalType:"uint64",name:"addedAt",type:"uint64"}],internalType:"struct BitcoinProviderRegistry.ProviderEntry[]",name:"entries",type:"tuple[]"}],stateMutability:"view",type:"function"}];async function st(t,e){const r=66===e.length?"0x"+e.slice(-40):e;try{const e=await Lit.Actions.Decrypt({pkpId:r,ciphertext:t}),o="string"==typeof e?e:String(e??"");if(!o)throw new Error("Decrypt returned empty plaintext");return o}catch(t){throw new Error(`Failed to decrypt Bitcoin provider URL (pkpId=${e}): ${t?.message??String(t)}`)}}async function ct(t){const{policy:e,bitcoinProviderUrl:r,ethersProvider:o,registryAddress:i}=t;if(!r||"string"!=typeof r)throw new Error("resolveBitcoinProviderForPolicy: bitcoinProviderUrl is required");if(e.allowArbitraryBitcoinProvider)return{name:"Custom Provider",url:r,minConfirmations:e.minBitcoinConfirmations,network:e.bitcoinNetwork};if(!i)throw new Error(`Bitcoin provider registry address is required on chain "${e.chain}" \u2014 set contractAddresses.BitcoinProviderRegistry. Hardcoded allowlists have been removed (see audit C-1/H-6 fix).`);const a=await async function(t,e,r){if(!r||"string"!=typeof r)throw new Error("fetchBitcoinProviderEntries: registryAddress (BitcoinProviderRegistry) is required");if(!e)throw new Error("fetchBitcoinProviderEntries: ethers provider is required");const o=new ethers.Contract(r,nt,e),[i,a]=await o.getProviders(t),n=[];for(let t=0;t<i.length;t++)n.push({providerId:i[t],ciphertext:a[t].ciphertext,pkpId:a[t].pkpId,addedAt:Number(a[t].addedAt)});return n}(e.chainId,o,i);if(0===a.length)throw new Error(`No Bitcoin providers registered for chainId=${e.chainId} in ${i}. Admin must addProvider(...) via AdminModule before LIT Actions can sign.`);const n=await async function(t,e){if(!e)throw new Error("matchAllowedProviderUrl: candidateUrl is required");for(const r of t)if(await st(r.ciphertext,r.pkpId)===e)return r.providerId;return null}(a,r);if(!n)throw new Error(`Bitcoin provider URL not in any registered encrypted slot for chainId=${e.chainId}`);return{name:`Registry Provider ${n.slice(0,10)}`,url:r,minConfirmations:e.minBitcoinConfirmations,network:e.bitcoinNetwork}}async function main(t){for(const[e,r]of Object.entries(t))globalThis[e]=r;const e=M.create("BTC Withdrawal Validator");console.log("[Step 0] Validating configuration..."),e.stepStart("0","Validate configuration parameters");const r=globalThis.chain,o=globalThis.bitcoinProviderUrl;if(!r)throw new Error('Missing required parameter: "chain". Must be "sepolia" or "ethereum"');if(!o)throw new Error('Missing required parameter: "bitcoinProviderUrl". Must be an approved Bitcoin RPC provider URL');const i=function(t){const e=S(t),r=it[e];if(!r)throw new Error(`No chain policy registered for "${t}"`);return r}(r),a=i.chain,n=i.bitcoinNetwork;let s;if(console.log(` \u2705 Chain: ${r} (${a})`),console.log(` \u2705 Bitcoin Network: ${n}`),!globalThis.contractAddresses||"object"!=typeof globalThis.contractAddresses)throw new Error('Required "contractAddresses" object with: PositionManager, LoanOperationsManagerModule, TermManagerModule');const p=globalThis.contractAddresses.PositionManager,g=globalThis.contractAddresses.LoanOperationsManagerModule,y=globalThis.contractAddresses.TermManagerModule,b=globalThis.contractAddresses.UCDController,v=globalThis.contractAddresses.LITActionValidator,E=globalThis.contractAddresses.LiquidationManagerModule??globalThis.contractAddresses.LiquidationManager??"",$=globalThis.contractAddresses.BTCSpendAuthorizer??"";if(!(p&&g&&y&&b&&E))throw new Error("All contract addresses are required: PositionManager, LoanOperationsManagerModule, TermManagerModule, UCDController, and LiquidationManager or LiquidationManagerModule");console.log(" \u2705 Contract addresses loaded and validated"),console.log(` PositionManager: ${p}`),console.log(` LoanOpsManager: ${g}`),e.stepEnd("0"),console.log("[Step 0.5] Critical system health checks..."),e.stepStart("0.5","Critical system health checks"),e.warn("0.5","System health checks temporarily disabled (SystemHealthModule not available)"),e.log("0.5","\u2705 System health checks complete (skipped)"),e.stepEnd("0.5");const I=globalThis.auth;if(!I||"object"!=typeof I)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!I.positionId)throw new Error("auth.positionId is required");if("number"!=typeof I.timestamp)throw new Error("auth.timestamp must be a number");if(!I.signature)throw new Error("auth.signature is required");const C=BigInt(I.amount);if(C<=0n)throw new Error(`Withdrawal amount must be positive, got: ${C.toString()} sats`);const B=u;if(C<B)throw new Error(`Withdrawal amount ${C.toString()} sats is below minimum ${B.toString()} sats (Bitcoin dust limit). This amount is not economically viable for Bitcoin network transactions.`);const U=I.destinationAddress;if(!U||"string"!=typeof U)throw new Error('Missing or invalid "destinationAddress" parameter - must be a valid Bitcoin address');const D=x().publicKey;if(!D)throw new Error("publicKey must not be blank");if("string"!=typeof D||0===D.length)throw new Error("publicKey must not be blank");const O=D;if(!(()=>{try{const t=/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,35}$/.test(U),e=/^(bc1|tb1|bcrt1)[0-9a-zA-Z]{11,87}$/.test(U);return!(!t&&!e)&&("testnet"===n||"regtest"===n?U.startsWith("m")||U.startsWith("n")||U.startsWith("2")||U.startsWith("tb1")||U.startsWith("bcrt1"):"mainnet"===n&&(U.startsWith("1")||U.startsWith("3")||U.startsWith("bc1")))}catch(t){return!1}})())throw new Error(`Invalid Bitcoin address format for ${n} network: ${U}. Expected ${"mainnet"===n?"address starting with 1, 3, or bc1":"address starting with m, n, 2, tb1, or bcrt1"}`);console.log(` \u2705 Destination address validated: ${U} (${n})`);let N="0.5";try{console.log("[BTC Withdrawal] Started"),console.log(` Position: ${I.positionId}`),console.log(` Withdrawal Amount: ${C.toString()} sats`),console.log(` Destination Address: ${U}`);const t=new R(globalThis.priceProviders,void 0),n=globalThis.rpcUrl;let u;n&&"string"==typeof n?(!function(t,e){if(t.allowArbitraryEvmRpc)return;if(!e||"string"!=typeof e)throw new Error("validateEvmRpcForPolicy: rpcUrl must be a non-empty string");let r;try{r=new URL(e).hostname}catch(t){throw new Error(`Invalid rpcUrl: ${t?.message??String(t)}`)}if(!T.some((t=>r===t||r.endsWith(`.${t}`))))throw new Error(`Custom RPC domain "${r}" is not in the approved list: ${T.join(", ")}`)}(i,n),console.log(` Using RPC URL override: ${function(t){if(null==t||"string"!=typeof t)return"[missing]";let e=t.replace(/([?&])(dkey|apikey|api_key|key|token|secret|password|auth|authorization)=[^&]*/gi,"$1$2=[REDACTED]");try{const t=new URL(e);(t.username||t.password)&&(t.username="",t.password="",e=t.toString())}catch{}return e}(n)}`),u=n):(u=await Lit.Actions.getRpcUrl({chain:r}),console.log(` Using LIT-provided RPC URL for chain: ${r}`));const S=new ethers.providers.JsonRpcProvider(u);s=await ct({policy:i,bitcoinProviderUrl:o,ethersProvider:S,registryAddress:globalThis.contractAddresses.BitcoinProviderRegistry}),console.log(` \u2705 Bitcoin Provider: ${s.name} (min confs ${s.minConfirmations})`);const B=new k({providerUrl:s.url});!function(t,e){const r=K[t],o=[];for(const t of r)e[t]&&o.push(t);if(o.length>0)throw new Error(JSON.stringify({code:"PROTOCOL_PAUSED",operation:t,paused:o,status:e}))}("withdrawBtc",await rt(S,p));const M=(L={contractAddress:$||g,chain:r,chainId:P(a),rpcUrl:u,bitcoinProvider:B,minConfirmations:s.minConfirmations},new q(L));N="1",console.log("[Step 1] Getting vault snapshot..."),e.stepStart("1","Get vault snapshot");const x=function(t){return new j(t)}({contractAddress:p,termManagerAddress:y,loanOpsManagerAddress:g,liquidationManagerAddress:E,chain:r,chainId:P(a),rpcUrl:u,vaultBalance:M,priceOracle:t}),D=await x.getVaultSnapshot(I.positionId);console.log(` \u2705 Vault balance: ${D.availableBTCSats.toString()} sats`),console.log(` \u2705 Current debt: ${D.ucdDebt.toString()} wei`),e.stepEnd("1"),N="2",console.log("[Step 2] Authenticating caller..."),e.stepStart("2","Authenticate caller");const F=e.getElapsed();console.log(`[TIMING] Step 2 started at ${F}ms`);const _=P(a),V=new ethers.providers.StaticJsonRpcProvider(u,_);let W;if(5===D.status){if(!v)throw new Error("LITActionValidator address required for LIQUIDATED position withdrawals. Pass contractAddresses.LITActionValidator in jsParams.");const t=new ethers.Contract(v,["function pkpOwners(bytes32) view returns (address)"],V);if(W=await t.pkpOwners(D.pkpId),!W||W===ethers.constants.AddressZero)throw new Error("No PKP owner registered for this liquidated position in LITActionValidator");console.log(` \u2139\ufe0f LIQUIDATED position \u2014 verifying against PKP owner: ${W}`)}else W=D.borrower;if(!await A.verifyWithdrawAuthorization(I,W,V))throw new Error("Caller not authorized");console.log(" \u2705 Caller authorized"),e.stepEnd("2"),N="2.5",console.log("[Step 2.5] Validating position status..."),e.stepStart("2.5","Validate position status");const G=e.getElapsed();console.log(`[TIMING] Step 2.5 started at ${G}ms`);if(![0,1,2,6,5].includes(D.status))throw new Error(`Position status ${m(D.status)} does not allow withdrawal. Valid statuses: PENDING_DEPOSIT, PENDING_MINT, ACTIVE, REPAID, LIQUIDATED.`);console.log(` \u2705 Position status valid for withdrawal: ${m(D.status)}`),e.stepEnd("2.5"),N="3",console.log("[Step 3] Validating post-withdrawal collateral ratio..."),e.stepStart("3","Validate collateral ratio");const J=e.getElapsed();console.log(`[TIMING] Step 3 started at ${J}ms`);const Q=C;if(0n===D.ucdDebt)console.log(" \u2705 Position has zero debt - withdrawal allowed");else{if(D.btcPriceUsd<c||D.btcPriceUsd>l)throw new Error(`Bitcoin price ${D.btcPriceUsd} is outside acceptable range (${c} - ${l}). This may indicate oracle manipulation or stale price data. Please try again later.`);console.log(` \u2705 BTC price validation passed: $${(Number(D.btcPriceUsd)/1e8).toFixed(2)}`);const t=D.availableBTCSats-Q,e=z(X(t,D.btcPriceUsd),D.ucdDebt);if(e<D.liquidationThresholdBps)throw new Error(`Insufficient collateral: post-withdrawal ratio ${H(e)}% < liquidation threshold ${H(D.liquidationThresholdBps)}%. Withdrawal would result in insufficient collateralization.`);console.log(` \u2705 Post-withdrawal collateral ratio: ${H(e)}%`),console.log(` \u2705 Exceeds liquidation threshold: ${H(D.liquidationThresholdBps)}%`)}e.stepEnd("3"),N="4",console.log("[Step 4] Validating withdrawal amount and balance..."),e.stepStart("4","Validate withdrawal amount and balance");const Y=e.getElapsed();console.log(`[TIMING] Step 4 started at ${Y}ms`);const Z="string"==typeof I.amount?BigInt(I.amount):I.amount;if(Z!==C)throw new Error(`Authorization amount mismatch: auth.amount=${Z.toString()} sats, but requested withdrawal=${C.toString()} sats. The signed authorization must match the requested withdrawal amount.`);if(console.log(" \u2705 Authorization amount matches withdrawal amount"),Q>D.availableBTCSats)throw new Error(`Insufficient vault balance: need ${Q.toString()} sats but only ${D.availableBTCSats.toString()} sats available`);console.log(" \u2705 Total deduction is within available balance");const tt=D.availableBTCSats-Q,et=D.ucdDebt,it=X(tt,D.btcPriceUsd),at=et>0n?z(it,et):void 0;if(console.log(` Post-withdrawal collateral: ${tt.toString()} sats`),console.log(` Post-withdrawal collateral value: $${(Number(it)/1e8).toFixed(2)}`),console.log(` Post-withdrawal debt: ${et.toString()} wei`),console.log(` Post-withdrawal collateral ratio: ${et>0n&&void 0!==at?H(Number(at)):"\u221e"}%`),tt<0n)throw new Error(`Withdrawal would result in negative balance: ${tt.toString()} sats`);console.log(" \u2705 Withdrawal validation passed"),e.stepEnd("4"),N="4.5",console.log("[Step 4.5] Querying vault UTXOs from Bitcoin network..."),e.stepStart("4.5","Query and select UTXO");const nt=e.getElapsed();console.log(`[TIMING] Step 4.5 started at ${nt}ms`);const st=D.vaultAddress;if(!st)throw new Error("Vault address not found in snapshot");console.log(` Vault address: ${st}`);let lt=[],ut=null;const dt=d,ht=h;for(let t=1;t<=dt;t++)try{console.log(` \ud83d\udd04 UTXO query attempt ${t}/${dt}...`),lt=await B.getUTXOs(st),console.log(` \u2705 UTXO query succeeded on attempt ${t}`),ut=null;break}catch(e){ut=e,console.error(` \u274c UTXO query failed on attempt ${t}:`,e.message),t<dt&&(console.log(` \u23f3 Waiting ${ht}ms before retry...`),await new Promise((t=>setTimeout(t,ht))))}if(ut)throw new Error(`Failed to query UTXOs after ${dt} attempts: ${ut.message}`);if(!lt||0===lt.length)throw new Error(`No UTXOs found in vault ${st}`);console.log(` \u2705 Found ${lt.length} UTXO(s) in vault`);const pt=s.minConfirmations,gt=lt.filter((t=>t.confirmations>=pt));if(0===gt.length)throw new Error(`No confirmed UTXOs found in vault. Required confirmations: ${pt}, found ${lt.length} UTXO(s) with insufficient confirmations.`);console.log(` \u2705 Found ${gt.length} confirmed UTXO(s)`);const mt=gt.filter((t=>t.satoshis>=Q));let wt;if(0===mt.length){const t=gt.reduce(((t,e)=>t+e.satoshis),0n);if(t<Q)throw new Error(`Insufficient vault balance for withdrawal. Need ${Q.toString()} sats, total across ${gt.length} UTXO(s): ${t.toString()} sats.`);wt=[...gt].sort(((t,e)=>Number(e.satoshis-t.satoshis)))[0],console.log(" \u2139\ufe0f No single UTXO covers withdrawal \u2014 multi-UTXO consolidation will be used in Phase 2."),console.log(` Primary UTXO (contract auth): ${wt.txid}:${wt.vout} (${wt.satoshis.toString()} sats)`)}else wt=mt.sort(((t,e)=>Number(t.satoshis-e.satoshis)))[0];console.log(` \u2705 Selected UTXO: ${wt.txid}:${wt.vout}`),console.log(` \u2705 UTXO value: ${wt.satoshis.toString()} sats`),console.log(` \u2705 UTXO confirmations: ${wt.confirmations}`);const ft=D.availableBTCSats;if(wt.satoshis<ft&&console.warn(` \u26a0\ufe0f Selected UTXO value (${wt.satoshis.toString()}) < calculated balance (${ft.toString()}). This is expected for multi-UTXO vaults where we select one UTXO.`),wt.satoshis<Q)throw new Error(`Selected UTXO value (${wt.satoshis.toString()} sats) < withdrawal amount (${Q.toString()} sats). This should never happen after eligibility filtering.`);console.log(" \u2705 UTXO validation passed"),console.log(" \ud83d\udd0d Verifying UTXO vout against Bitcoin transaction...");const yt=await B.getTransactionOutputs(wt.txid);if(yt){for(const t of yt)if(t.scriptpubkey_address===st){t.index!==wt.vout?(console.log(` \u26a0\ufe0f Esplora API reported vout=${wt.vout} but vault output is at vout=${t.index}. Correcting.`),wt={...wt,vout:t.index}):console.log(` \u2705 UTXO vout=${wt.vout} confirmed correct`);break}}else console.log(` \u26a0\ufe0f Could not fetch transaction outputs for vout verification - using API-reported vout=${wt.vout}`);e.stepEnd("4.5"),N="5",console.log("[Step 5] Building authorization message..."),e.stepStart("5","Build authorization message");const bt=e.getElapsed();console.log(`[TIMING] Step 5 started at ${bt}ms`);const Tt=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(I.action)),vt=D.authorizedSpendsHash,St=ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["uint256"],[D.ucdDebt])),Pt=ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["address","address","address","address"],[p,g,y,b])),Et=BigInt(I.timestamp),$t=tt,At=D.btcPriceUsd;if(At<c||At>l)throw new Error(`BTC price ${At.toString()} (8 decimals) is outside acceptable range (${c.toString()} - ${l.toString()})`);console.log("[Step 5] DEBUG: Message Hash Parameters:"),console.log(" positionId:",I.positionId),console.log(" action:",I.action),console.log(" actionHash:",Tt),console.log(" authorizedSpendsHash:",vt),console.log(" ucdDebtHash:",St),console.log(" withdrawalAddress:",U),console.log(" totalDeduction:",Q.toString()),console.log(" newCollateral:",$t.toString()),console.log(" quantumTimestamp:",Et.toString()),console.log(" btcPrice:",At.toString()),console.log(" utxoTxid:",wt.txid),console.log(" utxoVout:",wt.vout);const It=ot(I.positionId),Ct=P(a),Bt={name:w,version:f,chainId:Ct,verifyingContract:ethers.utils.getAddress(g)},Ut={WithdrawalAuthorization:[{name:"positionId",type:"bytes32"},{name:"actionHash",type:"bytes32"},{name:"authorizedSpendsHash",type:"bytes32"},{name:"ucdDebtHash",type:"bytes32"},{name:"contractBundleHash",type:"bytes32"},{name:"withdrawalAddress",type:"string"},{name:"totalDeduction",type:"uint256"},{name:"newCollateral",type:"uint256"},{name:"quantumTimestamp",type:"uint256"},{name:"btcPrice",type:"uint256"},{name:"utxoTxid",type:"string"},{name:"utxoVout",type:"uint32"}]},Mt=ethers.utils._TypedDataEncoder.hash(Bt,Ut,{positionId:It,actionHash:Tt,authorizedSpendsHash:vt,ucdDebtHash:St,contractBundleHash:Pt,withdrawalAddress:U,totalDeduction:Q.toString(),newCollateral:$t.toString(),quantumTimestamp:Et.toString(),btcPrice:At.toString(),utxoTxid:wt.txid,utxoVout:wt.vout});console.log("[Step 5] Contract message hash for signing:",Mt),console.log("[Step 5] Message hash bytes length:",ethers.utils.arrayify(Mt).length),e.stepEnd("5"),N="6",console.log("[Step 6] Signing contract message hash..."),e.stepStart("6","Sign message");const xt=e.getElapsed();console.log(`[TIMING] Step 6 started at ${xt}ms`),e.log("6","========================================"),e.log("6","\ud83d\udd0d FINAL HASH INPUTS - ALL LIT NODES MUST MATCH"),e.log("6","========================================"),e.log("6",`Position ID: ${I.positionId}`),e.log("6",`Action Hash: ${Tt}`),e.log("6",`Authorized Spends Hash: ${vt}`),e.log("6",`UCD Debt Hash: ${St}`),e.log("6",`Contract Bundle Hash: ${Pt}`),e.log("6",`Destination Address: ${U}`),e.log("6",`Total Deduction: ${Q.toString()}`),e.log("6",`New Collateral: ${$t.toString()}`),e.log("6",`Quantum Timestamp: ${Et.toString()}`),e.log("6",`BTC Price: ${At.toString()}`),e.log("6",`Selected UTXO Txid: ${wt.txid}`),e.log("6",`Selected UTXO Vout: ${wt.vout}`),e.log("6",`Selected UTXO Satoshis: ${wt.satoshis.toString()}`),e.log("6",`Selected UTXO Confirmations: ${wt.confirmations}`),e.log("6","---"),e.log("6",`\ud83d\udcdd FINAL CONTRACT MESSAGE HASH: ${Mt}`),e.log("6","========================================"),e.log("6","Preparing signEcdsa call"),e.log("6",`contractMessageHash: ${Mt}`),e.log("6",`publicKey: ${O}`),e.log("6","publicKey with 0x prefix: "+("0x"+O));const Dt=ethers.utils.arrayify(Mt);e.log("6",`messageBytes length: ${Dt.length}`),e.log("6",`messageBytes: ${JSON.stringify(Array.from(Dt))}`),e.log("6","About to sign with wallet-derived key (Chipotle)...");const Ot=Date.now(),kt=globalThis.validatorWalletAddress;if(!kt||!kt.startsWith("0x"))throw new Error("validatorWalletAddress required \u2014 must be a createWallet-derived address");const Nt=await Lit.Actions.getPrivateKey({pkpId:kt}),Rt="string"==typeof Nt?Nt.startsWith("0x")?Nt:`0x${Nt}`:String(Nt),qt=new ethers.Wallet(Rt),Lt=new ethers.utils.SigningKey(Rt);e.log("6",`Wallet-derived signer address: ${qt.address}`),e.log("6",`validatorWalletAddress: ${kt}`);const Ft=Lt.signDigest(Mt),_t=ethers.utils.joinSignature(Ft),Xt=Date.now()-Ot;e.log("6",`Chipotle signature created in ${Xt}ms`),e.log("6",`signature: ${_t}`);const zt=globalThis.contractAddresses.BTCSpendAuthorizer?.trim()||"";let Ht=null;if(zt){const t=ot(I.positionId),r=P(a),o=$t+Q,i={name:w,version:f,chainId:r,verifyingContract:ethers.utils.getAddress(zt)},n={BtcSpendAuth:[{name:"positionId",type:"bytes32"},{name:"txid",type:"string"},{name:"vout",type:"uint32"},{name:"satoshis",type:"uint256"},{name:"targetAddress",type:"string"},{name:"targetAmount",type:"uint256"}]},s=ethers.utils._TypedDataEncoder.hash(i,n,{positionId:t,txid:wt.txid,vout:wt.vout,satoshis:o.toString(),targetAddress:U,targetAmount:Q.toString()});e.log("6",`EIP-712 BtcSpendAuth digest: ${s}`),Ht=await qt._signTypedData(i,n,{positionId:t,txid:wt.txid,vout:wt.vout,satoshis:o.toString(),targetAddress:U,targetAmount:Q.toString()}),e.log("6",`btcSpendAuth signature: ${Ht}`)}e.stepEnd("6"),console.log("[BTC Withdrawal] \u2705 Complete"),e.summary();const Vt={approved:!0,positionId:I.positionId,actionHash:Tt,authorizedSpendsHash:vt,ucdDebtHash:St,contractBundleHash:Pt,totalDeduction:Q.toString(),remainingCollateral:tt.toString(),newCollateralRatioBps:et>0n&&void 0!==at?Number(at):void 0,destinationAddress:U,signature:_t,btcSpendAuthSignature:Ht,timestamp:Et.toString(),btcPrice:At.toString(),utxoTxid:wt.txid,utxoVout:wt.vout,utxoSatoshis:wt.satoshis.toString()};return console.log("[BTC Withdrawal] \ud83d\udd0d Response data before JSON.stringify:"),console.log(" totalDeduction:",Q,"type:",typeof Q,"toString():",Q.toString()),console.log(" remainingCollateral:",tt,"type:",typeof tt,"toString():",tt.toString()),console.log(" btcPrice:",At,"type:",typeof At,"toString():",At.toString()),console.log(" timestamp:",Et,"type:",typeof Et,"toString():",Et.toString()),console.log(" Full response object:",JSON.stringify(Vt,null,2)),Vt}catch(t){return console.error("[BTC Withdrawal] \u274c Failed:",t.message),console.error(`[BTC Withdrawal] Failed at step: ${N}`),e.error("error",`Failed: ${t.message}`),{approved:!1,reason:t.message||t.toString(),positionId:I?.positionId,timestamp:Date.now()}}var L}return at=i,((i,a,n,s)=>{if(a&&"object"==typeof a||"function"==typeof a)for(let c of r(a))o.call(i,c)||c===n||t(i,c,{get:()=>a[c],enumerable:!(s=e(a,c))||s.enumerable});return i})(t({},"__esModule",{value:!0}),at)})();if("object"==typeof _LIT_ACTION_)if("function"==typeof _LIT_ACTION_.main)var main=_LIT_ACTION_.main;else"function"==typeof _LIT_ACTION_.go&&_LIT_ACTION_.go();
|
|
1
|
+
var _LIT_ACTION_=(()=>{var t=Object.defineProperty,e=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,i={};((e,r)=>{for(var o in r)t(e,o,{get:r[o],enumerable:!0})})(i,{main:()=>main});var n=11e3,a=100000000n,s=1000000000000000000n,c=1000000000000n,l=100000000000000n,u=546n,d=3,h=500,p=100,g=8e3;function m(t){switch(t){case 0:return"PENDING_DEPOSIT";case 1:return"PENDING_MINT";case 2:return"ACTIVE";case 3:return"EXPIRED";case 4:return"LIQUIDATABLE";case 5:return"LIQUIDATED";case 6:return"REPAID";case 7:return"CLOSED";default:return`UNKNOWN(${t})`}}var w="DiamondHands.Protocol",f="1",y=(t=>(t.SEPOLIA="sepolia",t.ETHEREUM="ethereum",t.HARDHAT="hardhat",t))(y||{}),b={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},T=["infura.io","drpc.org"],v={ethereum:1,sepolia:11155111,hardhat:1337};function S(t){const e=t.toLowerCase().trim();if("sepolia"===e)return"sepolia";if("ethereum"===e||"mainnet"===e)return"ethereum";if("hardhat"===e)return"hardhat";throw new Error(`Unsupported EVM chain: "${t}". Supported chains: ${Object.values(y).join(", ")}`)}function P(t){return v[t]}function E(t){return Math.floor(t/p)*p}async function $(){const t=Math.floor(Date.now()/1e3),e=t%p;if(!function(t){const e=(t??Math.floor(Date.now()/1e3))%p;return e<8||e>=92}(t))return;let r;r=e>=92?p-e+8:8-e,console.log(`[Quantum] Waiting ${r}s for safe moment (current time in quantum: ${e}s)`),await new Promise((t=>setTimeout(t,1e3*r))),console.log(`[Quantum] Safe quantum ready: ${E(Math.floor(Date.now()/1e3))}`)}var A=class t{static verifyAmountPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.amount||null===t.amount)throw new Error("auth.amount is required");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");return t}static async recoverSigner(t,e){try{const r=ethers.utils.arrayify(t),o=ethers.utils.hashMessage(r);return ethers.utils.recoverAddress(o,e)}catch(t){throw new Error(`Failed to recover signer: ${t instanceof Error?t.message:String(t)}`)}}static async checkEIP1271(t,e,r,o){try{const i=new ethers.Contract(t,["function isValidSignature(bytes32,bytes) view returns (bytes4)"],o);return"0x1626ba7e"===await i.isValidSignature(e,r)}catch{return!1}}static async verifyActionAuthorization(t,e,r,o){const i=Math.floor(Date.now()/1e3);await $();const n=o?.strictCurrentQuantum?i:void 0;try{!function(t,e,r){const o=e??Math.floor(Date.now()/1e3),i=E(o),n=E(t);if(r?.strictCurrentQuantum){if(n!==i)throw new Error(`[Quantum] Signature must come from current quantum (strict mode). Signature quantum: ${n}, current quantum: ${i}`);return}const a=i-p,s=i+p;if(n!==a&&n!==i&&n!==s)throw new Error(`[Quantum] Signature outside 3-quantum window. Signature quantum: ${n}, Valid range: [${a} (past), ${i} (current), ${s} (next)]`);if(n===a){if(o<a+8)throw new Error(`[Quantum] Dead zone violation: Current time (${o}) is in first 8 seconds of PAST quantum (${a}). This is unsafe due to boundary instability.`)}else if(n===s&&o>=s+92)throw new Error(`[Quantum] Dead zone violation: Current time (${o}) is in last 8 seconds of NEXT quantum (${s}). This is unsafe due to boundary instability.`)}(t.timestamp,n,{strictCurrentQuantum:!0===o?.strictCurrentQuantum})}catch{return!1}const a=e(),s=await this.recoverSigner(a,t.signature);return await r(s,a,t.signature)}static async verifyAmountPositionActionAuthorization(e,r,o){return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),r="string"==typeof e.amount?BigInt(e.amount):e.amount,o=[e.positionId,e.timestamp,e.chainId,r.toString(),t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],o)}),(async(e,i,n)=>e.toLowerCase()===r.toLowerCase()||!!o&&t.checkEIP1271(r,i,n,o)))}static async verifyMintAuthorization(t,e,r){return"mint-ucd"===t.action&&this.verifyAmountPositionActionAuthorization(t,e,r)}static async verifyWithdrawAuthorization(e,r,o){if("withdraw-btc"!==e.action)return!1;return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),r="string"==typeof e.amount?BigInt(e.amount):e.amount,o=[e.positionId,e.timestamp,e.chainId,r.toString(),e.destinationAddress,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","string","bytes32"],o)}),(async(e,i,n)=>e.toLowerCase()===r.toLowerCase()||!!o&&t.checkEIP1271(r,i,n,o)))}static verifyTermPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.newTerm||null===t.newTerm)throw new Error("auth.newTerm is required");if("number"!=typeof t.newTerm)throw new Error("auth.newTerm must be a number");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");return t}static async verifyTermPositionActionAuthorization(e,r,o){return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),r=[e.positionId,e.timestamp,e.chainId,e.newTerm,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],r)}),(async(e,i,n)=>e.toLowerCase()===r.toLowerCase()||!!o&&t.checkEIP1271(r,i,n,o)))}static async verifyExtendAuthorization(t,e,r){return"extend-position"===t.action&&this.verifyTermPositionActionAuthorization(t,e,r)}static async verifyBtcExecutionAuthorization(e,r,o){if("execute-btc-withdrawal"!==e.action)return!1;return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),r=[e.positionId,e.timestamp,e.chainId,e.utxoIdentifier,e.networkFee,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","string","uint256","bytes32"],r)}),(async(e,i,n)=>e.toLowerCase()===r.toLowerCase()||!!o&&t.checkEIP1271(r,i,n,o)),{strictCurrentQuantum:!0})}};function I(t){if(!0===t)return!0;if(!1===t)return!1;const e=globalThis;return!0===e.debugAction||(!0===e.LIT_ACTION_DEBUG||("true"===e.LIT_ACTION_DEBUG||function(){try{if("undefined"==typeof process||!process.env)return!1;const t=process.env.LIT_ACTION_DEBUG,e=process.env.DEBUG;return"1"===t||"true"===t||"1"===e||"true"===e}catch{return!1}}()))}function C(...t){I()&&console.log(...t)}var B=class{stepStart(t,e){}stepEnd(t,e){}log(t,e,...r){}warn(t,e){}error(t,e){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},U=class{constructor(t){this.stepStartTimes=new Map,this.stepDurations=new Map,this.TIMEOUT_MS=3e4,this.actionName=t,this.executionStartTime=Date.now();const e=Date.now(),r=Math.random().toString(36).substring(2,9);this.executionId=`exec_${e}_${r}`,this.logHeader()}logHeader(){console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution started`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`);void 0!==globalThis.litNodeContext?(console.log(`[${this.actionName}] Context: LIT Node`),console.log(`[${this.actionName}] Node: ${globalThis.litNodeContext?.nodeAddress||"unknown"}`)):console.log(`[${this.actionName}] Context: Browser/Test`),console.log(`[${this.actionName}] ========================================`)}stepStart(t,e){this.stepStartTimes.set(t,Date.now());const r=Date.now()-this.executionStartTime,o=this.TIMEOUT_MS-r;console.log(`[Step ${t}] ${e}...`),console.log(`[Step ${t}] Elapsed: ${r}ms | Remaining: ${o}ms`)}stepEnd(t,e=5e3){const r=this.stepStartTimes.get(t);if(!r)return console.warn(`[Step ${t}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const o=Date.now()-r;this.stepDurations.set(t,o),console.log(`[Step ${t}] Duration: ${o}ms`),o>e&&console.warn(`\u26a0\ufe0f [Step ${t}] Took ${o}ms (> ${e}ms threshold)`);const i=Date.now()-this.executionStartTime,n=this.TIMEOUT_MS-i;n<5e3&&console.warn(`\u26a0\ufe0f [Step ${t}] Time remaining: ${n}ms (approaching timeout)`)}log(t,e,...r){r.length>0?console.log(`[Step ${t}] ${e}`,...r):console.log(`[Step ${t}] ${e}`)}warn(t,e){console.warn(`\u26a0\ufe0f [Step ${t}] ${e}`)}error(t,e){console.error(`\u274c [Step ${t}] ${e}`)}getElapsed(){return Date.now()-this.executionStartTime}getRemaining(){return this.TIMEOUT_MS-this.getElapsed()}summary(){const t=Date.now()-this.executionStartTime,e=this.TIMEOUT_MS-t;if(console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution Summary`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`),console.log(`[${this.actionName}] Total time: ${(t/1e3).toFixed(2)}s`),console.log(`[${this.actionName}] Time remaining: ${e}ms`),this.stepDurations.size>0){console.log(`[${this.actionName}] Step breakdown:`);for(const[e,r]of this.stepDurations.entries()){const o=(r/t*100).toFixed(1);console.log(`[${this.actionName}] Step ${e}: ${r}ms (${o}%)`)}}t>2e4&&console.warn(`\u26a0\ufe0f [${this.actionName}] Slow execution: ${(t/1e3).toFixed(2)}s (${e}ms remaining)`),e<5e3&&console.warn(`\u26a0\ufe0f [${this.actionName}] CRITICAL: Only ${e}ms remaining before timeout!`),console.log(`[${this.actionName}] ========================================`)}getExecutionId(){return this.executionId}isDebugEnabled(){return!0}},M=class{static create(t="LIT Action",e){return I(e)?new U(t):new B}};function x(){const t=globalThis,e=t.jsParams;return null==e||"object"!=typeof e||Array.isArray(e)?t:e}function D(t){if(t instanceof Error)return t.message;if("string"==typeof t)return t;try{return JSON.stringify(t)}catch{return String(t)}}function O(t){return t instanceof Error&&"AbortError"===t.name}var k=class{constructor(t){this.config=t,this.timeout=t.timeout||15e3,this.logger=M.create("BitcoinDataProvider")}async getCurrentBlockHeight(){if(this.logger.stepStart("0","getCurrentBlockHeight"),this.config.rpcHelper){const t=await this.config.rpcHelper.getBlockCount();return this.logger.stepEnd("0"),t}const t=new AbortController,e=setTimeout((()=>t.abort()),this.timeout);try{const r=Date.now(),o=await fetch(`${this.config.providerUrl}/blocks/tip/height`,{signal:t.signal,headers:{Accept:"text/plain","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(e),C(`[BitcoinDataProvider] blocks/tip/height ${Date.now()-r}ms (timeout budget ${this.timeout}ms)`),!o.ok)throw new Error(`Failed to fetch block height: ${o.status} ${o.statusText}`);const i=await o.text();if(i.trim().startsWith("<!DOCTYPE")||i.trim().startsWith("<html"))throw new Error("Bitcoin RPC endpoint returned HTML instead of block height. URL may be pointing to web interface instead of API endpoint. Expected: plain text number, Got: HTML document. Check that URL ends with /api/esplora (not root /)");const n=parseInt(i.trim(),10);if(isNaN(n)){const t=i.length>200?i.substring(0,200)+"... (truncated)":i;throw new Error(`Invalid block height response: ${t}`)}return this.logger.stepEnd("0"),n}catch(t){if(clearTimeout(e),this.logger.stepEnd("0"),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOs(t){this.logger.log("0",`Original address: "${t}"`);const e=this.stripNetworkPrefix(t);if(this.logger.log("0",`Cleaned address: "${e}"`),this.config.rpcHelper)return await this.fetchUTXOsFromRPC(e);try{return await this.fetchUTXOsFromProvider(this.config.providerUrl,e)}catch(t){if(this.logger.warn("0",`Primary provider failed: ${D(t)}`),this.config.fallbackProviders&&this.config.fallbackProviders.length>0){const t=[...this.config.fallbackProviders].sort(((t,e)=>t.priority-e.priority));for(const r of t)try{return this.logger.log("0",`Trying fallback: ${r.name} (${r.url})`),await this.fetchUTXOsFromProvider(r.url,e)}catch(t){this.logger.warn("0",`Fallback ${r.name} failed: ${D(t)}`);continue}}throw new Error(`Failed to fetch UTXOs: ${D(t)}`)}}stripNetworkPrefix(t){return t.replace(/^(REGTEST_|TESTNET_|MAINNET_)/,"")}async fetchUTXOsFromRPC(t){if(!this.config.rpcHelper)throw new Error("RPC helper not configured");const e=this.config.rpcWallet||"";return(await this.config.rpcHelper.listUnspent(e,t)).map((t=>({txid:t.txid,vout:t.vout,satoshis:BigInt(Math.round(1e8*t.amount)),confirmations:t.confirmations})))}parseBlockstreamUTXOs(t,e){if(!Array.isArray(t))throw new Error("Invalid Blockstream response format: expected an array");const r=t,o=[];for(const t of r){let r=0;t.status&&"object"==typeof t.status?t.status.confirmed&&(r=void 0!==t.status.block_height&&t.status.block_height>0?e-t.status.block_height+1:1):void 0!==t.confirmations&&(r=t.confirmations);const i=t.txid,n=void 0!==t.vout?t.vout:t.n,a=void 0!==t.value?t.value:t.satoshis;if(void 0===i||void 0===n||void 0===a)throw new Error("Invalid UTXO row: missing txid, vout/n, or value/satoshis");o.push({txid:i,vout:n,satoshis:BigInt(a),confirmations:r})}return o}async fetchUTXOsFromProvider(t,e){this.logger.stepStart("1","fetchUTXOsFromProvider");const r=`${t}/address/${e}/utxos`;this.logger.log("1",`Fetching UTXOs from ${r}`);const o=new AbortController,i=setTimeout((()=>o.abort()),this.timeout);try{const t=Date.now(),[n,a]=await Promise.all([this.getCurrentBlockHeight(),fetch(r,{signal:o.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}})]);if(clearTimeout(i),C(`[BitcoinDataProvider] parallel(tipHeight + GET utxos) wall ${Date.now()-t}ms endpoint tail .../${e.slice(0,12)}.../utxos (timeout budget ${this.timeout}ms)`),this.logger.log("1",`Current block height: ${n}`),!a.ok)throw new Error(`Bitcoin provider error: ${a.status} ${a.statusText}`);const s=await a.text();if(s.trim().startsWith("<!DOCTYPE")||s.trim().startsWith("<html"))throw new Error("Bitcoin RPC endpoint returned HTML instead of JSON. URL may be pointing to web interface instead of API endpoint. Expected: JSON array of UTXOs, Got: HTML document. Check that URL ends with /api/esplora (not root /)");let c;try{c=JSON.parse(s)}catch(t){const e=s.length>200?s.substring(0,200)+"... (truncated)":s;throw new Error(`Failed to parse JSON response: ${D(t)}. Response: ${e}`)}this.logger.log("1",`Raw API Response: ${JSON.stringify(c,null,2)}`);const l=this.parseBlockstreamUTXOs(c,n);return this.logger.stepEnd("1"),l}catch(t){if(clearTimeout(i),this.logger.stepEnd("1"),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOSet(t,e){let r=[],o=null;const i=d,n=h;for(let e=1;e<=i;e++)try{this.logger.log("2",`UTXO query attempt ${e}/${i}...`),r=await this.getUTXOs(t),this.logger.log("2",`UTXO query succeeded on attempt ${e}`),o=null;break}catch(t){o=t instanceof Error?t:new Error(D(t)),this.logger.error("2",`UTXO query failed on attempt ${e}: ${D(t)}`),e<i&&(this.logger.log("2",`Waiting ${n}ms before retry...`),await new Promise((t=>setTimeout(t,n))))}if(o)throw new Error(`Failed to query UTXOs after ${i} attempts: ${o.message}`);const a=r.reduce(((t,e)=>t+e.satoshis),0n),s=r.filter((t=>t.confirmations>=e)).reduce(((t,e)=>t+e.satoshis),0n),c=a-s;return{utxos:r,totalBalance:a,totalUTXOs:r.length,confirmedBalance:s,unconfirmedBalance:c}}async getBalance(t){const e=this.stripNetworkPrefix(t),r=`${this.config.providerUrl}/address/${e}`;this.logger.log("3",`Fetching balance from ${r}`);const o=new AbortController,i=setTimeout((()=>o.abort()),this.timeout);try{const t=Date.now(),e=await fetch(r,{signal:o.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(i),C(`[BitcoinDataProvider] GET address chain_stats ${Date.now()-t}ms (timeout budget ${this.timeout}ms)`),!e.ok)throw new Error(`Bitcoin provider error: ${e.status} ${e.statusText}`);const n=await e.json();if(!n.chain_stats)throw new Error("Invalid response: missing chain_stats");const a=n.chain_stats.funded_txo_sum,s=n.chain_stats.spent_txo_sum;if(void 0===a||void 0===s)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${a}, spent_txo_sum: ${s}`);const c=BigInt(a-s);return this.logger.log("3",`Balance: ${c.toString()} sats`),this.logger.log("3",` - Funded: ${a} sats, Spent: ${s} sats`),c}catch(t){if(clearTimeout(i),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);const r=D(t);throw this.logger.log("3",`Balance fetch failed: ${r}`),new Error(`Failed to fetch Bitcoin balance for address ${e}: ${r}`)}}async getTransaction(t){if(this.config.rpcHelper)try{const e=this.config.rpcWallet||"",r=await this.config.rpcHelper.getTransaction(e,t);return{txid:r.txid,confirmations:r.confirmations||0}}catch(t){const e=D(t),r=null!==t&&"object"==typeof t&&"code"in t&&"number"==typeof t.code?t.code:void 0;if(e.includes("Invalid or non-wallet transaction")||-5===r)return null;throw t}const e=new AbortController,r=setTimeout((()=>e.abort()),this.timeout);try{const o=Date.now(),i=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(r),C(`[BitcoinDataProvider] GET tx/${t.slice(0,10)}\u2026 ${Date.now()-o}ms (timeout budget ${this.timeout}ms)`),!i.ok){if(404===i.status)return null;throw new Error(`Bitcoin provider error: ${i.status} ${i.statusText}`)}const n=await i.json();return n.status?{txid:n.txid??t,confirmations:n.status.confirmed&&n.status.block_height?1:0}:null}catch(t){if(clearTimeout(r),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);if(D(t).includes("404"))return null;throw t}}async getTransactionOutputs(t){if(!this.config.providerUrl)return null;const e=new AbortController,r=setTimeout((()=>e.abort()),this.timeout);try{const o=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(r),!o.ok){if(404===o.status)return null;throw new Error(`Bitcoin provider error: ${o.status} ${o.statusText}`)}const i=await o.json();return i.vout&&Array.isArray(i.vout)?i.vout.map(((t,e)=>({index:e,scriptpubkey:t.scriptpubkey,scriptpubkey_address:t.scriptpubkey_address,value:t.value??0}))):null}catch(t){if(clearTimeout(r),O(t))throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}},N=class t{constructor(e,r,o){if(r&&Array.isArray(r))this.sources=r;else{if(!Array.isArray(e))throw new Error("Price oracle requires a priceProviders array \u2014 defaults and fallbacks have been removed. Pass 3 distinct {name, apiKey} entries from the supported set: "+t.ACTIVE_PROVIDER_NAMES.join(", "));this.sources=this.buildSources(e)}this.sources.sort(((t,e)=>t.priority-e.priority)),this.validateProviderCount()}validateProviderCount(){if(this.sources.length<t.MIN_DISTINCT_PROVIDERS)throw new Error(`Price oracle requires at least ${t.MIN_DISTINCT_PROVIDERS} eligible providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported active providers: ${t.ACTIVE_PROVIDER_NAMES.join(", ")}`)}buildSources(e){if(0===e.length)throw new Error("priceProviders must be a non-empty array of 3 distinct providers, each with an apiKey.");const r=[],o=new Set;let i=1;for(const n of e){if(!n||"string"!=typeof n.name)throw new Error("priceProviders entries must be {name, apiKey, apiSecret?} objects");const e=n.name.toLowerCase();if(!t.ACTIVE_PROVIDER_NAMES.includes(e))throw new Error(`Unsupported price provider "${n.name}". Supported: ${t.ACTIVE_PROVIDER_NAMES.join(", ")}`);if(o.has(e))throw new Error(`Duplicate price provider "${e}" \u2014 the oracle requires 3 DISTINCT providers so a single upstream failure or manipulation cannot compromise consensus. Use 3 different provider names from: ${t.ACTIVE_PROVIDER_NAMES.join(", ")}`);switch(o.add(e),e){case"binance":if(!n.apiKey)throw new Error("Binance provider requires an apiKey");r.push(this.createBinanceSource(n.apiKey,i++));break;case"coinbase":if(!n.apiKey)throw new Error("Coinbase provider requires an apiKey");r.push(this.createCoinbaseSource(n.apiKey,i++));break;case"cryptocompare":if(!n.apiKey)throw new Error("CryptoCompare provider requires an apiKey");r.push(this.createCryptoCompareSource(n.apiKey,i++));break;case"coinmarketcap":if(!n.apiKey)throw new Error("CoinMarketCap provider requires an apiKey");r.push(this.createCoinMarketCapSource(n.apiKey,i++));break;case"coingecko":if(!n.apiKey)throw new Error("CoinGecko provider requires an apiKey (Demo or Pro). Unauthenticated CoinGecko access is not accepted \u2014 the free tier is rate-limited and edge-cached, which breaks median consensus under load.");r.push(this.createCoinGeckoSource(n.apiKey,i++));break;default:throw new Error(`Unhandled provider: ${e}`)}}if(r.length<t.MIN_DISTINCT_PROVIDERS)throw new Error(`Insufficient price providers: need at least ${t.MIN_DISTINCT_PROVIDERS} distinct entries, got ${r.length}.`);return this.selectSampledSources(r)}createCryptoCompareSource(t,e){return{name:"CryptoCompare",fetchPrice:async()=>{const e=new URL("https://min-api.cryptocompare.com/data/price");e.searchParams.set("fsym","BTC"),e.searchParams.set("tsyms","USDT"),e.searchParams.set("api_key",t);const r=await(async t=>{const e=new AbortController,r=setTimeout((()=>e.abort()),5e3);try{const o=await fetch(t,{signal:e.signal,headers:{Accept:"application/json"}});if(clearTimeout(r),!o.ok)throw new Error(`HTTP ${o.status} ${o.statusText}`);return await o.json()}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error("Request timeout after 5000ms");throw t}})(e.toString()),o=Number(r?.USDT);if(!Number.isFinite(o)||o<=0)throw new Error("Invalid CryptoCompare price payload");return o},priority:e}}createFetchJson(){return async(t,e)=>{const r=new AbortController,o=setTimeout((()=>r.abort()),5e3);try{const o=await fetch(t,{headers:{Accept:"application/json",...e||{}},signal:r.signal});if(!o.ok)throw new Error(`HTTP ${o.status} ${o.statusText}`);return await o.json()}finally{clearTimeout(o)}}}getSelectionSeed(){const t="undefined"!=typeof process?process.env.PRICE_PROVIDER_SELECTION_SEED:void 0,e=t?Number(t):NaN;return Number.isFinite(e)?e:("undefined"!=typeof process,0,Math.floor(Date.now()+1e6*Math.random()))}selectSampledSources(t){const e=[...t];let r=this.getSelectionSeed()>>>0;for(let t=e.length-1;t>0;t--){const o=Math.floor((r=1664525*r+1013904223>>>0,r/4294967296*(t+1)));[e[t],e[o]]=[e[o],e[t]]}return e.slice(0,3).map(((t,e)=>({...t,priority:e+1})))}createCoinGeckoSource(t,e){if(!t)throw new Error("createCoinGeckoSource requires an apiKey");const r=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const e=t.startsWith("pro:"),o=e?t.slice(4):t,i=e?{"x-cg-pro-api-key":o}:{"x-cg-demo-api-key":o},n=new URL("https://api.coingecko.com/api/v3/simple/price");n.searchParams.set("ids","bitcoin"),n.searchParams.set("vs_currencies","usd");const a=await r(n.toString(),i),s=Number(a?.bitcoin?.usd);if(!Number.isFinite(s)||s<=0)throw new Error("Invalid CoinGecko price payload");return s},priority:e}}createBinanceSource(t,e){if(!t)throw new Error("createBinanceSource requires an apiKey");const r=this.createFetchJson();return{name:"Binance",fetchPrice:async()=>{const e=new URL("https://api.binance.com/api/v3/ticker/price");e.searchParams.set("symbol","BTCUSDT");const o=await r(e.toString(),{"X-MBX-APIKEY":t}),i=Number(o?.price);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid Binance price payload");return i},priority:e}}createCoinbaseSource(t,e){if(!t)throw new Error("createCoinbaseSource requires an apiKey");const r=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const e=await r("https://api.coinbase.com/v2/prices/BTC-USDT/spot",{"CB-ACCESS-KEY":t}),o=Number(e?.data?.amount);if(!Number.isFinite(o)||o<=0)throw new Error("Invalid Coinbase price payload");return o},priority:e}}createCoinMarketCapSource(t,e){const r=this.createFetchJson();return{name:"CoinMarketCap",fetchPrice:async()=>{const e=new URL("https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest");e.searchParams.set("symbol","BTC"),e.searchParams.set("convert","USDT");const o=await r(e.toString(),{"X-CMC_PRO_API_KEY":t}),i=Number(o?.data?.BTC?.[0]?.quote?.USDT?.price??o?.data?.BTC?.quote?.USDT?.price);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CoinMarketCap price payload");return i},priority:e}}async getBTCPrice(){const t=Date.now();if(3!==this.sources.length)throw new Error(`Price oracle requires exactly 3 providers. Currently configured: ${this.sources.length}`);const e=new Promise(((t,e)=>{setTimeout((()=>{e(new Error("Price oracle global timeout after 8000ms"))}),8e3)})),r=Promise.all(this.sources.map((async t=>{const e=Date.now(),r=await t.fetchPrice();return console.log(`[Price Oracle] \u2705 [+${Date.now()-e}ms] ${t.name}: $${r.toLocaleString()}`),r}))),o=[...await Promise.race([r,e])].sort(((t,e)=>t-e)),i=o[1];if((o[2]-o[0])/o[0]>.01){const t=this.sources.map((t=>t.name)).join(", "),e=o.map((t=>`$${t.toLocaleString()}`)).join(", ");throw new Error(`Price consensus failed: prices not within 1% tolerance. Providers: [${t}]. Prices: [${e}]`)}const n=Math.round(100*i),a=1000000n*BigInt(n),s=Date.now()-t;return console.log(`[Price Oracle] Median price: $${i.toLocaleString()} \u2014 total time: ${s}ms`),console.log(`[Price Oracle] Price with 8 decimals: ${a}`),a}async getBTCPriceConsensus(){console.log("[Price Oracle] Fetching BTC price with consensus...");const t=(await Promise.allSettled(this.sources.map((async t=>{const e=await t.fetchPrice();return{source:t.name,price:e}})))).filter((t=>"fulfilled"===t.status)).map((t=>t.value));if(0===t.length)throw new Error("No price sources returned data");console.log(`[Price Oracle] Got prices from ${t.length}/${this.sources.length} sources:`),t.forEach((t=>{console.log(` ${t.source}: $${t.price.toLocaleString()}`)}));const e=t.map((t=>t.price));e.sort(((t,e)=>t-e));const r=e[Math.floor(e.length/2)],o=e[0],i=e[e.length-1]/o,n=t.filter((t=>Math.abs(t.price-r)/r<=.02));if(i>1.05&&n.length===t.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(i-1)).toFixed(1)}% spread)`);if(n.length<t.length){const e=t.filter((t=>!n.find((e=>e.source===t.source))));if(console.log(`[Price Oracle] \u26a0\ufe0f Detected ${e.length} outlier(s):`),e.forEach((t=>{const e=Math.abs(t.price-r)/r;console.log(` ${t.source}: $${t.price.toLocaleString()} (${(100*e).toFixed(1)}% deviation)`)})),n.length<2)throw new Error("Price consensus failed: insufficient valid sources after outlier removal");console.log(`[Price Oracle] \u2705 Outliers filtered, continuing with ${n.length} valid sources`);const o=n.map((t=>t.price));o.sort(((t,e)=>t-e));const i=o[Math.floor(o.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${i.toLocaleString()}`);const a=Math.round(100*i);return 1000000n*BigInt(a)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${r.toLocaleString()}`);const a=Math.round(100*r);return 1000000n*BigInt(a)}};N.ACTIVE_PROVIDER_NAMES=["cryptocompare","coinbase","binance","coinmarketcap","coingecko"],N.MIN_DISTINCT_PROVIDERS=3;var R=N,q=class{constructor(t){this.config=t,this.bitcoinProvider=t.bitcoinProvider}async calculateBalance(t,e){const r=await this.bitcoinProvider.getBalance(e),o=await this.getAuthorizedSpendsFromContract(t),i=o.reduce(((t,e)=>t+e.satoshis),0n),n=r>i?r-i:0n;return{totalUTXOs:[],totalBalance:r,authorizedUTXOs:o,authorizedBalance:i,authorizedSpendsHash:this.computeAuthorizedSpendsHash(t,o),availableUTXOs:[],availableBalance:n,vaultAddress:e,positionId:t,timestamp:Date.now()}}async calculateTrustedBalance(t,e,r){if(r)return await this.calculateBalance(t,e);const o=this.config.minConfirmations||6,i=await this.bitcoinProvider.getUTXOSet(e,o),n=await this.getAuthorizedSpendsFromContract(t);for(const t of n){if(i.utxos.some((e=>e.txid===t.txid&&e.vout===t.vout)))throw new Error(`Authorized UTXO ${t.txid}:${t.vout} not yet spent. Transaction was authorized in smart contract but not signed and broadcasted to Bitcoin network. Complete the transaction by signing and broadcasting it.`)}const a=n.reduce(((t,e)=>t+e.satoshis),0n),s=i.utxos.filter((t=>!this.isUTXOAuthorized(t,n))),c=s.reduce(((t,e)=>t+e.satoshis),0n),l=this.computeAuthorizedSpendsHash(t,n);return{totalUTXOs:i.utxos,totalBalance:i.totalBalance,authorizedUTXOs:n,authorizedBalance:a,authorizedSpendsHash:l,availableUTXOs:s,availableBalance:c,vaultAddress:e,positionId:t,timestamp:Date.now()}}async getTrustedBalance(t,e){return(await this.calculateTrustedBalance(t,e)).availableBalance}async getAvailableUTXOs(t,e){return(await this.calculateTrustedBalance(t,e)).availableUTXOs}async isUTXOAvailable(t,e,r,o){return(await this.getAvailableUTXOs(t,e)).some((t=>t.txid===r&&t.vout===o))}isUTXOAuthorized(t,e){return e.some((e=>e.txid===t.txid&&e.vout===t.vout))}async getAuthorizedSpendsFromContract(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let r;r=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const o=new ethers.providers.StaticJsonRpcProvider({url:r,timeout:g},this.config.chainId),i=new ethers.Contract(this.config.contractAddress,[{inputs:[{internalType:"bytes32",name:"positionId",type:"bytes32"}],name:"getAuthorizedSpends",outputs:[{components:[{internalType:"string",name:"txid",type:"string"},{internalType:"uint32",name:"vout",type:"uint32"},{internalType:"uint256",name:"satoshis",type:"uint256"},{internalType:"string",name:"targetAddress",type:"string"},{internalType:"uint256",name:"targetAmount",type:"uint256"},{internalType:"uint256",name:"authorizedAt",type:"uint256"}],internalType:"struct LoanOperationsManager.AuthorizedSpend[]",name:"",type:"tuple[]"}],stateMutability:"view",type:"function"}],o);return(await i.getAuthorizedSpends(e)).map((e=>({txid:e.txid,vout:Number(e.vout),satoshis:BigInt(e.satoshis.toString()),positionId:t,targetAddress:e.targetAddress,targetAmount:BigInt(e.targetAmount.toString()),timestamp:Number(e.authorizedAt)})))}computeAuthorizedSpendsHash(t,e){const r=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;if(0===e.length)return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,[]]));const o=e.map((t=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.txid)),r=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.targetAddress));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","uint32","uint256","bytes32","uint256","uint256"],[e,t.vout,t.satoshis.toString(),r,t.targetAmount.toString(),t.timestamp]))}));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,o]))}};function L(t,e){if(0===t)return{termDurationDays:0,termLengthDays:30*e,isExpired:!1,daysUntilExpiry:30*e,daysIntoGracePeriod:0};const r=30*e,o=Math.floor(Date.now()/1e3)-t,i=Math.floor(o/86400);return{termDurationDays:i,termLengthDays:r,isExpired:i>r,daysUntilExpiry:Math.max(0,r-i),daysIntoGracePeriod:Math.max(0,i-r)}}function F(t,e,r,o){if(!t)return r??13e3;const i=o??n,a=Math.min(e,30);return n+a*a*(i-n)/900}function _(t,e,r){const o=t*e/(100000000n*a);if(0n===r)return{collateralValueUsd:o,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:o,collateralRatioBps:Number(o*s*10000n/r)}}function z(t,e){if(t<0n)throw new Error("BTC amount cannot be negative");if(e<=0n)throw new Error("BTC price must be positive");return t*e/a}function X(t,e){if(t<0n)throw new Error("Collateral value cannot be negative");if(e<=0n)throw new Error("Debt must be positive to calculate ratio");const r=Number(10000n*(10000000000n*t)/e);if(r<0)throw new Error(`Collateral ratio out of bounds: ${r} bps (${r/100}%)`);return!isFinite(r)||r>Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:r}function V(t){return t/100}var H=class{constructor(t){this.termManagerAddress=t.termManagerAddress,this.loanOpsManagerAddress=t.loanOpsManagerAddress,this.liquidationManagerAddress=t.liquidationManagerAddress,this.chain=t.chain,this.chainId=t.chainId,this.rpcUrl=t.rpcUrl}async getLiquidationThreshold(){try{let t;t=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const e=new ethers.providers.StaticJsonRpcProvider({url:t,timeout:g},{name:"any",chainId:this.chainId}),r=[{inputs:[],name:"liquidationThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],o=new ethers.Contract(this.loanOpsManagerAddress,r,e),i=await o.liquidationThreshold();return Number(i.toString())}catch(t){throw console.error("[ProtocolParameters] Error fetching liquidation threshold:",t.message),new Error(`Failed to fetch liquidation threshold: ${t.message}`)}}async getTermFees(t){try{let e;e=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const r=new ethers.providers.StaticJsonRpcProvider({url:e,timeout:g},{name:"any",chainId:this.chainId}),o=[{inputs:[{internalType:"uint256",name:"_termMonths",type:"uint256"}],name:"getTermFees",outputs:[{internalType:"uint88",name:"originationFee",type:"uint88"},{internalType:"uint88",name:"extensionFee",type:"uint88"}],stateMutability:"view",type:"function"}],i=new ethers.Contract(this.termManagerAddress,o,r),n=await i.getTermFees(t);return{originationFeeBps:Number(n.originationFee?.toString?.()??n[0]?.toString?.()??n[0]),extensionFeeBps:Number(n.extensionFee?.toString?.()??n[1]?.toString?.()??n[1])}}catch(t){throw console.error("[ProtocolParameters] Error fetching term fees:",t.message),new Error(`Failed to fetch term fees: ${t.message}`)}}async getAuthorizedSpendsHash(t){try{const e=this.rpcUrl||await Lit.Actions.getRpcUrl({chain:this.chain}),r=new ethers.providers.StaticJsonRpcProvider({url:e,timeout:g},{name:"any",chainId:this.chainId}),o=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`,i=new ethers.Contract(this.loanOpsManagerAddress,[{inputs:[{name:"positionId",type:"bytes32"}],name:"getAuthorizedSpendsHash",outputs:[{name:"",type:"bytes32"}],stateMutability:"view",type:"function"}],r);return await i.getAuthorizedSpendsHash(o)}catch(t){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",t.message),new Error(`Failed to fetch authorized spends hash: ${t.message}`)}}async getMaxEscalatedThreshold(){try{let t;t=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const e=new ethers.providers.StaticJsonRpcProvider({url:t,timeout:g},{name:"any",chainId:this.chainId}),r=[{inputs:[],name:"maxEscalatedThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],o=new ethers.Contract(this.liquidationManagerAddress,r,e),i=await o.maxEscalatedThreshold();return Number(i.toString())}catch(t){throw console.error("[ProtocolParameters] Error fetching max escalated threshold:",t.message),new Error(`Failed to fetch max escalated threshold: ${t.message}`)}}};function W(t){return new H(t)}var j=class{constructor(t){this.config=t}async getVaultSnapshot(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const r=!0===this.config.debugOverrides?.useStubbedBtcData,[o,i]=r?this.buildStubbedBtcSnapshotInputs(e):await Promise.all([this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress),this.config.priceOracle.getBTCPriceConsensus()]),n=W({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,liquidationManagerAddress:this.config.liquidationManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),a=this.config.termOverride??e.selectedTerm,[s,c,l]=await Promise.all([n.getLiquidationThreshold(),n.getMaxEscalatedThreshold(),n.getTermFees(a)]),u=L(e.termStartTimestamp,e.selectedTerm),d=F(u.isExpired,u.daysIntoGracePeriod,s,c),h=_(o.availableBalance,i,e.ucdDebt),p=h.collateralRatioBps<d,g=h.collateralRatioBps-d;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,previousExpiryAt:e.previousExpiryAt,totalTerm:e.totalTerm,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:i,collateralValueUsd:h.collateralValueUsd,collateralRatioBps:h.collateralRatioBps,termDurationDays:u.termDurationDays,termLengthDays:u.termLengthDays,isExpired:u.isExpired,daysUntilExpiry:u.daysUntilExpiry,daysIntoGracePeriod:u.daysIntoGracePeriod,currentLiquidationThreshold:d,isLiquidatable:p,marginToLiquidationBps:g,liquidationThresholdBps:s,originationFeeBps:l.originationFeeBps,extensionFeeBps:l.extensionFeeBps,timestamp:Date.now()}}async getVaultSnapshotFast(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const r=!0===this.config.debugOverrides?.useStubbedBtcData,[o,i]=r?this.buildStubbedBtcSnapshotInputs(e):await Promise.all([this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress,!0),this.config.priceOracle.getBTCPriceConsensus()]),n=W({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,liquidationManagerAddress:this.config.liquidationManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),a=this.config.termOverride??e.selectedTerm,[s,c,l]=await Promise.all([n.getLiquidationThreshold(),n.getMaxEscalatedThreshold(),n.getTermFees(a)]),u=L(e.termStartTimestamp,e.selectedTerm),d=F(u.isExpired,u.daysIntoGracePeriod,s,c),h=_(o.availableBalance,i,e.ucdDebt),p=h.collateralRatioBps<d,g=h.collateralRatioBps-d;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,previousExpiryAt:e.previousExpiryAt,totalTerm:e.totalTerm,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:i,collateralValueUsd:h.collateralValueUsd,collateralRatioBps:h.collateralRatioBps,termDurationDays:u.termDurationDays,termLengthDays:u.termLengthDays,isExpired:u.isExpired,daysUntilExpiry:u.daysUntilExpiry,daysIntoGracePeriod:u.daysIntoGracePeriod,currentLiquidationThreshold:d,isLiquidatable:p,marginToLiquidationBps:g,liquidationThresholdBps:s,originationFeeBps:l.originationFeeBps,extensionFeeBps:l.extensionFeeBps,timestamp:Date.now()}}buildStubbedBtcSnapshotInputs(t){const e=BigInt(this.config.debugOverrides?.stubbedAvailableBtcSats??"10000000"),r=BigInt(this.config.debugOverrides?.stubbedBtcPriceUsd??"5000000000000");return[{totalUTXOs:[],totalBalance:e,authorizedUTXOs:[],authorizedBalance:0n,authorizedSpendsHash:this.config.debugOverrides?.stubbedAuthorizedSpendsHash??ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[t.positionId,[]])),availableUTXOs:[],availableBalance:e,vaultAddress:t.vaultAddress,positionId:t.positionId,timestamp:Date.now()},r]}async queryPositionState(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let r;r=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const o=new ethers.providers.StaticJsonRpcProvider({url:r,timeout:g},this.config.chainId),i=new ethers.Contract(this.config.contractAddress,[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],o),n=await i.core(),a=new ethers.Contract(n,[{inputs:[{internalType:"bytes32",name:"positionId",type:"bytes32"}],name:"getPositionDetails",outputs:[{components:[{internalType:"bytes32",name:"positionId",type:"bytes32"},{internalType:"bytes32",name:"pkpId",type:"bytes32"},{internalType:"uint256",name:"ucdDebt",type:"uint256"},{internalType:"string",name:"vaultAddress",type:"string"},{internalType:"address",name:"borrower",type:"address"},{internalType:"uint40",name:"createdAt",type:"uint40"},{internalType:"uint40",name:"lastUpdated",type:"uint40"},{internalType:"uint16",name:"selectedTerm",type:"uint16"},{internalType:"uint40",name:"expiryAt",type:"uint40"},{internalType:"enum LoanStatusLib.LoanStatus",name:"status",type:"uint8"},{internalType:"uint40",name:"previousExpiryAt",type:"uint40"},{internalType:"uint16",name:"totalTerm",type:"uint16"}],internalType:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],o),s=await a.getPositionDetails(e),c=Number(s.status);if(c<0||c>7)throw console.error("[VaultSnapshot] INVALID STATUS - ABI decoding error detected"),console.error(" Position ID:",e),console.error(" Contract:",this.config.contractAddress),console.error(" Status decoded:",c,"(expected 0-7)"),console.error(" Full position:",JSON.stringify(s,null,2)),new Error(`Invalid position status decoded from contract: ${c} (expected 0-7). This indicates an ABI decoding issue. Position: ${e}`);const l=Number(s.expiryAt),u=Number(s.selectedTerm),d=Number(s.totalTerm),h=Number(s.previousExpiryAt),p=(w=u,0===(m=l)?0:m-30*w*86400);var m,w;return{positionId:s.positionId,pkpId:s.pkpId,borrower:s.borrower,vaultAddress:this.parseVaultAddress(s.vaultAddress,S(this.config.chain)),ucdDebt:BigInt(s.ucdDebt.toString()),termStartTimestamp:p,selectedTerm:u,status:c,expiryAt:l,previousExpiryAt:h,totalTerm:d}}parseVaultAddress(t,e){try{const r=JSON.parse(t);if("object"==typeof r&&null!==r){if("sepolia"===e.toLowerCase())return r.regtest||r.testnet||r.mainnet;const t=function(t){return b[t]}(e);return"testnet"===t&&(r.testnet||r.regtest)||r.mainnet}}catch(t){}return t}async isLiquidatable(t){return(await this.getVaultSnapshot(t)).isLiquidatable}async getCollateralRatio(t){return(await this.getVaultSnapshot(t)).collateralRatioBps}async getAvailableBalance(t){return(await this.getVaultSnapshot(t)).availableBTCSats}async hasSufficientCollateral(t,e,r){const o=await this.getVaultSnapshot(t);return function(t,e,r,o){const i=e+r;return 0n===i||Number(t*s*10000n/i)>=o}(o.collateralValueUsd,o.ucdDebt,e,r)}};var G="function getProtocolPauseStatus() view returns (tuple(bool positionManagerPaused, bool positionManagerLiquidationsPaused, bool positionManagerCorePaused, bool loanOperationsPaused, bool collateralManagerPaused, bool liquidationManagerPaused, bool circuitBreakerPaused, bool ucdControllerPaused, bool ucdTokenPaused, bool simplePsmPaused))",K={mintUcd:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],withdrawBtc:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],processPayment:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused"],extendPosition:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],liquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],adminLiquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],btcTransactionSign:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused"]};function J(t){return{positionManagerPaused:t.positionManagerPaused,positionManagerLiquidationsPaused:t.positionManagerLiquidationsPaused,positionManagerCorePaused:t.positionManagerCorePaused,loanOperationsPaused:t.loanOperationsPaused,collateralManagerPaused:t.collateralManagerPaused,liquidationManagerPaused:t.liquidationManagerPaused,circuitBreakerPaused:t.circuitBreakerPaused,ucdControllerPaused:t.ucdControllerPaused,ucdTokenPaused:t.ucdTokenPaused,simplePsmPaused:t.simplePsmPaused}}var Q=new ethers.utils.Interface(["function views() view returns (address)","function paused() view returns (bool)","function liquidationPaused() view returns (bool)","function simplePsm() view returns (address)"]),Y=new ethers.utils.Interface(["function core() view returns (address)","function loanOps() view returns (address)","function collateralManager() view returns (address)","function liquidationManager() view returns (address)","function circuitBreaker() view returns (address)"]),Z=new ethers.utils.Interface(["function getUcdController() view returns (address)","function ucdToken() view returns (address)"]),tt=new ethers.utils.Interface(["function paused() view returns (bool)"]);async function et(t,e,r){return t.call({to:e,data:r})}async function rt(t,e){const r=new ethers.utils.Interface([G]).encodeFunctionData("getProtocolPauseStatus",[]);let o;try{return function(t){const e=new ethers.utils.Interface([G]).decodeFunctionResult("getProtocolPauseStatus",t)[0];return Array.isArray(e)?J({positionManagerPaused:e[0],positionManagerLiquidationsPaused:e[1],positionManagerCorePaused:e[2],loanOperationsPaused:e[3],collateralManagerPaused:e[4],liquidationManagerPaused:e[5],circuitBreakerPaused:e[6],ucdControllerPaused:e[7],ucdTokenPaused:e[8],simplePsmPaused:e[9]}):J(e)}(await t.call({to:e,data:r}))}catch(t){o=t}try{return await async function(t,e){const r=await et(t,e,Q.encodeFunctionData("views",[])),[o]=Q.decodeFunctionResult("views",r),[i,n,a,s,c,l,u,d]=await Promise.all([et(t,e,Q.encodeFunctionData("paused",[])),et(t,e,Q.encodeFunctionData("liquidationPaused",[])),et(t,e,Q.encodeFunctionData("simplePsm",[])),et(t,o,Y.encodeFunctionData("core",[])),et(t,o,Y.encodeFunctionData("loanOps",[])),et(t,o,Y.encodeFunctionData("collateralManager",[])),et(t,o,Y.encodeFunctionData("liquidationManager",[])),et(t,o,Y.encodeFunctionData("circuitBreaker",[]))]),h=Q.decodeFunctionResult("paused",i)[0],p=Q.decodeFunctionResult("liquidationPaused",n)[0],g=Q.decodeFunctionResult("simplePsm",a)[0],m=Y.decodeFunctionResult("core",s)[0],w=Y.decodeFunctionResult("loanOps",c)[0],f=Y.decodeFunctionResult("collateralManager",l)[0],y=Y.decodeFunctionResult("liquidationManager",u)[0],b=Y.decodeFunctionResult("circuitBreaker",d)[0],T=await et(t,w,Z.encodeFunctionData("getUcdController",[])),v=await et(t,w,Z.encodeFunctionData("ucdToken",[])),S=Z.decodeFunctionResult("getUcdController",T)[0],P=Z.decodeFunctionResult("ucdToken",v)[0],E=async e=>{if(!e||e===ethers.constants.AddressZero)return!1;try{const r=await et(t,e,tt.encodeFunctionData("paused",[]));return tt.decodeFunctionResult("paused",r)[0]}catch{return!1}},[$,A,I,C,B,U,M,x]=await Promise.all([E(m),E(w),E(f),E(y),E(b),E(S),E(P),E(g)]);return J({positionManagerPaused:h,positionManagerLiquidationsPaused:p,positionManagerCorePaused:$,loanOperationsPaused:A,collateralManagerPaused:I,liquidationManagerPaused:C,circuitBreakerPaused:B,ucdControllerPaused:U,ucdTokenPaused:M,simplePsmPaused:x})}(t,e)}catch(t){const e=o instanceof Error?o.message:String(o),r=t instanceof Error?t.message:String(t);throw new Error(`getProtocolPauseStatus failed (aggregate: ${e}; decomposed: ${r})`)}}function ot(t){if("string"!=typeof t)throw new Error("positionId must be a string");const e=t.trim(),r=/^0x/i.test(e)?e.slice(2):e;if(0===r.length)throw new Error("positionId is empty");if(!/^[0-9a-fA-F]+$/.test(r))throw new Error("positionId must be hexadecimal (bytes32)");if(r.length>64)throw new Error("positionId exceeds 32 bytes");return"0x"+r.padStart(64,"0").toLowerCase()}var it={hardhat:{chain:"hardhat",chainId:v.hardhat,bitcoinNetwork:b.hardhat,allowArbitraryBitcoinProvider:!0,allowArbitraryEvmRpc:!0,minBitcoinConfirmations:1},sepolia:{chain:"sepolia",chainId:v.sepolia,bitcoinNetwork:b.sepolia,allowArbitraryBitcoinProvider:!1,allowArbitraryEvmRpc:!1,minBitcoinConfirmations:1},ethereum:{chain:"ethereum",chainId:v.ethereum,bitcoinNetwork:b.ethereum,allowArbitraryBitcoinProvider:!1,allowArbitraryEvmRpc:!1,minBitcoinConfirmations:6}};var nt,at=[{inputs:[{internalType:"uint256",name:"chainId",type:"uint256"}],name:"getProviders",outputs:[{internalType:"bytes32[]",name:"ids",type:"bytes32[]"},{components:[{internalType:"bytes",name:"ciphertext",type:"bytes"},{internalType:"bytes32",name:"pkpId",type:"bytes32"},{internalType:"uint64",name:"addedAt",type:"uint64"}],internalType:"struct BitcoinProviderRegistry.ProviderEntry[]",name:"entries",type:"tuple[]"}],stateMutability:"view",type:"function"}];async function st(t,e){const r=66===e.length?"0x"+e.slice(-40):e;try{const e=await Lit.Actions.Decrypt({pkpId:r,ciphertext:t}),o="string"==typeof e?e:String(e??"");if(!o)throw new Error("Decrypt returned empty plaintext");return o}catch(t){throw new Error(`Failed to decrypt Bitcoin provider URL (pkpId=${e}): ${t?.message??String(t)}`)}}async function ct(t){const{policy:e,bitcoinProviderUrl:r,ethersProvider:o,registryAddress:i}=t;if(!r||"string"!=typeof r)throw new Error("resolveBitcoinProviderForPolicy: bitcoinProviderUrl is required");if(e.allowArbitraryBitcoinProvider)return{name:"Custom Provider",url:r,minConfirmations:e.minBitcoinConfirmations,network:e.bitcoinNetwork};if(!i)throw new Error(`Bitcoin provider registry address is required on chain "${e.chain}" \u2014 set contractAddresses.BitcoinProviderRegistry. Hardcoded allowlists have been removed (see audit C-1/H-6 fix).`);const n=await async function(t,e,r){if(!r||"string"!=typeof r)throw new Error("fetchBitcoinProviderEntries: registryAddress (BitcoinProviderRegistry) is required");if(!e)throw new Error("fetchBitcoinProviderEntries: ethers provider is required");const o=new ethers.Contract(r,at,e),[i,n]=await o.getProviders(t),a=[];for(let t=0;t<i.length;t++)a.push({providerId:i[t],ciphertext:n[t].ciphertext,pkpId:n[t].pkpId,addedAt:Number(n[t].addedAt)});return a}(e.chainId,o,i);if(0===n.length)throw new Error(`No Bitcoin providers registered for chainId=${e.chainId} in ${i}. Admin must addProvider(...) via AdminModule before LIT Actions can sign.`);const a=await async function(t,e){if(!e)throw new Error("matchAllowedProviderUrl: candidateUrl is required");for(const r of t)if(await st(r.ciphertext,r.pkpId)===e)return r.providerId;return null}(n,r);if(!a)throw new Error(`Bitcoin provider URL not in any registered encrypted slot for chainId=${e.chainId}`);return{name:`Registry Provider ${a.slice(0,10)}`,url:r,minConfirmations:e.minBitcoinConfirmations,network:e.bitcoinNetwork}}function lt(t){if(/^(bc1|tb1|bcrt1)[0-9a-zA-Z]{11,87}$/.test(t)){const e=function(t){const e="qpzry9x8gf2tvdw0s3jn54khce6mua7l",r=t.lastIndexOf("1");if(r<1)throw new Error("No separator character for bech32");t.substring(0,r).toLowerCase();const o=t.substring(r+1).toLowerCase(),i=[];for(let t=0;t<o.length;t++){const r=e.indexOf(o[t]);if(-1===r)throw new Error(`Invalid bech32 character: ${o[t]}`);i.push(r)}const n=i.slice(0,-6),a=(n[0],function(t,e,r,o){let i=0,n=0;const a=[],s=(1<<r)-1;for(let o=0;o<t.length;o++){const c=t[o];if(c<0||c>>e)throw new Error("Invalid data for bit conversion");for(i=i<<e|c,n+=e;n>=r;)n-=r,a.push(i>>n&s)}if(o)n>0&&a.push(i<<r-n&s);else if(n>=e||i<<r-n&s)throw new Error("Invalid padding in bit conversion");return a}(n.slice(1),5,8,!1));return{witnessProgram:a.map((t=>t.toString(16).padStart(2,"0"))).join("")}}(t);return"0014"+e.witnessProgram}{const e=function(t){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";let r=0n;for(let o=0;o<t.length;o++){const i=e.indexOf(t[o]);if(i<0)throw new Error(`Invalid Base58 character: ${t[o]}`);r=58n*r+BigInt(i)}let o=r.toString(16);o.length%2&&(o="0"+o);for(let e=0;e<t.length&&"1"===t[e];e++)o="00"+o;return o}(t);return"76a914"+e.slice(2,e.length-8)+"88ac"}}async function main(t){for(const[e,r]of Object.entries(t))globalThis[e]=r;const e=M.create("BTC Withdrawal Validator");console.log("[Step 0] Validating configuration..."),e.stepStart("0","Validate configuration parameters");const r=globalThis.chain,o=globalThis.bitcoinProviderUrl;if(!r)throw new Error('Missing required parameter: "chain". Must be "sepolia" or "ethereum"');if(!o)throw new Error('Missing required parameter: "bitcoinProviderUrl". Must be an approved Bitcoin RPC provider URL');const i=function(t){const e=S(t),r=it[e];if(!r)throw new Error(`No chain policy registered for "${t}"`);return r}(r),n=i.chain,a=i.bitcoinNetwork;let s;if(console.log(` \u2705 Chain: ${r} (${n})`),console.log(` \u2705 Bitcoin Network: ${a}`),!globalThis.contractAddresses||"object"!=typeof globalThis.contractAddresses)throw new Error('Required "contractAddresses" object with: PositionManager, LoanOperationsManagerModule, TermManagerModule');const p=globalThis.contractAddresses.PositionManager,g=globalThis.contractAddresses.LoanOperationsManagerModule,y=globalThis.contractAddresses.TermManagerModule,b=globalThis.contractAddresses.UCDController,v=globalThis.contractAddresses.LITActionValidator,E=globalThis.contractAddresses.LiquidationManagerModule??globalThis.contractAddresses.LiquidationManager??"",$=globalThis.contractAddresses.BTCSpendAuthorizer??"";if(!(p&&g&&y&&b&&E))throw new Error("All contract addresses are required: PositionManager, LoanOperationsManagerModule, TermManagerModule, UCDController, and LiquidationManager or LiquidationManagerModule");console.log(" \u2705 Contract addresses loaded and validated"),console.log(` PositionManager: ${p}`),console.log(` LoanOpsManager: ${g}`),e.stepEnd("0"),console.log("[Step 0.5] Critical system health checks..."),e.stepStart("0.5","Critical system health checks"),e.warn("0.5","System health checks temporarily disabled (SystemHealthModule not available)"),e.log("0.5","\u2705 System health checks complete (skipped)"),e.stepEnd("0.5");const I=globalThis.auth;if(!I||"object"!=typeof I)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!I.positionId)throw new Error("auth.positionId is required");if("number"!=typeof I.timestamp)throw new Error("auth.timestamp must be a number");if(!I.signature)throw new Error("auth.signature is required");const C=BigInt(I.amount);if(C<=0n)throw new Error(`Withdrawal amount must be positive, got: ${C.toString()} sats`);const B=u;if(C<B)throw new Error(`Withdrawal amount ${C.toString()} sats is below minimum ${B.toString()} sats (Bitcoin dust limit). This amount is not economically viable for Bitcoin network transactions.`);const U=I.destinationAddress;if(!U||"string"!=typeof U)throw new Error('Missing or invalid "destinationAddress" parameter - must be a valid Bitcoin address');const D=x().publicKey;if(!D)throw new Error("publicKey must not be blank");if("string"!=typeof D||0===D.length)throw new Error("publicKey must not be blank");const O=D;if(!(()=>{try{const t=/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,35}$/.test(U),e=/^(bc1|tb1|bcrt1)[0-9a-zA-Z]{11,87}$/.test(U);return!(!t&&!e)&&("testnet"===a||"regtest"===a?U.startsWith("m")||U.startsWith("n")||U.startsWith("2")||U.startsWith("tb1")||U.startsWith("bcrt1"):"mainnet"===a&&(U.startsWith("1")||U.startsWith("3")||U.startsWith("bc1")))}catch(t){return!1}})())throw new Error(`Invalid Bitcoin address format for ${a} network: ${U}. Expected ${"mainnet"===a?"address starting with 1, 3, or bc1":"address starting with m, n, 2, tb1, or bcrt1"}`);console.log(` \u2705 Destination address validated: ${U} (${a})`);let N="0.5";try{console.log("[BTC Withdrawal] Started"),console.log(` Position: ${I.positionId}`),console.log(` Withdrawal Amount: ${C.toString()} sats`),console.log(` Destination Address: ${U}`);const t=new R(globalThis.priceProviders,void 0),a=globalThis.rpcUrl;let u;a&&"string"==typeof a?(!function(t,e){if(t.allowArbitraryEvmRpc)return;if(!e||"string"!=typeof e)throw new Error("validateEvmRpcForPolicy: rpcUrl must be a non-empty string");let r;try{r=new URL(e).hostname}catch(t){throw new Error(`Invalid rpcUrl: ${t?.message??String(t)}`)}if(!T.some((t=>r===t||r.endsWith(`.${t}`))))throw new Error(`Custom RPC domain "${r}" is not in the approved list: ${T.join(", ")}`)}(i,a),console.log(` Using RPC URL override: ${function(t){if(null==t||"string"!=typeof t)return"[missing]";let e=t.replace(/([?&])(dkey|apikey|api_key|key|token|secret|password|auth|authorization)=[^&]*/gi,"$1$2=[REDACTED]");try{const t=new URL(e);(t.username||t.password)&&(t.username="",t.password="",e=t.toString())}catch{}return e}(a)}`),u=a):(u=await Lit.Actions.getRpcUrl({chain:r}),console.log(` Using LIT-provided RPC URL for chain: ${r}`));const S=new ethers.providers.JsonRpcProvider(u);s=await ct({policy:i,bitcoinProviderUrl:o,ethersProvider:S,registryAddress:globalThis.contractAddresses.BitcoinProviderRegistry}),console.log(` \u2705 Bitcoin Provider: ${s.name} (min confs ${s.minConfirmations})`);const B=new k({providerUrl:s.url});!function(t,e){const r=K[t],o=[];for(const t of r)e[t]&&o.push(t);if(o.length>0)throw new Error(JSON.stringify({code:"PROTOCOL_PAUSED",operation:t,paused:o,status:e}))}("withdrawBtc",await rt(S,p));const M=(L={contractAddress:$||g,chain:r,chainId:P(n),rpcUrl:u,bitcoinProvider:B,minConfirmations:s.minConfirmations},new q(L));N="1",console.log("[Step 1] Getting vault snapshot..."),e.stepStart("1","Get vault snapshot");const x=function(t){return new j(t)}({contractAddress:p,termManagerAddress:y,loanOpsManagerAddress:g,liquidationManagerAddress:E,chain:r,chainId:P(n),rpcUrl:u,vaultBalance:M,priceOracle:t}),D=await x.getVaultSnapshot(I.positionId);console.log(` \u2705 Vault balance: ${D.availableBTCSats.toString()} sats`),console.log(` \u2705 Current debt: ${D.ucdDebt.toString()} wei`),e.stepEnd("1"),N="2",console.log("[Step 2] Authenticating caller..."),e.stepStart("2","Authenticate caller");const F=e.getElapsed();console.log(`[TIMING] Step 2 started at ${F}ms`);const _=P(n),H=new ethers.providers.StaticJsonRpcProvider(u,_);let W;if(5===D.status){if(!v)throw new Error("LITActionValidator address required for LIQUIDATED position withdrawals. Pass contractAddresses.LITActionValidator in jsParams.");const t=new ethers.Contract(v,["function pkpOwners(bytes32) view returns (address)"],H);if(W=await t.pkpOwners(D.pkpId),!W||W===ethers.constants.AddressZero)throw new Error("No PKP owner registered for this liquidated position in LITActionValidator");console.log(` \u2139\ufe0f LIQUIDATED position \u2014 verifying against PKP owner: ${W}`)}else W=D.borrower;if(!await A.verifyWithdrawAuthorization(I,W,H))throw new Error("Caller not authorized");console.log(" \u2705 Caller authorized"),e.stepEnd("2"),N="2.5",console.log("[Step 2.5] Validating position status..."),e.stepStart("2.5","Validate position status");const G=e.getElapsed();console.log(`[TIMING] Step 2.5 started at ${G}ms`);if(![0,1,2,6,5].includes(D.status))throw new Error(`Position status ${m(D.status)} does not allow withdrawal. Valid statuses: PENDING_DEPOSIT, PENDING_MINT, ACTIVE, REPAID, LIQUIDATED.`);console.log(` \u2705 Position status valid for withdrawal: ${m(D.status)}`),e.stepEnd("2.5"),N="3",console.log("[Step 3] Validating post-withdrawal collateral ratio..."),e.stepStart("3","Validate collateral ratio");const J=e.getElapsed();console.log(`[TIMING] Step 3 started at ${J}ms`);const Q=C;if(0n===D.ucdDebt)console.log(" \u2705 Position has zero debt - withdrawal allowed");else{if(D.btcPriceUsd<c||D.btcPriceUsd>l)throw new Error(`Bitcoin price ${D.btcPriceUsd} is outside acceptable range (${c} - ${l}). This may indicate oracle manipulation or stale price data. Please try again later.`);console.log(` \u2705 BTC price validation passed: $${(Number(D.btcPriceUsd)/1e8).toFixed(2)}`);const t=D.availableBTCSats-Q,e=X(z(t,D.btcPriceUsd),D.ucdDebt);if(e<D.liquidationThresholdBps)throw new Error(`Insufficient collateral: post-withdrawal ratio ${V(e)}% < liquidation threshold ${V(D.liquidationThresholdBps)}%. Withdrawal would result in insufficient collateralization.`);console.log(` \u2705 Post-withdrawal collateral ratio: ${V(e)}%`),console.log(` \u2705 Exceeds liquidation threshold: ${V(D.liquidationThresholdBps)}%`)}e.stepEnd("3"),N="4",console.log("[Step 4] Validating withdrawal amount and balance..."),e.stepStart("4","Validate withdrawal amount and balance");const Y=e.getElapsed();console.log(`[TIMING] Step 4 started at ${Y}ms`);const Z="string"==typeof I.amount?BigInt(I.amount):I.amount;if(Z!==C)throw new Error(`Authorization amount mismatch: auth.amount=${Z.toString()} sats, but requested withdrawal=${C.toString()} sats. The signed authorization must match the requested withdrawal amount.`);if(console.log(" \u2705 Authorization amount matches withdrawal amount"),Q>D.availableBTCSats)throw new Error(`Insufficient vault balance: need ${Q.toString()} sats but only ${D.availableBTCSats.toString()} sats available`);console.log(" \u2705 Total deduction is within available balance");const tt=D.availableBTCSats-Q,et=D.ucdDebt,it=z(tt,D.btcPriceUsd),nt=et>0n?X(it,et):void 0;if(console.log(` Post-withdrawal collateral: ${tt.toString()} sats`),console.log(` Post-withdrawal collateral value: $${(Number(it)/1e8).toFixed(2)}`),console.log(` Post-withdrawal debt: ${et.toString()} wei`),console.log(` Post-withdrawal collateral ratio: ${et>0n&&void 0!==nt?V(Number(nt)):"\u221e"}%`),tt<0n)throw new Error(`Withdrawal would result in negative balance: ${tt.toString()} sats`);console.log(" \u2705 Withdrawal validation passed"),e.stepEnd("4"),N="4.5",console.log("[Step 4.5] Querying vault UTXOs from Bitcoin network..."),e.stepStart("4.5","Query and select UTXO");const at=e.getElapsed();console.log(`[TIMING] Step 4.5 started at ${at}ms`);const st=D.vaultAddress;if(!st)throw new Error("Vault address not found in snapshot");console.log(` Vault address: ${st}`);let ut=[],dt=null;const ht=d,pt=h;for(let t=1;t<=ht;t++)try{console.log(` \ud83d\udd04 UTXO query attempt ${t}/${ht}...`),ut=await B.getUTXOs(st),console.log(` \u2705 UTXO query succeeded on attempt ${t}`),dt=null;break}catch(e){dt=e,console.error(` \u274c UTXO query failed on attempt ${t}:`,e.message),t<ht&&(console.log(` \u23f3 Waiting ${pt}ms before retry...`),await new Promise((t=>setTimeout(t,pt))))}if(dt)throw new Error(`Failed to query UTXOs after ${ht} attempts: ${dt.message}`);if(!ut||0===ut.length)throw new Error(`No UTXOs found in vault ${st}`);console.log(` \u2705 Found ${ut.length} UTXO(s) in vault`);const gt=s.minConfirmations,mt=ut.filter((t=>t.confirmations>=gt));if(0===mt.length)throw new Error(`No confirmed UTXOs found in vault. Required confirmations: ${gt}, found ${ut.length} UTXO(s) with insufficient confirmations.`);console.log(` \u2705 Found ${mt.length} confirmed UTXO(s)`);const wt=mt.filter((t=>t.satoshis>=Q));let ft;if(0===wt.length){const t=mt.reduce(((t,e)=>t+e.satoshis),0n);if(t<Q)throw new Error(`Insufficient vault balance for withdrawal. Need ${Q.toString()} sats, total across ${mt.length} UTXO(s): ${t.toString()} sats.`);ft=[...mt].sort(((t,e)=>Number(e.satoshis-t.satoshis)))[0],console.log(" \u2139\ufe0f No single UTXO covers withdrawal \u2014 multi-UTXO consolidation will be used in Phase 2."),console.log(` Primary UTXO (contract auth): ${ft.txid}:${ft.vout} (${ft.satoshis.toString()} sats)`)}else ft=wt.sort(((t,e)=>Number(t.satoshis-e.satoshis)))[0];console.log(` \u2705 Selected UTXO: ${ft.txid}:${ft.vout}`),console.log(` \u2705 UTXO value: ${ft.satoshis.toString()} sats`),console.log(` \u2705 UTXO confirmations: ${ft.confirmations}`);const yt=D.availableBTCSats;if(ft.satoshis<yt&&console.warn(` \u26a0\ufe0f Selected UTXO value (${ft.satoshis.toString()}) < calculated balance (${yt.toString()}). This is expected for multi-UTXO vaults where we select one UTXO.`),ft.satoshis<Q)throw new Error(`Selected UTXO value (${ft.satoshis.toString()} sats) < withdrawal amount (${Q.toString()} sats). This should never happen after eligibility filtering.`);console.log(" \u2705 UTXO validation passed"),console.log(" \ud83d\udd0d Verifying UTXO vout against Bitcoin transaction...");const bt=lt(st).toLowerCase(),Tt=await B.getTransactionOutputs(ft.txid);if(!Tt)throw new Error(`Could not fetch transaction outputs for ${ft.txid} \u2014 cannot canonicalize vout. Refusing to authorize potentially incorrect UTXO. Retry the withdrawal once the Bitcoin data provider is reachable.`);let vt=-1;for(const t of Tt){const e=(t.scriptpubkey??"").toLowerCase(),r=e.length>0&&e===bt,o=t.scriptpubkey_address===st;if(r||o){vt=t.index;break}}if(-1===vt)throw new Error(`Vault output not found in tx ${ft.txid}. Expected scriptPubKey ${bt} or address ${st}, but no output in the transaction matches. Refusing to authorize wrong UTXO.`);vt!==ft.vout?(console.log(` \u26a0\ufe0f Esplora API reported vout=${ft.vout} but vault output is at vout=${vt} (matched on hex scriptpubkey). Correcting.`),ft={...ft,vout:vt}):console.log(` \u2705 UTXO vout=${ft.vout} confirmed correct`),e.stepEnd("4.5"),N="5",console.log("[Step 5] Building authorization message..."),e.stepStart("5","Build authorization message");const St=e.getElapsed();console.log(`[TIMING] Step 5 started at ${St}ms`);const Pt=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(I.action)),Et=D.authorizedSpendsHash,$t=ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["uint256"],[D.ucdDebt])),At=ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["address","address","address","address"],[p,g,y,b])),It=BigInt(I.timestamp),Ct=tt,Bt=D.btcPriceUsd;if(Bt<c||Bt>l)throw new Error(`BTC price ${Bt.toString()} (8 decimals) is outside acceptable range (${c.toString()} - ${l.toString()})`);console.log("[Step 5] DEBUG: Message Hash Parameters:"),console.log(" positionId:",I.positionId),console.log(" action:",I.action),console.log(" actionHash:",Pt),console.log(" authorizedSpendsHash:",Et),console.log(" ucdDebtHash:",$t),console.log(" withdrawalAddress:",U),console.log(" totalDeduction:",Q.toString()),console.log(" newCollateral:",Ct.toString()),console.log(" quantumTimestamp:",It.toString()),console.log(" btcPrice:",Bt.toString()),console.log(" utxoTxid:",ft.txid),console.log(" utxoVout:",ft.vout);const Ut=ot(I.positionId),Mt=P(n),xt={name:w,version:f,chainId:Mt,verifyingContract:ethers.utils.getAddress(g)},Dt={WithdrawalAuthorization:[{name:"positionId",type:"bytes32"},{name:"actionHash",type:"bytes32"},{name:"authorizedSpendsHash",type:"bytes32"},{name:"ucdDebtHash",type:"bytes32"},{name:"contractBundleHash",type:"bytes32"},{name:"withdrawalAddress",type:"string"},{name:"totalDeduction",type:"uint256"},{name:"newCollateral",type:"uint256"},{name:"quantumTimestamp",type:"uint256"},{name:"btcPrice",type:"uint256"},{name:"utxoTxid",type:"string"},{name:"utxoVout",type:"uint32"}]},Ot=ethers.utils._TypedDataEncoder.hash(xt,Dt,{positionId:Ut,actionHash:Pt,authorizedSpendsHash:Et,ucdDebtHash:$t,contractBundleHash:At,withdrawalAddress:U,totalDeduction:Q.toString(),newCollateral:Ct.toString(),quantumTimestamp:It.toString(),btcPrice:Bt.toString(),utxoTxid:ft.txid,utxoVout:ft.vout});console.log("[Step 5] Contract message hash for signing:",Ot),console.log("[Step 5] Message hash bytes length:",ethers.utils.arrayify(Ot).length),e.stepEnd("5"),N="6",console.log("[Step 6] Signing contract message hash..."),e.stepStart("6","Sign message");const kt=e.getElapsed();console.log(`[TIMING] Step 6 started at ${kt}ms`),e.log("6","========================================"),e.log("6","\ud83d\udd0d FINAL HASH INPUTS - ALL LIT NODES MUST MATCH"),e.log("6","========================================"),e.log("6",`Position ID: ${I.positionId}`),e.log("6",`Action Hash: ${Pt}`),e.log("6",`Authorized Spends Hash: ${Et}`),e.log("6",`UCD Debt Hash: ${$t}`),e.log("6",`Contract Bundle Hash: ${At}`),e.log("6",`Destination Address: ${U}`),e.log("6",`Total Deduction: ${Q.toString()}`),e.log("6",`New Collateral: ${Ct.toString()}`),e.log("6",`Quantum Timestamp: ${It.toString()}`),e.log("6",`BTC Price: ${Bt.toString()}`),e.log("6",`Selected UTXO Txid: ${ft.txid}`),e.log("6",`Selected UTXO Vout: ${ft.vout}`),e.log("6",`Selected UTXO Satoshis: ${ft.satoshis.toString()}`),e.log("6",`Selected UTXO Confirmations: ${ft.confirmations}`),e.log("6","---"),e.log("6",`\ud83d\udcdd FINAL CONTRACT MESSAGE HASH: ${Ot}`),e.log("6","========================================"),e.log("6","Preparing signEcdsa call"),e.log("6",`contractMessageHash: ${Ot}`),e.log("6",`publicKey: ${O}`),e.log("6","publicKey with 0x prefix: "+("0x"+O));const Nt=ethers.utils.arrayify(Ot);e.log("6",`messageBytes length: ${Nt.length}`),e.log("6",`messageBytes: ${JSON.stringify(Array.from(Nt))}`),e.log("6","About to sign with wallet-derived key (Chipotle)...");const Rt=Date.now(),qt=globalThis.validatorWalletAddress;if(!qt||!qt.startsWith("0x"))throw new Error("validatorWalletAddress required \u2014 must be a createWallet-derived address");const Lt=await Lit.Actions.getPrivateKey({pkpId:qt}),Ft="string"==typeof Lt?Lt.startsWith("0x")?Lt:`0x${Lt}`:String(Lt),_t=new ethers.Wallet(Ft),zt=new ethers.utils.SigningKey(Ft);e.log("6",`Wallet-derived signer address: ${_t.address}`),e.log("6",`validatorWalletAddress: ${qt}`);const Xt=zt.signDigest(Ot),Vt=ethers.utils.joinSignature(Xt),Ht=Date.now()-Rt;e.log("6",`Chipotle signature created in ${Ht}ms`),e.log("6",`signature: ${Vt}`);const Wt=globalThis.contractAddresses.BTCSpendAuthorizer?.trim()||"";let jt=null;if(Wt){const t=ot(I.positionId),r=P(n),o=Ct+Q,i={name:w,version:f,chainId:r,verifyingContract:ethers.utils.getAddress(Wt)},a={BtcSpendAuth:[{name:"positionId",type:"bytes32"},{name:"txid",type:"string"},{name:"vout",type:"uint32"},{name:"satoshis",type:"uint256"},{name:"targetAddress",type:"string"},{name:"targetAmount",type:"uint256"}]},s=ethers.utils._TypedDataEncoder.hash(i,a,{positionId:t,txid:ft.txid,vout:ft.vout,satoshis:o.toString(),targetAddress:U,targetAmount:Q.toString()});e.log("6",`EIP-712 BtcSpendAuth digest: ${s}`),jt=await _t._signTypedData(i,a,{positionId:t,txid:ft.txid,vout:ft.vout,satoshis:o.toString(),targetAddress:U,targetAmount:Q.toString()}),e.log("6",`btcSpendAuth signature: ${jt}`)}e.stepEnd("6"),console.log("[BTC Withdrawal] \u2705 Complete"),e.summary();const Gt={approved:!0,positionId:I.positionId,actionHash:Pt,authorizedSpendsHash:Et,ucdDebtHash:$t,contractBundleHash:At,totalDeduction:Q.toString(),remainingCollateral:tt.toString(),newCollateralRatioBps:et>0n&&void 0!==nt?Number(nt):void 0,destinationAddress:U,signature:Vt,btcSpendAuthSignature:jt,timestamp:It.toString(),btcPrice:Bt.toString(),utxoTxid:ft.txid,utxoVout:ft.vout,utxoSatoshis:ft.satoshis.toString()};return console.log("[BTC Withdrawal] \ud83d\udd0d Response data before JSON.stringify:"),console.log(" totalDeduction:",Q,"type:",typeof Q,"toString():",Q.toString()),console.log(" remainingCollateral:",tt,"type:",typeof tt,"toString():",tt.toString()),console.log(" btcPrice:",Bt,"type:",typeof Bt,"toString():",Bt.toString()),console.log(" timestamp:",It,"type:",typeof It,"toString():",It.toString()),console.log(" Full response object:",JSON.stringify(Gt,null,2)),Gt}catch(t){return console.error("[BTC Withdrawal] \u274c Failed:",t.message),console.error(`[BTC Withdrawal] Failed at step: ${N}`),e.error("error",`Failed: ${t.message}`),{approved:!1,reason:t.message||t.toString(),positionId:I?.positionId,timestamp:Date.now()}}var L}return nt=i,((i,n,a,s)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let c of r(n))o.call(i,c)||c===a||t(i,c,{get:()=>n[c],enumerable:!(s=e(n,c))||s.enumerable});return i})(t({},"__esModule",{value:!0}),nt)})();if("object"==typeof _LIT_ACTION_)if("function"==typeof _LIT_ACTION_.main)var main=_LIT_ACTION_.main;else"function"==typeof _LIT_ACTION_.go&&_LIT_ACTION_.go();
|
package/dist/btc-withdrawal.hash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5d2d3672dfa4027fd17d6bf4740995561ccc4dbe1ed388be19c241ecf5f4018f
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "btc-withdrawal",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 158116,
|
|
4
|
+
"minifiedSize": 68213,
|
|
5
|
+
"compressionRatio": 0.5685888841104,
|
|
6
|
+
"hash": "851b84daad3049625c5bc4e88234ae30abdaeb63976410e122dad13dca5f0701",
|
|
7
|
+
"buildTime": 1776708630772,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "extend-position-validator",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 143557,
|
|
4
|
+
"minifiedSize": 58925,
|
|
5
|
+
"compressionRatio": 0.589535863803228,
|
|
6
|
+
"hash": "2b1c8b248de4d8e871af5c6348633b2ad6b971994be53a1312b7db97ea9a3f0f",
|
|
7
|
+
"buildTime": 1776708630912,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "liquidation-validator",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 120292,
|
|
4
|
+
"minifiedSize": 52154,
|
|
5
|
+
"compressionRatio": 0.566438333388754,
|
|
6
|
+
"hash": "98ea7934bc28a9a301cdabecddd9ac29d8d5ad9db500a8812dbd714e6ef90d37",
|
|
7
|
+
"buildTime": 1776708631025,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "loan-vault-btc-balance",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 100326,
|
|
4
|
+
"minifiedSize": 41974,
|
|
5
|
+
"compressionRatio": 0.581623906066224,
|
|
6
|
+
"hash": "8af1d27077dc23ba453d3e1e63b7778c5681d3438c23cde5d8dc2ecabb54548e",
|
|
7
|
+
"buildTime": 1776708631151,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "pkp-validator",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 8281,
|
|
4
|
+
"minifiedSize": 4030,
|
|
5
|
+
"compressionRatio": 0.5133437990580847,
|
|
6
|
+
"hash": "f57523311d2f1f1f1fae5a1bbdcd877582e64268c1cb90396c0d1dc26ab9a1fb",
|
|
7
|
+
"buildTime": 1776708631170,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "price-oracle",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 27462,
|
|
4
|
+
"minifiedSize": 11644,
|
|
5
|
+
"compressionRatio": 0.5759959216371713,
|
|
6
|
+
"hash": "d175d8571aa4337d465115f9939a2d72965255c7c7a64c33e546ffff141e7a09",
|
|
7
|
+
"buildTime": 1776708631202,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "process-payment-validator",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 142957,
|
|
4
|
+
"minifiedSize": 60029,
|
|
5
|
+
"compressionRatio": 0.5800905167288066,
|
|
6
|
+
"hash": "aadca9aa3f69c571b906231d5ae96d81ae2c9ae715c9aa4f541ec316bcec37f7",
|
|
7
|
+
"buildTime": 1776708631331,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "ucd-mint-validator",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 150854,
|
|
4
|
+
"minifiedSize": 63656,
|
|
5
|
+
"compressionRatio": 0.5780290877272065,
|
|
6
|
+
"hash": "86e035885bc0888afe8ef8cd69baca7e9219098b28822a7f4af15f32fdf6fbca",
|
|
7
|
+
"buildTime": 1776708631455,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|