@gvnrdao/dh-lit-actions 0.0.112 → 0.0.192

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.
Files changed (93) hide show
  1. package/dist/admin-liquidation-validator.action.js +1 -0
  2. package/dist/admin-liquidation-validator.hash +1 -0
  3. package/dist/admin-liquidation-validator.meta.json +9 -0
  4. package/dist/always-signer.action.js +1 -0
  5. package/dist/always-signer.hash +1 -0
  6. package/dist/always-signer.meta.json +9 -0
  7. package/dist/authorization-dummy-b.action.js +1 -0
  8. package/dist/authorization-dummy-b.hash +1 -0
  9. package/dist/authorization-dummy-b.meta.json +9 -0
  10. package/dist/authorization-dummy.action.js +1 -0
  11. package/dist/authorization-dummy.hash +1 -0
  12. package/dist/authorization-dummy.meta.json +9 -0
  13. package/dist/btc-transaction-signer.action.js +1 -0
  14. package/dist/btc-transaction-signer.hash +1 -0
  15. package/dist/btc-transaction-signer.meta.json +9 -0
  16. package/dist/btc-withdrawal.action.js +1 -0
  17. package/dist/btc-withdrawal.hash +1 -0
  18. package/dist/btc-withdrawal.meta.json +9 -0
  19. package/dist/extend-position-validator.action.js +1 -0
  20. package/dist/extend-position-validator.hash +1 -0
  21. package/dist/extend-position-validator.meta.json +9 -0
  22. package/dist/liquidation-validator.action.js +1 -0
  23. package/dist/liquidation-validator.hash +1 -0
  24. package/dist/liquidation-validator.meta.json +9 -0
  25. package/dist/loan-vault-btc-balance.action.js +1 -0
  26. package/dist/loan-vault-btc-balance.hash +1 -0
  27. package/dist/loan-vault-btc-balance.meta.json +9 -0
  28. package/dist/pkp-validator-datil.action.js +1 -0
  29. package/dist/pkp-validator-datil.hash +1 -0
  30. package/dist/pkp-validator-datil.meta.json +9 -0
  31. package/dist/price-oracle.action.js +1 -0
  32. package/dist/price-oracle.hash +1 -0
  33. package/dist/price-oracle.meta.json +9 -0
  34. package/dist/process-payment-sign-only.action.js +1 -0
  35. package/dist/process-payment-sign-only.hash +1 -0
  36. package/dist/process-payment-sign-only.meta.json +9 -0
  37. package/dist/process-payment-validator.action.js +1 -0
  38. package/dist/process-payment-validator.hash +1 -0
  39. package/dist/process-payment-validator.meta.json +9 -0
  40. package/dist/ucd-mint-validator.action.js +1 -0
  41. package/dist/ucd-mint-validator.hash +1 -0
  42. package/dist/ucd-mint-validator.meta.json +9 -0
  43. package/package.json +4 -2
  44. package/pkg-dist/pkg-src/constants/chunks/lit-actions-registry.d.ts.map +1 -1
  45. package/pkg-dist/pkg-src/constants/chunks/lit-actions-registry.js +127 -108
  46. package/pkg-dist/pkg-src/constants/chunks/lit-actions-registry.js.map +1 -1
  47. package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.d.ts +47 -0
  48. package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.d.ts.map +1 -0
  49. package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.js +77 -0
  50. package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.js.map +1 -0
  51. package/pkg-dist/pkg-src/executors/index.d.ts +39 -0
  52. package/pkg-dist/pkg-src/executors/index.d.ts.map +1 -1
  53. package/pkg-dist/pkg-src/executors/index.js +41 -1
  54. package/pkg-dist/pkg-src/executors/index.js.map +1 -1
  55. package/pkg-dist/pkg-src/utils/chunks/cid-utils.d.ts.map +1 -1
  56. package/pkg-dist/pkg-src/utils/chunks/cid-utils.js +0 -5
  57. package/pkg-dist/pkg-src/utils/chunks/cid-utils.js.map +1 -1
  58. package/pkg-dist/src/constants/chunks/quantum-time.d.ts +5 -5
  59. package/pkg-dist/src/constants/chunks/quantum-time.js +5 -5
  60. package/pkg-dist/src/interfaces/chunks/vault-balance.i.d.ts +0 -1
  61. package/pkg-dist/src/interfaces/chunks/vault-balance.i.d.ts.map +1 -1
  62. package/pkg-dist/src/modules/bitcoin-data-provider.module.d.ts +7 -6
  63. package/pkg-dist/src/modules/bitcoin-data-provider.module.d.ts.map +1 -1
  64. package/pkg-dist/src/modules/bitcoin-data-provider.module.js +93 -97
  65. package/pkg-dist/src/modules/bitcoin-data-provider.module.js.map +1 -1
  66. package/pkg-dist/src/modules/business-rules-math.module.d.ts +2 -0
  67. package/pkg-dist/src/modules/business-rules-math.module.d.ts.map +1 -1
  68. package/pkg-dist/src/modules/business-rules-math.module.js +9 -2
  69. package/pkg-dist/src/modules/business-rules-math.module.js.map +1 -1
  70. package/pkg-dist/src/modules/lit-action-logger.module.d.ts +55 -0
  71. package/pkg-dist/src/modules/lit-action-logger.module.d.ts.map +1 -0
  72. package/pkg-dist/src/modules/lit-action-logger.module.js +184 -0
  73. package/pkg-dist/src/modules/lit-action-logger.module.js.map +1 -0
  74. package/pkg-dist/src/modules/price-oracle.module.d.ts +41 -3
  75. package/pkg-dist/src/modules/price-oracle.module.d.ts.map +1 -1
  76. package/pkg-dist/src/modules/price-oracle.module.js +265 -122
  77. package/pkg-dist/src/modules/price-oracle.module.js.map +1 -1
  78. package/pkg-dist/src/modules/protocol-parameters.module.d.ts +10 -0
  79. package/pkg-dist/src/modules/protocol-parameters.module.d.ts.map +1 -1
  80. package/pkg-dist/src/modules/protocol-parameters.module.js +49 -2
  81. package/pkg-dist/src/modules/protocol-parameters.module.js.map +1 -1
  82. package/pkg-dist/src/modules/quantum-time.module.d.ts +1 -1
  83. package/pkg-dist/src/modules/quantum-time.module.d.ts.map +1 -1
  84. package/pkg-dist/src/modules/quantum-time.module.js +18 -9
  85. package/pkg-dist/src/modules/quantum-time.module.js.map +1 -1
  86. package/pkg-dist/src/modules/vault-balance.module.d.ts +37 -3
  87. package/pkg-dist/src/modules/vault-balance.module.d.ts.map +1 -1
  88. package/pkg-dist/src/modules/vault-balance.module.js +116 -63
  89. package/pkg-dist/src/modules/vault-balance.module.js.map +1 -1
  90. package/pkg-dist/src/modules/vault-snapshot.d.ts +5 -3
  91. package/pkg-dist/src/modules/vault-snapshot.d.ts.map +1 -1
  92. package/pkg-dist/src/modules/vault-snapshot.js +9 -6
  93. package/pkg-dist/src/modules/vault-snapshot.js.map +1 -1
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{var t=1000000000000000000n,e=100;function r(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 i=(t=>(t.SEPOLIA="sepolia",t.ETHEREUM="ethereum",t.HARDHAT="hardhat",t))(i||{}),a={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},o={ethereum:1,sepolia:11155111,hardhat:31337};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(i).join(", ")}`)}function n(t){return o[t]}var c=class{stepStart(t,e){}stepEnd(t,e){}log(t,e){}warn(t,e){}error(t,e){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},l=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,i=this.TIMEOUT_MS-r;console.log(`[Step ${t}] ${e}...`),console.log(`[Step ${t}] Elapsed: ${r}ms | Remaining: ${i}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 i=Date.now()-r;this.stepDurations.set(t,i),console.log(`[Step ${t}] Duration: ${i}ms`),i>e&&console.warn(`\u26a0\ufe0f [Step ${t}] Took ${i}ms (> ${e}ms threshold)`);const a=Date.now()-this.executionStartTime,o=this.TIMEOUT_MS-a;o<5e3&&console.warn(`\u26a0\ufe0f [Step ${t}] Time remaining: ${o}ms (approaching timeout)`)}log(t,e){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 i=(r/t*100).toFixed(1);console.log(`[${this.actionName}] Step ${e}: ${r}ms (${i}%)`)}}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}},u=class{static create(t="LIT Action",e){return e??globalThis.debugAction??!1?new l(t):new c}},d=class{constructor(t){this.config=t,this.timeout=t.timeout||15e3,this.logger=u.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=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),!r.ok)throw new Error(`Failed to fetch block height: ${r.status} ${r.statusText}`);const i=await r.text(),a=parseInt(i.trim(),10);if(isNaN(a))throw new Error(`Invalid block height response: ${i}`);return this.logger.stepEnd("0"),a}catch(t){if(clearTimeout(e),this.logger.stepEnd("0"),"AbortError"===t.name)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: ${t.message}`),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: ${t.message}`);continue}}throw new Error(`Failed to fetch UTXOs: ${t.message}`)}}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");return t.map((t=>{let r=0;return 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),{txid:t.txid,vout:void 0!==t.vout?t.vout:t.n,satoshis:BigInt(void 0!==t.value?t.value:t.satoshis),confirmations:r}}))}async fetchUTXOsFromProvider(t,e){this.logger.stepStart("1","fetchUTXOsFromProvider");const r=`${t}/address/${e}/utxos`;this.logger.log("1",`Fetching UTXOs from ${r}`);const i=await this.getCurrentBlockHeight();this.logger.log("1",`Current block height: ${i}`);const a=new AbortController,o=setTimeout((()=>a.abort()),this.timeout);try{const t=await fetch(r,{signal:a.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(o),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();this.logger.log("1",`Raw API Response: ${JSON.stringify(e,null,2)}`);const s=this.parseBlockstreamUTXOs(e,i);return this.logger.stepEnd("1"),s}catch(t){if(clearTimeout(o),this.logger.stepEnd("1"),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOSet(t,e){let r=[],i=null;for(let e=1;e<=3;e++)try{this.logger.log("2",`UTXO query attempt ${e}/3...`),r=await this.getUTXOs(t),this.logger.log("2",`UTXO query succeeded on attempt ${e}`),i=null;break}catch(t){i=t,this.logger.error("2",`UTXO query failed on attempt ${e}: ${t.message}`),e<3&&(this.logger.log("2","Waiting 500ms before retry..."),await new Promise((t=>setTimeout(t,500))))}if(i)throw new Error(`Failed to query UTXOs after 3 attempts: ${i.message}`);const a=r.reduce(((t,e)=>t+e.satoshis),0n),o=r.filter((t=>t.confirmations>=e)).reduce(((t,e)=>t+e.satoshis),0n),s=a-o;return{utxos:r,totalBalance:a,totalUTXOs:r.length,confirmedBalance:o,unconfirmedBalance:s}}async getBalance(t){const e=this.stripNetworkPrefix(t),r=`${this.config.providerUrl}/address/${e}`;this.logger.log("3",`Fetching balance from ${r}`);const i=new AbortController,a=setTimeout((()=>i.abort()),this.timeout);try{const t=await fetch(r,{signal:i.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(a),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();if(!e.chain_stats)throw new Error("Invalid response: missing chain_stats");const o=e.chain_stats.funded_txo_sum,s=e.chain_stats.spent_txo_sum;if(void 0===o||void 0===s)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${o}, spent_txo_sum: ${s}`);const n=BigInt(o-s);return this.logger.log("3",`Balance: ${n.toString()} sats`),this.logger.log("3",` - Funded: ${o} sats, Spent: ${s} sats`),n}catch(t){if(clearTimeout(a),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);const r=t.message||"Unknown error";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){if(t.message?.includes("Invalid or non-wallet transaction")||-5===t.code)return null;throw t}const e=new AbortController,r=setTimeout((()=>e.abort()),this.timeout);try{const i=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(r),!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,confirmations:a.status.confirmed&&a.status.block_height?1:0}:null}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);if(t.message?.includes("404"))return null;throw t}}};function h(t,e){if(0===t)return{termDurationDays:0,termLengthDays:30*e,isExpired:!1,daysUntilExpiry:30*e,daysIntoGracePeriod:0};const r=30*e,i=Math.floor(Date.now()/1e3)-t,a=Math.floor(i/86400);return{termDurationDays:a,termLengthDays:r,isExpired:a>r,daysUntilExpiry:Math.max(0,r-a),daysIntoGracePeriod:Math.max(0,a-r)}}function p(t,e){if(!t)return 13e3;const r=Math.min(e,30);return 11e3+9e3*(r*r)/900}function g(e,r,i){const a=e*r/10000000000000000n;if(0n===i)return{collateralValueUsd:a,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:a,collateralRatioBps:Number(a*t*10000n/i)}}function m(t){return t/100}var f=class{constructor(t,e,r){this.mode=r,e&&Array.isArray(e)?this.sources=e:t&&Array.isArray(t)?this.sources=this.buildSources(t):this.sources=this.buildSources(),this.sources.sort(((t,e)=>t.priority-e.priority)),this.validateProviderCount()}validateProviderCount(){if("prod"===this.mode&&this.sources.length<3)throw new Error(`Production mode requires at least 3 price providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported providers: CoinGecko, Binance, Coinbase, CryptoCompare (with API key)`);if(0===this.sources.length)throw new Error("No price providers configured. At least one provider is required.")}buildSources(t){const e=[];if(!t||0===t.length)return this.getDefaultSources();let r=1;for(const i of t){switch(i.name.toLowerCase()){case"coingecko":e.push(this.createCoinGeckoSource(i.apiKey,r++));break;case"binance":e.push(this.createBinanceSource(i.apiKey,i.apiSecret,r++));break;case"coinbase":e.push(this.createCoinbaseSource(i.apiKey,r++));break;case"cryptocompare":if(!i.apiKey)throw new Error("CryptoCompare requires an API key. Please provide apiKey in priceProviders config.");e.push(this.createCryptoCompareSource(i.apiKey,r++));break;default:console.warn(`[Price Oracle] Unknown provider: ${i.name} - skipping`)}}return e}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 i=await fetch(t,{signal:e.signal,headers:{Accept:"application/json"}});if(clearTimeout(r),!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error("Request timeout after 5000ms");throw t}})(e.toString()),i=Number(r?.USDT);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CryptoCompare price payload");const a=(await Lit.Actions.broadcastAndCollect({name:"cryptoComparePrice",value:i.toString()})).map((t=>parseFloat(t)));return a.sort(((t,e)=>t-e)),a[Math.floor(a.length/2)]},priority:e}}createFetchJson(){return async(t,e)=>{const r=new AbortController,i=setTimeout((()=>r.abort()),5e3);try{const i=await fetch(t,{headers:{Accept:"application/json",...e||{}},signal:r.signal});if(!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}finally{clearTimeout(i)}}}getDefaultSources(){return[this.createCoinGeckoSource(void 0,1),this.createBinanceSource(void 0,void 0,2),this.createCoinbaseSource(void 0,3)]}createCoinGeckoSource(t,e=1){const r=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const t=new URL("https://api.coingecko.com/api/v3/simple/price");t.searchParams.set("ids","bitcoin"),t.searchParams.set("vs_currencies","usd");const e=await r(t.toString()),i=Number(e?.bitcoin?.usd);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CoinGecko price payload");const a=(await Lit.Actions.broadcastAndCollect({name:"coinGeckoPrice",value:i.toString()})).map((t=>parseFloat(t)));return a.sort(((t,e)=>t-e)),a[Math.floor(a.length/2)]},priority:e}}createBinanceSource(t,e,r=2){const i=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 r=t?{"X-MBX-APIKEY":t}:void 0,a=await i(e.toString(),r),o=Number(a?.price);if(!Number.isFinite(o)||o<=0)throw new Error("Invalid Binance price payload");const s=(await Lit.Actions.broadcastAndCollect({name:"binancePrice",value:o.toString()})).map((t=>parseFloat(t)));return s.sort(((t,e)=>t-e)),s[Math.floor(s.length/2)]},priority:r}}createCoinbaseSource(t,e=3){const r=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const e=t?{"CB-ACCESS-KEY":t}:void 0,i=await r("https://api.coinbase.com/v2/prices/BTC-USD/spot",e),a=Number(i?.data?.amount);if(!Number.isFinite(a)||a<=0)throw new Error("Invalid Coinbase price payload");const o=(await Lit.Actions.broadcastAndCollect({name:"coinbasePrice",value:a.toString()})).map((t=>parseFloat(t)));return o.sort(((t,e)=>t-e)),o[Math.floor(o.length/2)]},priority:e}}async getBTCPrice(){const t=Date.now();console.log("[Price Oracle] Fetching BTC price from external sources (parallel)..."),console.log(`[Price Oracle] Start time: ${t}`);const e=new Promise(((e,r)=>{setTimeout((()=>{const e=Date.now()-t;console.error(`[Price Oracle] \u274c GLOBAL TIMEOUT after ${e}ms`),r(new Error("Price oracle global timeout after 15000ms"))}),15e3)})),r=this.sources.map((async t=>{const e=Date.now();try{console.log(`[Price Oracle] [${e}] Querying ${t.name}...`);const r=await t.fetchPrice(),i=Date.now()-e;return console.log(`[Price Oracle] \u2705 [+${i}ms] ${t.name}: $${r.toLocaleString()}`),{source:t.name,priceUSD:r}}catch(r){const i=Date.now()-e;throw console.warn(`[Price Oracle] \u26a0\ufe0f [+${i}ms] ${t.name} failed: ${r.message}`),r}}));try{console.log("[Price Oracle] Waiting for first success (race pattern)...");const i=await Promise.race([Promise.race(r.map((t=>t.catch((t=>({error:t})))))),e]);if("error"in i){console.log("[Price Oracle] First result was error, waiting for any success...");const e=Date.now(),i=new Promise(((t,r)=>{setTimeout((()=>{const t=Date.now()-e;console.error(`[Price Oracle] \u274c FALLBACK TIMEOUT after ${t}ms`),r(new Error("Price oracle fallback timeout after 10000ms"))}),1e4)})),a=await Promise.race([Promise.allSettled(r),i]),o=Date.now()-e;console.log(`[Price Oracle] Fallback completed in ${o}ms`);const s=a.find((t=>"fulfilled"===t.status));if(!s){const e=Date.now()-t;throw console.error(`[Price Oracle] All sources failed after ${e}ms`),new Error("All price sources failed")}const{source:n,priceUSD:c}=s.value,l=Date.now()-t;console.log(`[Price Oracle] Using ${n} (first successful after failures) - total time: ${l}ms`);const u=Math.round(100*c),d=1000000n*BigInt(u);return console.log(`[Price Oracle] Price with 8 decimals: ${d}`),d}const{source:a,priceUSD:o}=i,s=Date.now()-t;console.log(`[Price Oracle] Using ${a} (fastest response) - total time: ${s}ms`);const n=Math.round(100*o),c=1000000n*BigInt(n);return console.log(`[Price Oracle] Price with 8 decimals: ${c}`),c}catch(e){const r=Date.now()-t;throw console.error(`[Price Oracle] Failed after ${r}ms: ${e.message}`),new Error(`All price sources failed: ${e.message}`)}}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)],i=e[0],a=e[e.length-1]/i,o=t.filter((t=>Math.abs(t.price-r)/r<=.02));if(a>1.05&&o.length===t.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(a-1)).toFixed(1)}% spread)`);if(o.length<t.length){const e=t.filter((t=>!o.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)`)})),o.length<2)throw new Error("Price consensus failed: insufficient valid sources after outlier removal");console.log(`[Price Oracle] \u2705 Outliers filtered, continuing with ${o.length} valid sources`);const i=o.map((t=>t.price));i.sort(((t,e)=>t-e));const a=i[Math.floor(i.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${a.toLocaleString()}`);const s=Math.round(100*a);return 1000000n*BigInt(s)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${r.toLocaleString()}`);const s=Math.round(100*r);return 1000000n*BigInt(s)}};function w(){const t=Math.floor(Date.now()/1e3);return r=t,Math.floor(r/e)*e;var r}0,0;var y=class{constructor(t){this.config=t,this.bitcoinProvider=t.bitcoinProvider}async calculateBalance(t,e){const r=await this.bitcoinProvider.getBalance(e),i=await this.getAuthorizedSpendsFromContract(t),a=i.reduce(((t,e)=>t+e.satoshis),0n),o=r>a?r-a:0n;return{totalUTXOs:[],totalBalance:r,authorizedUTXOs:i,authorizedBalance:a,authorizedSpendsHash:this.computeAuthorizedSpendsHash(t,i),availableUTXOs:[],availableBalance:o,vaultAddress:e,positionId:t,timestamp:Date.now()}}async calculateTrustedBalance(t,e,r){if(r)return await this.calculateBalance(t,e);const i=this.config.minConfirmations||6,a=await this.bitcoinProvider.getUTXOSet(e,i),o=await this.getAuthorizedSpendsFromContract(t);for(const t of o){if(a.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 s=o.reduce(((t,e)=>t+e.satoshis),0n),n=a.utxos.filter((t=>!this.isUTXOAuthorized(t,o))),c=n.reduce(((t,e)=>t+e.satoshis),0n),l=this.computeAuthorizedSpendsHash(t,o);return{totalUTXOs:a.utxos,totalBalance:a.totalBalance,authorizedUTXOs:o,authorizedBalance:s,authorizedSpendsHash:l,availableUTXOs:n,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,i){return(await this.getAvailableUTXOs(t,e)).some((t=>t.txid===r&&t.vout===i))}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 i=new ethers.providers.StaticJsonRpcProvider(r,{name:"any",chainId:this.config.chainId}),a=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"}],i);return(await a.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 i=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,i]))}};var b=class{constructor(t){this.termManagerAddress=t.termManagerAddress,this.loanOpsManagerAddress=t.loanOpsManagerAddress,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(t,{name:"any",chainId:this.chainId}),r=[{inputs:[],name:"liquidationThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],i=new ethers.Contract(this.loanOpsManagerAddress,r,e),a=await i.liquidationThreshold();return Number(a.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(e,{name:"any",chainId:this.chainId}),i=[{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"}],a=new ethers.Contract(this.termManagerAddress,i,r),o=await a.getTermFees(t);return{originationFeeBps:Number(o.originationFee?.toString?.()??o[0]?.toString?.()??o[0]),extensionFeeBps:Number(o.extensionFee?.toString?.()??o[1]?.toString?.()??o[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(e,{name:"any",chainId:this.chainId}),i=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`,a=new ethers.Contract(this.loanOpsManagerAddress,[{inputs:[{name:"positionId",type:"bytes32"}],name:"getAuthorizedSpendsHash",outputs:[{name:"",type:"bytes32"}],stateMutability:"view",type:"function"}],r);return await a.getAuthorizedSpendsHash(i)}catch(t){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",t.message),new Error(`Failed to fetch authorized spends hash: ${t.message}`)}}};function T(t){return new b(t)}var S=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=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress),i=await this.config.priceOracle.getBTCPrice(),a=T({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[o,s]=await Promise.all([a.getLiquidationThreshold(),a.getTermFees(e.selectedTerm)]),n=h(e.termStartTimestamp,e.selectedTerm),c=p(n.isExpired,n.daysIntoGracePeriod),l=g(r.availableBalance,i,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;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,totalBTCSats:r.totalBalance,totalUTXOs:r.totalUTXOs,authorizedSpendsSats:r.authorizedBalance,authorizedSpendsHash:r.authorizedSpendsHash,availableBTCSats:r.availableBalance,availableUTXOs:r.availableUTXOs,btcPriceUsd:i,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:n.termDurationDays,termLengthDays:n.termLengthDays,isExpired:n.isExpired,daysUntilExpiry:n.daysUntilExpiry,daysIntoGracePeriod:n.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:o,originationFeeBps:s.originationFeeBps,extensionFeeBps:s.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=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress,!0),i=await this.config.priceOracle.getBTCPrice(),a=T({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[o,s]=await Promise.all([a.getLiquidationThreshold(),a.getTermFees(e.selectedTerm)]),n=h(e.termStartTimestamp,e.selectedTerm),c=p(n.isExpired,n.daysIntoGracePeriod),l=g(r.availableBalance,i,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;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,totalBTCSats:r.totalBalance,totalUTXOs:r.totalUTXOs,authorizedSpendsSats:r.authorizedBalance,authorizedSpendsHash:r.authorizedSpendsHash,availableBTCSats:r.availableBalance,availableUTXOs:r.availableUTXOs,btcPriceUsd:i,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:n.termDurationDays,termLengthDays:n.termLengthDays,isExpired:n.isExpired,daysUntilExpiry:n.daysUntilExpiry,daysIntoGracePeriod:n.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:o,originationFeeBps:s.originationFeeBps,extensionFeeBps:s.extensionFeeBps,timestamp:Date.now()}}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 i=new ethers.providers.StaticJsonRpcProvider(r,{name:"any",chainId:this.config.chainId}),a=new ethers.Contract(this.config.contractAddress,[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],i),o=await a.core(),n=new ethers.Contract(o,[{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:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],i),c=await n.getPositionDetails(e),l=Number(c.status);if(l<0||l>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:",l,"(expected 0-7)"),console.error(" Full position:",JSON.stringify(c,null,2)),new Error(`Invalid position status decoded from contract: ${l} (expected 0-7). This indicates an ABI decoding issue. Position: ${e}`);const u=Number(c.expiryAt),d=Number(c.selectedTerm),h=(g=d,0===(p=u)?0:p-30*g*86400);var p,g;return{positionId:c.positionId,pkpId:c.pkpId,borrower:c.borrower,vaultAddress:this.parseVaultAddress(c.vaultAddress,s(this.config.chain)),ucdDebt:BigInt(c.ucdDebt.toString()),termStartTimestamp:h,selectedTerm:d,status:l,expiryAt:u}}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 a[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(e,r,i){const a=await this.getVaultSnapshot(e);return function(e,r,i,a){const o=r+i;return 0n===o||Number(e*t*10000n/o)>=a}(a.collateralValueUsd,a.ucdDebt,r,i)}};var A=1000000000000n,v=100000000000000n,$=[{inputs:[{internalType:"bytes32",name:"positionId",type:"bytes32"},{internalType:"uint256",name:"btcPrice",type:"uint256"},{internalType:"uint256",name:"quantumTimestamp",type:"uint256"},{internalType:"bytes",name:"liquidationValidatorSignature",type:"bytes"}],name:"isLiquidatable",outputs:[{internalType:"bool",name:"",type:"bool"}],stateMutability:"nonpayable",type:"function"}];(async()=>{console.log("[Admin Liquidation Validator] ========================================"),console.log("[Admin Liquidation Validator] DEBUG: Received globalThis parameters:"),console.log("[Admin Liquidation Validator] globalThis.contractAddresses:",globalThis.contractAddresses),console.log("[Admin Liquidation Validator] globalThis.contractAddresses:",globalThis.contractAddresses),console.log("[Admin Liquidation Validator] globalThis.positionId:",globalThis.positionId),console.log("[Admin Liquidation Validator] ========================================");let t,e,i,a,o="0";try{o="0a",console.log("[Step 0a] Validating configuration...");const c=globalThis.chain,l=globalThis.bitcoinProviderUrl,u=globalThis.positionId;if(!c)throw new Error('Missing required parameter: "chain"');if(!l)throw new Error('Missing required parameter: "bitcoinProviderUrl"');if(!u)throw new Error('Missing required parameter: "positionId"');let h,p,g;o="0b",p=c,g="sepolia"===c?"testnet":"regtest",h={name:"Custom Provider",url:l,minConfirmations:1,network:g},o="0c";const b=globalThis.contractAddresses;if(!b||"object"!=typeof b)throw new Error("Missing or invalid 'contractAddresses' parameter");if(e=b.PositionManager,i=b.LoanOperationsManagerModule,a=b.LiquidationManager,!e||!i)throw new Error("Missing one or more required contract addresses (PositionManager, LoanOperationsManagerModule)");t=new ethers.providers.JsonRpcProvider(globalThis.rpcUrl||Lit.Actions.getRpcUrl());const T=new f({provider:h,priceProviders:globalThis.priceProviders}),P=new d({provider:h}),E=(s={contractAddress:i,chain:c,chainId:n(p),rpcUrl:globalThis.rpcUrl,bitcoinProvider:P,minConfirmations:1},new y(s)),U=function(t){return new S(t)}({contractAddress:e,termManagerAddress:b.TermManagerModule,loanOpsManagerAddress:i,chain:c,chainId:n(p),rpcUrl:globalThis.rpcUrl,vaultBalance:E,priceOracle:T});o="1",console.log("[Step 1] Getting vault snapshot...");const I=await U.getVaultSnapshot(u);if(I.btcPriceUsd<A||I.btcPriceUsd>v)throw new Error(`Bitcoin price ${I.btcPriceUsd} is outside acceptable range (1000000000000 - 100000000000000). This may indicate oracle manipulation or stale price data. Please try again later.`);console.log(` \u2705 BTC price validation passed: $${(Number(I.btcPriceUsd)/1e8).toFixed(2)}`),o="2",console.log("[Step 2] Validating position state...");if(!["ACTIVE","EXPIRED","LIQUIDATABLE"].includes(I.status))throw new Error(`Invalid position status for liquidation: ${r(I.status)}. Must be ACTIVE, EXPIRED, or LIQUIDATABLE.`);if(console.log(` \u2705 Position status valid for liquidation: ${r(I.status)}`),o="3",console.log("[Step 3] Checking liquidation eligibility..."),!I.isLiquidatable)throw new Error(`Position is not liquidatable (${m(I.collateralRatioBps)}% >= ${m(I.currentLiquidationThreshold)}%) - liquidation rejected.`);console.log(" \u2705 Position is liquidatable"),console.log(` - Current ratio: ${m(I.collateralRatioBps)}%`),console.log(` - Liquidation threshold: ${m(I.currentLiquidationThreshold)}%`),o="4",console.log("[Step 4] Building authorization message...");const B=w()+10,C=ethers.utils.defaultAbiCoder.encode(["bytes32","uint256","uint256"],[u,B,I.btcPriceUsd.toString()]),x=ethers.utils.defaultAbiCoder.encode(["bytes32","uint256","uint256","uint256"],[u,B,I.btcPriceUsd.toString(),I.availableBTCSats.toString()]),O=ethers.utils.keccak256(C),D=ethers.utils.keccak256(x),L=globalThis.publicKey;o="5",console.log("[Step 5] Signing authorization...");const k=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(O),publicKey:L,sigName:"adminLiquidationAuth"}),M=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(D),publicKey:L,sigName:"adminLiquidationBonusAuth"});let q=null;if(globalThis.callIsLiquidatable&&a){o="6",console.log("[Step 6] Calling isLiquidatable() on contract...");try{new ethers.Contract(a,$,t);console.log(" \u26a0\ufe0f Skipping on-chain isLiquidatable() call (circular dependency with signature)"),console.log(" \u2139\ufe0f On-chain validation will occur in PositionManager.liquidatePosition()")}catch(t){console.warn(` \u26a0\ufe0f Failed to call isLiquidatable(): ${t.message}`)}}console.log("[Admin Liquidation Validator] \u2705 Complete"),Lit.Actions.setResponse({response:JSON.stringify({approved:!0,positionId:u,btcPrice:I.btcPriceUsd.toString(),btcAmountSats:I.availableBTCSats.toString(),signature:k,signatureBonus:M,timestamp:B,validatorPkp:L,isLiquidatable:I.isLiquidatable,isLiquidatableOnChain:q})})}catch(t){console.error("[Admin Liquidation Validator] \u274c Failed:",t.message),console.error(`[Admin Liquidation Validator] Failed at step: ${o}`),Lit.Actions.setResponse({response:JSON.stringify({approved:!1,reason:t.message||t.toString(),failedStep:o,positionId:globalThis.positionId,timestamp:Date.now()})})}var s})()})();
@@ -0,0 +1 @@
1
+ aaaa731b7ad39489824dc8fca1243247d6b6e6ae49c5ad68ec728d4ca916f5f9
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "admin-liquidation-validator",
3
+ "originalSize": 85965,
4
+ "minifiedSize": 36197,
5
+ "compressionRatio": 0.5789332868027686,
6
+ "hash": "aaaa731b7ad39489824dc8fca1243247d6b6e6ae49c5ad68ec728d4ca916f5f9",
7
+ "buildTime": 1768236993988,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{function t(t){const e=function(t){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";let n=0n;for(let s=0;s<t.length;s++){const i=e.indexOf(t[s]);if(i<0)throw new Error("Invalid Base58 character");n=58n*n+BigInt(i)}let s=n.toString(16);s.length%2&&(s="0"+s);for(let e=0;e<t.length&&"1"===t[e];e++)s="00"+s;return s}(t);return"76a914"+e.slice(2,e.length-8)+"88ac"}function e(t,e){const n=t.toString(16).padStart(2*e,"0").match(/../g);if(!n)throw new Error("Invalid conversion");return n.reverse().join("")}function n(t){return t<253?t.toString(16).padStart(2,"0"):t<=65535?"fd"+e(t,2):t<=4294967295?"fe"+e(t,4):"ff"+e(t,8)}(async()=>{try{const s=globalThis.params;if(!s)throw new Error("Missing params");const{amount:i,fee:r,destination:o,utxo:a,network:c,pkpPublicKey:pkpPublicKey}=s;if(!(i&&r&&o&&a&&c&&pkpPublicKey))throw new Error("Missing required parameters: amount, fee, destination, utxo, network, pkpPublicKey");if(!a.txid||void 0===a.vout||!a.value||!a.scriptPubKey)throw new Error("Invalid UTXO: must have txid, vout, value, scriptPubKey");const u=i+r;if(a.value<u)throw new Error(`Insufficient UTXO value: have ${a.value}, need ${u}`);const l=a.value-u,h=await async function(pkpPublicKey,t){const e=pkpPublicKey.startsWith("0x")?pkpPublicKey.slice(2):pkpPublicKey;if(130!==e.length)throw new Error(`Invalid public key length: ${e.length}, expected 130`);const n=e.slice(2,66),s=e.slice(66,130),i=BigInt("0x"+s),r=(i%2n===0n?"02":"03")+n,o=ethers.utils.sha256("0x"+r),a=ethers.utils.ripemd160(o);let c;c="mainnet"===t?"00":"6f";const u=c+a.slice(2),l=ethers.utils.sha256("0x"+u),h=ethers.utils.sha256(l).slice(2,10);return function(t){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";let n=BigInt("0x"+t),s="";for(;n>0n;){s=e[Number(n%58n)]+s,n/=58n}for(let e=0;e<t.length&&"00"===t.substr(e,2);e+=2)s="1"+s;return s}(u+h)}(pkpPublicKey,c),{txHex:f,sigHash:g}=function(s){const{utxo:i,destination:r,amount:o,changeAddress:a,changeAmount:c,pkpPublicKey:pkpPublicKey}=s,u="02000000",l="01",h=function(t){const e=(t.startsWith("0x")?t.slice(2):t).match(/../g);if(!e)throw new Error("Invalid hex");return e.reverse().join("")}(i.txid),f=e(i.vout,4),g="ffffffff",p=i.scriptPubKey.startsWith("0x")?i.scriptPubKey.slice(2):i.scriptPubKey,d=n(p.length/2),x=[],v=t(r),w=e(o,8),m=n(v.length/2);if(x.push(w+m+v),c>0){const s=t(a),i=e(c,8),r=n(s.length/2);x.push(i+r+s)}const y=n(x.length),b=x.join(""),K="00000000",k=u+l+h+f+d+p+g+y+b+K+"01000000",A=ethers.utils.sha256("0x"+k),P=ethers.utils.sha256(A);return{txHex:u+l+h+f+"00"+g+y+b+K,sigHash:P}}({utxo:a,destination:o,amount:i,changeAddress:h,changeAmount:l,network:c,pkpPublicKey:pkpPublicKey}),p=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(g),publicKey:"0x"+pkpPublicKey,sigName:"btcTxSig"});Lit.Actions.setResponse({response:JSON.stringify({success:!0,unsignedTxHex:f,sigHash:g,signature:p,pkpPublicKey:pkpPublicKey,pkpBtcAddress:h,destination:o,amount:i,fee:r,changeAmount:l,utxo:{txid:a.txid,vout:a.vout,value:a.value},timestamp:Date.now(),action:"always-signer"})})}catch(t){Lit.Actions.setResponse({response:JSON.stringify({success:!1,error:t.message||t.toString(),timestamp:Date.now()})})}})()})();
@@ -0,0 +1 @@
1
+ 16fec6721b33573a80939be326501e0ff5dddd6d6521eb4dec606d3d6afaf946
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "always-signer",
3
+ "originalSize": 7202,
4
+ "minifiedSize": 3234,
5
+ "compressionRatio": 0.5509580672035546,
6
+ "hash": "16fec6721b33573a80939be326501e0ff5dddd6d6521eb4dec606d3d6afaf946",
7
+ "buildTime": 1768236994028,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=void(async()=>{try{const s=globalThis.messageToSign||"diamond-hands-test-signature-b";if(!globalThis.publicKey)return Lit.Actions.setResponse({response:JSON.stringify({success:!1,error:"PKP public key not available"})}),void 0;const e=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.toUtf8Bytes(s))),publicKey:"0x"+globalThis.publicKey,sigName:"dummySig"});Lit.Actions.setResponse({response:JSON.stringify({success:!0,signature:e,message:s,pkpPublicKey:globalThis.publicKey,timestamp:Date.now(),action:"dummy-authorization-sign-b"})})}catch(s){Lit.Actions.setResponse({response:JSON.stringify({success:!1,error:s.message||s.toString(),timestamp:Date.now()})})}})();
@@ -0,0 +1 @@
1
+ fdfc89c557ef3cb5e9cf81eef854a80add513696875f9ce1bae7fb96988b09ce
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "authorization-dummy-b",
3
+ "originalSize": 1313,
4
+ "minifiedSize": 728,
5
+ "compressionRatio": 0.4455445544554455,
6
+ "hash": "fdfc89c557ef3cb5e9cf81eef854a80add513696875f9ce1bae7fb96988b09ce",
7
+ "buildTime": 1768236994034,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=void(async()=>{try{if(!globalThis.publicKey)return Lit.Actions.setResponse({response:JSON.stringify({success:!1,error:"PKP public key not available"})}),void 0;const s=globalThis.toSign,e=globalThis.messageToSign||"diamond-hands-test-signature";let i,t;if(s){if("string"==typeof s)i=ethers.utils.arrayify(s);else if(s instanceof Uint8Array)i=s;else{if(!Array.isArray(s))throw new Error("Invalid toSign format");i=new Uint8Array(s)}t="bitcoin-raw-bytes"}else i=ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e))),t="ethereum-message-hash";const a=await Lit.Actions.signEcdsa({toSign:i,publicKey:"0x"+globalThis.publicKey,sigName:"dummySig"});Lit.Actions.setResponse({response:JSON.stringify({success:!0,signature:a,signatureType:t,message:e,rawDataSigned:!!s,pkpPublicKey:globalThis.publicKey,timestamp:Date.now(),action:"dummy-authorization-sign"})})}catch(s){Lit.Actions.setResponse({response:JSON.stringify({success:!1,error:s.message||s.toString(),timestamp:Date.now()})})}})();
@@ -0,0 +1 @@
1
+ 650ea6a8889dfc3366038e7f503cf879ebeb17805256ee42a754b622678998bf
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "authorization-dummy",
3
+ "originalSize": 2037,
4
+ "minifiedSize": 1021,
5
+ "compressionRatio": 0.498772704958272,
6
+ "hash": "650ea6a8889dfc3366038e7f503cf879ebeb17805256ee42a754b622678998bf",
7
+ "buildTime": 1768236994047,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{var t=Object.defineProperty,e=Object.getOwnPropertyNames,o={};async function n(pkpPublicKey,t){const e=pkpPublicKey.startsWith("0x")?pkpPublicKey.slice(2):pkpPublicKey;if(130!==e.length)throw new Error(`Invalid public key length: ${e.length}, expected 130 (uncompressed format)`);if(!e.startsWith("04"))throw new Error(`Invalid public key prefix: expected 04 (uncompressed), got ${e.substring(0,2)}`);const o=e.slice(2,66),n=e.slice(66,130),i=(BigInt("0x"+n)%2n===0n?"02":"03")+o,r=ethers.utils.sha256("0x"+i);let a;a="mainnet"===t?"bc":"testnet"===t?"tb":"bcrt";const c=function(t,e,o){const n="qpzry9x8gf2tvdw0s3jn54khce6mua7l",i=[];for(let t=0;t<o.length;t+=2)i.push(parseInt(o.substr(t,2),16));const r=s(i,8,5,!0),a=[e,...r],c=function(t,e){const o=function(t){const e=[];for(let o=0;o<t.length;o++)e.push(t.charCodeAt(o)>>5);e.push(0);for(let o=0;o<t.length;o++)e.push(31&t.charCodeAt(o));return e}(t).concat(e).concat([0,0,0,0,0,0]),n=1^function(t){const e=[996825010,642813549,513874426,1027748829,705979059];let o=1;for(let n=0;n<t.length;n++){const i=o>>25;o=(33554431&o)<<5^t[n];for(let t=0;t<5;t++)i>>t&1&&(o^=e[t])}return o}(o),i=[];for(let t=0;t<6;t++)i.push(n>>5*(5-t)&31);return i}(t,a),u=[...a,...c];let l=t+"1";for(let t=0;t<u.length;t++)l+=n[u[t]];return l}(a,0,ethers.utils.ripemd160(r).slice(2));return c}async function i(pkpPublicKey,t){const e=pkpPublicKey.startsWith("0x")?pkpPublicKey.slice(2):pkpPublicKey;if(130!==e.length)throw new Error(`Invalid public key length: ${e.length}, expected 130 (uncompressed format)`);if(!e.startsWith("04"))throw new Error(`Invalid public key prefix: expected 04 (uncompressed), got ${e.substring(0,2)}`);const o=e.slice(2,66),n=e.slice(66,130),i=(BigInt("0x"+n)%2n===0n?"02":"03")+o,r=ethers.utils.sha256("0x"+i);let s;s="mainnet"===t?"00":"6f";const a=s+ethers.utils.ripemd160(r).slice(2),c=ethers.utils.sha256("0x"+a);return function(t){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";let o=BigInt("0x"+t),n="";for(;o>0n;){n=e[Number(o%58n)]+n,o/=58n}for(let e=0;e<t.length&&"00"===t.substr(e,2);e+=2)n="1"+n;return n}(a+ethers.utils.sha256(c).slice(2,10))}function r(t){if(/^(bc1|tb1|bcrt1)[0-9a-zA-Z]{11,87}$/.test(t)){const e=function(t){const e="qpzry9x8gf2tvdw0s3jn54khce6mua7l",o=t.lastIndexOf("1");if(o<1)throw new Error("No separator character for bech32");t.substring(0,o).toLowerCase();const n=t.substring(o+1).toLowerCase(),i=[];for(let t=0;t<n.length;t++){const o=e.indexOf(n[t]);if(-1===o)throw new Error(`Invalid bech32 character: ${n[t]}`);i.push(o)}const r=i.slice(0,-6),a=(r[0],s(r.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 o=0n;for(let n=0;n<t.length;n++){const i=e.indexOf(t[n]);if(i<0)throw new Error(`Invalid Base58 character: ${t[n]}`);o=58n*o+BigInt(i)}let n=o.toString(16);n.length%2&&(n="0"+n);for(let e=0;e<t.length&&"1"===t[e];e++)n="00"+n;return n}(t);return"76a914"+e.slice(2,e.length-8)+"88ac"}}function s(t,e,o,n){let i=0,r=0;const s=[],a=(1<<o)-1;for(let n=0;n<t.length;n++){const c=t[n];if(c<0||c>>e)throw new Error("Invalid data for bit conversion");for(i=i<<e|c,r+=e;r>=o;)r-=o,s.push(i>>r&a)}if(n)r>0&&s.push(i<<o-r&a);else if(r>=e||i<<o-r&a)throw new Error("Invalid padding in bit conversion");return s}((e,o)=>{for(var n in o)t(e,n,{get:o[n],enumerable:!0})})(o,{addressToScriptPubKey:()=>r,deriveBitcoinAddress:()=>n,deriveBitcoinAddressP2PKH:()=>i});var a,c,u=(a={"src/modules/bitcoin/address.ts"(){"use strict"}},function(){return a&&(c=(0,a[e(a)[0]])(a=0)),c});function l(t){const e=(t.startsWith("0x")?t.slice(2):t).match(/../g);if(!e)throw new Error(`Invalid hex string: ${t}`);return e.reverse().join("")}function d(t,e){const o=t.toString(16).padStart(2*e,"0").match(/../g);if(!o)throw new Error(`Invalid hex conversion: ${t}`);return o.reverse().join("")}function h(t){return t<253?t.toString(16).padStart(2,"0"):t<=65535?"fd"+d(t,2):t<=4294967295?"fe"+d(t,4):"ff"+d(t,8)}u(),u();var g=100;function m(t){return Math.floor(t/g)*g}async function p(){const t=Math.floor(Date.now()/1e3),e=t%g;if(!function(t){const e=(t??Math.floor(Date.now()/1e3))%g;return e>=92||e<8}(t))return;let o;o=e>=92?100-e+8:8-e,console.log(`[Quantum] Waiting ${o}s for safe moment (current time in quantum: ${e}s)`),await new Promise((t=>setTimeout(t,1e3*o))),console.log(`[Quantum] Safe quantum ready: ${m(Math.floor(Date.now()/1e3))}`)}0,0;var f=class{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");if(!t.mode)throw new Error("auth.mode is required");if("dev"!==t.mode&&"prod"!==t.mode)throw new Error(`auth.mode must be "dev" or "prod", got: ${t.mode}`);return t}static async recoverSigner(t,e){try{const o=ethers.utils.arrayify(t),n=ethers.utils.hashMessage(o);return ethers.utils.recoverAddress(n,e)}catch(t){throw console.error("[Authorization] Signature recovery failed:",t),new Error(`Failed to recover signer: ${t instanceof Error?t.message:String(t)}`)}}static async verifyActionAuthorization(t,e,o){await p();try{!function(t,e){const o=m(e??Math.floor(Date.now()/1e3)),n=m(t);if(n!==o)throw new Error(`[Quantum] Invalid timestamp: signature from quantum ${n}, current quantum is ${o}`)}(t.timestamp)}catch(t){return console.error("[Authorization] Quantum timestamp validation failed:",t),!1}if(!t.mode)return console.error("[Authorization] Missing mode field"),!1;if("dev"!==t.mode&&"prod"!==t.mode)return console.error(`[Authorization] Invalid mode: ${t.mode}, must be "dev" or "prod"`),!1;if("prod"===t.mode)return console.error('[Authorization] Production mode not yet supported - use mode="dev"'),!1;const n=e();console.log("[Authorization] Message hash:",n),console.log("[Authorization] Recovering signer from signature..."),console.log(" signature:",t.signature);const i=await this.recoverSigner(n,t.signature);console.log(" recoveredAddress:",i),console.log("[Authorization] Checking authorization:"),console.log(" recoveredAddress:",i),console.log(" recoveredAddress (lowercase):",i.toLowerCase());const r=o(i);return console.log(" match:",r?"\u2705 YES":"\u274c NO"),r||console.error("[Authorization] Not authorized:",`\n Recovered: ${i}`),r}static async verifyAmountPositionActionAuthorization(t,e){return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action)),o="string"==typeof t.amount?BigInt(t.amount):t.amount;console.log("[Authorization] Building message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" amount (string):",t.amount),console.log(" amount (BigInt):",o.toString()),console.log(" action:",t.action),console.log(" actionHash:",e);const n=[t.positionId,t.timestamp,t.chainId,o.toString(),e],i=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],n);return console.log(" messageHash (solidityKeccak256):",i),i}),(t=>t.toLowerCase()===e.toLowerCase()))}static async verifyMintAuthorization(t,e){return"mint-ucd"!==t.action?(console.error(`[Authorization] Invalid action for mint: expected "mint-ucd", got "${t.action}"`),!1):this.verifyAmountPositionActionAuthorization(t,e)}static async verifyWithdrawAuthorization(t,e){if("withdraw-btc"!==t.action)return console.error(`[Authorization] Invalid action for withdrawal: expected "withdraw-btc", got "${t.action}"`),!1;return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action)),o="string"==typeof t.amount?BigInt(t.amount):t.amount;console.log("[Authorization] Building withdrawal message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" amount (string):",t.amount),console.log(" amount (BigInt):",o.toString()),console.log(" destinationAddress:",t.destinationAddress),console.log(" action:",t.action),console.log(" actionHash:",e);const n=[t.positionId,t.timestamp,t.chainId,o.toString(),t.destinationAddress,e],i=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","string","bytes32"],n);return console.log(" messageHash (solidityKeccak256):",i),i}),(t=>t.toLowerCase()===e.toLowerCase()))}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");if(!t.mode)throw new Error("auth.mode is required");if("dev"!==t.mode&&"prod"!==t.mode)throw new Error(`auth.mode must be "dev" or "prod", got: ${t.mode}`);return t}static async verifyTermPositionActionAuthorization(t,e){return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action));console.log("[Authorization] Building term message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" newTerm:",t.newTerm),console.log(" action:",t.action),console.log(" actionHash:",e);const o=[t.positionId,t.timestamp,t.chainId,t.newTerm,e],n=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],o);return console.log(" messageHash (solidityKeccak256):",n),n}),(t=>t.toLowerCase()===e.toLowerCase()))}static async verifyExtendAuthorization(t,e){return"extend-position"!==t.action?(console.error(`[Authorization] Invalid action for extension: expected "extend-position", got "${t.action}"`),!1):this.verifyTermPositionActionAuthorization(t,e)}static async verifyBtcExecutionAuthorization(t,e){if("execute-btc-withdrawal"!==t.action)return console.error(`[Authorization] Invalid action for BTC execution: expected "execute-btc-withdrawal", got "${t.action}"`),!1;return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action));console.log("[Authorization] Building BTC execution message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" utxoIdentifier:",t.utxoIdentifier),console.log(" networkFee:",t.networkFee),console.log(" action:",t.action),console.log(" actionHash:",e);const o=[t.positionId,t.timestamp,t.chainId,t.utxoIdentifier,t.networkFee,e],n=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","string","uint256","bytes32"],o);return console.log(" messageHash (solidityKeccak256):",n),n}),(t=>t.toLowerCase()===e.toLowerCase()))}};(async()=>{const t=1e5;console.log("[Step 0] Validating configuration...");const e=globalThis.chain,i=globalThis.bitcoinNetwork;if(!e)throw new Error('Missing required parameter: "chain". Must be "sepolia" or "ethereum"');if(!i)throw new Error('Missing required parameter: "bitcoinNetwork". Must be "mainnet", "testnet", or "regtest"');if(!["mainnet","testnet","regtest"].includes(i))throw new Error(`Invalid bitcoinNetwork: "${i}". Must be "mainnet", "testnet", or "regtest"`);const s=globalThis.auth;if(!s||"object"!=typeof s)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!s.positionId)throw new Error("auth.positionId is required");if(!s.utxo||"object"!=typeof s.utxo)throw new Error("auth.utxo is required and must be an object");if(void 0===s.networkFee||s.networkFee<0)throw new Error("auth.networkFee is required and must be non-negative");if(!s.utxo.txid||void 0===s.utxo.vout||!s.utxo.value||!s.utxo.scriptPubKey)throw new Error("Invalid UTXO: must have txid, vout, value, scriptPubKey");const a=s.mode||"prod",c=s.positionId,g=s.utxo,m=s.networkFee;console.log(" \u2705 Parameters validated"),console.log(` Position: ${c}`),console.log(` Network fee: ${m} sats`),console.log(` Bitcoin network: ${i}`);const p=globalThis.publicKey;if(!p)throw new Error('Missing required parameter: "publicKey"');if("string"!=typeof p||0===p.length)throw new Error("Invalid publicKey: must be non-empty string");const w=p;let y,b;if("dev"===a){if(!globalThis.contractAddresses||!globalThis.contractAddresses.LoanOperationsManagerModule)throw new Error("Dev mode requires contractAddresses.LoanOperationsManagerModule address");if(!globalThis.contractAddresses||!globalThis.contractAddresses.PositionManager)throw new Error("Dev mode requires contractAddresses.PositionManager address");y=globalThis.contractAddresses.LoanOperationsManagerModule,b=globalThis.contractAddresses.PositionManager,console.log(" \u2705 Contract addresses loaded from parameters (dev mode)")}else{if(y="0x0000000000000000000000000000000000000000",b="0x0000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000"===y)throw new Error('Production addresses not set. Use mode="dev" for testing.');console.log(" \u2705 Contract addresses loaded from constants (prod mode)")}let v="0.5";try{console.log("[BTC Transaction Signer] Started"),console.log(` Position: ${c}`),console.log(` UTXO: ${g.txid}:${g.vout} (${g.value} sats)`),v="1",console.log("[Step 1] Setting up RPC connection...");const a=globalThis.customRpcUrl;let p;a&&"string"==typeof a?(console.log(` \u2705 Using custom RPC URL: ${a}`),p=a):(console.log(" \u26a0\ufe0f No custom RPC URL provided, falling back to Lit.Actions.getRpcUrl()"),console.log(" \u26a0\ufe0f This may timeout or fail if Lit RPC service is slow/unavailable"),p=await Lit.Actions.getRpcUrl({chain:e}),console.log(` Using LIT-provided RPC URL for chain: ${e}`));const A=new ethers.providers.JsonRpcProvider(p);v="2",console.log("[Step 2] UTXO existence check..."),console.log(` UTXO: ${g.txid}:${g.vout}`),console.log(" \u26a0\ufe0f UTXO existence check deferred to Bitcoin consensus"),console.log(" \u2705 Proceeding (Bitcoin will reject if spent)"),v="3",console.log("[Step 3] Querying authorization from contract...");const x=[{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"}],I=new ethers.Contract(y,x,A),T=await I.getAuthorizedSpends(c);let E=null;for(const t of T){const e=t.txid.toLowerCase(),o=g.txid.toLowerCase(),n=Number(t.vout),i=Number(g.vout);if(e===o&&n===i){E=t;break}}if(!E)throw new Error(`No authorization found for UTXO ${g.txid}:${g.vout}`);const $=BigInt(E.targetAmount.toString()),P=E.targetAddress,S=BigInt(E.authorizedAt.toString());console.log(" \u2705 Authorization found on-chain"),console.log(` Target amount: ${$.toString()} sats`),console.log(` Destination: ${P}`),console.log(` Authorized at: ${S.toString()}`),v="3.5",console.log("[Step 3.5] Verifying owner signature for network fee...");const k=[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],z=new ethers.Contract(b,k,A),B=await z.core();console.log(` Core facet address: ${B}`);const C=[{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:"uint8",name:"status",type:"uint8"}],internalType:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],M=new ethers.Contract(B,C,A),K=(await M.getPositionDetails(c)).borrower;console.log(` Position owner: ${K}`);const L=`${g.txid}:${g.vout}`;if(!await f.verifyBtcExecutionAuthorization({positionId:s.positionId,timestamp:s.timestamp,chainId:s.chainId,utxoIdentifier:L,networkFee:s.networkFee,action:"execute-btc-withdrawal",signature:s.signature,mode:s.mode},K))throw new Error("Caller not authorized for execution");if(console.log(" \u2705 Owner signature verified"),v="4",console.log("[Step 4] Validating network fee..."),m>t)throw new Error(`Network fee ${m} sats exceeds maximum 100000 sats`);if(m<100)throw new Error(`Network fee ${m} sats below minimum 100 sats`);if(BigInt(m)>=$)throw new Error(`Network fee ${m} sats cannot exceed target amount ${$.toString()} sats`);const U=Number($)-m;console.log(" \u2705 Network fee validated"),console.log(` User receives: ${U} sats`),v="5",console.log("[Step 5] Deriving PKP Bitcoin address...");const O=await n(w,i);console.log(` \u2705 PKP BTC address: ${O}`),v="6",console.log("[Step 6] Validating UTXO ownership...");const q=g.scriptPubKey.startsWith("0x")?g.scriptPubKey.slice(2):g.scriptPubKey,{addressToScriptPubKey:N}=await Promise.resolve().then((()=>(u(),o)));if(q!==N(O))throw new Error(`UTXO does not belong to vault. Expected PKP address ${O}, got scriptPubKey ${q}`);console.log(" \u2705 UTXO belongs to vault (PKP address)"),v="7",console.log("[Step 7] Validating UTXO value...");const j=Number($);if(g.value<j)throw new Error(`Insufficient UTXO value: have ${g.value} sats, need ${j} sats`);const R=g.value-j;console.log(" \u2705 UTXO value sufficient"),console.log(` UTXO value: ${g.value} sats`),console.log(` Total required: ${j} sats`),console.log(` Change: ${R} sats`),v="8",console.log("[Step 8] Building Bitcoin transaction...");const{txHex:H,sigHash:D}=function(t){const{utxo:e,destination:o,amount:n,changeAddress:i,changeAmount:s,pkpPublicKey:pkpPublicKey}=t,a="02000000",c=l(e.txid),u=d(e.vout,4),g="ffffffff",m=[],p=r(o),f=d(n,8),w=h(p.length/2);if(m.push(f+w+p),s>0){const t=r(i),e=d(s,8),o=h(t.length/2);m.push(e+o+t)}const y=h(m.length),b=m.join(""),v="00000000",A=a,x=l(e.txid)+u,I=ethers.utils.sha256(ethers.utils.sha256("0x"+x)),T=ethers.utils.sha256(ethers.utils.sha256("0x"+g)),E=x,$="1976a914"+(e.scriptPubKey.startsWith("0x")?e.scriptPubKey.slice(2):e.scriptPubKey).slice(4)+"88ac",P=d(e.value,8),S=g,k=ethers.utils.sha256(ethers.utils.sha256("0x"+b)),z=v,B=A+I.slice(2)+T.slice(2)+E+$+P+S+k.slice(2)+z+"01000000",C=ethers.utils.sha256("0x"+B);return{txHex:a+"01"+c+u+"00"+g+y+b+v,sigHash:ethers.utils.sha256(C)}}({utxo:g,destination:P,amount:U,changeAddress:O,changeAmount:R,network:i,pkpPublicKey:w});console.log(" \u2705 Transaction built"),console.log(` Signature hash: ${D}`),v="9",console.log("[Step 9] Signing Bitcoin transaction...");const F=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(D),publicKey:"0x"+w,sigName:"btcTxSig"});console.log("[BTC Transaction Signer] \u2705 Complete"),Lit.Actions.setResponse({response:JSON.stringify({success:!0,unsignedTxHex:H,sigHash:D,signature:F,pkpPublicKey:w,pkpBtcAddress:O,destination:P,userReceivesAmount:U,networkFee:m,changeAmount:R,utxo:{txid:g.txid,vout:g.vout,value:g.value},authorization:{targetAmount:$.toString(),authorizedAt:S.toString()},timestamp:Date.now(),action:"btc-transaction-signer"})})}catch(t){console.error("[BTC Transaction Signer] \u274c Failed:",t.message),console.error(`[BTC Transaction Signer] Failed at step: ${v}`),Lit.Actions.setResponse({response:JSON.stringify({success:!1,error:t.message||t.toString(),failedStep:v,positionId:s?.positionId,timestamp:Date.now()})})}})()})();
@@ -0,0 +1 @@
1
+ dbf23063b3c267c3e104958c0072c9d26f1e2f8bcc46a0726cbfff300fa3fb0c
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "btc-transaction-signer",
3
+ "originalSize": 46495,
4
+ "minifiedSize": 20804,
5
+ "compressionRatio": 0.5525540380686096,
6
+ "hash": "dbf23063b3c267c3e104958c0072c9d26f1e2f8bcc46a0726cbfff300fa3fb0c",
7
+ "buildTime": 1768236994233,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{var t=100000000n,e=1000000000000000000n,o=100;function r(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 i=(t=>(t.SEPOLIA="sepolia",t.ETHEREUM="ethereum",t.HARDHAT="hardhat",t))(i||{}),a=(t=>(t.TESTNET="testnet",t.MAINNET="mainnet",t.REGTEST="regtest",t))(a||{}),n={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},s={ethereum:1,sepolia:11155111,hardhat:31337},c={regtest:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://127.0.0.1:18443",network:"regtest",minConfirmations:1,name:"Local Regtest"},{url:"https://313561dc30c5.ngrok-free.app",network:"regtest",minConfirmations:1,name:"Ngrok Bitcoin Tunnel"}],testnet:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"}],mainnet:[{url:"https://blockstream.info/api",network:"mainnet",minConfirmations:6,name:"Blockstream Mainnet"},{url:"https://mempool.space/api",network:"mainnet",minConfirmations:6,name:"Mempool.space Mainnet"}]};function l(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(i).join(", ")}`)}function u(t){return n[t]}function d(t){return s[t]}function h(t){return Math.floor(t/o)*o}async function g(){const t=Math.floor(Date.now()/1e3),e=t%o;if(!function(t){const e=(t??Math.floor(Date.now()/1e3))%o;return e>=92||e<8}(t))return;let r;r=e>=92?100-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: ${h(Math.floor(Date.now()/1e3))}`)}0,0;var m=class{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");if(!t.mode)throw new Error("auth.mode is required");if("dev"!==t.mode&&"prod"!==t.mode)throw new Error(`auth.mode must be "dev" or "prod", got: ${t.mode}`);return t}static async recoverSigner(t,e){try{const o=ethers.utils.arrayify(t),r=ethers.utils.hashMessage(o);return ethers.utils.recoverAddress(r,e)}catch(t){throw console.error("[Authorization] Signature recovery failed:",t),new Error(`Failed to recover signer: ${t instanceof Error?t.message:String(t)}`)}}static async verifyActionAuthorization(t,e,o){await g();try{!function(t,e){const o=h(e??Math.floor(Date.now()/1e3)),r=h(t);if(r!==o)throw new Error(`[Quantum] Invalid timestamp: signature from quantum ${r}, current quantum is ${o}`)}(t.timestamp)}catch(t){return console.error("[Authorization] Quantum timestamp validation failed:",t),!1}if(!t.mode)return console.error("[Authorization] Missing mode field"),!1;if("dev"!==t.mode&&"prod"!==t.mode)return console.error(`[Authorization] Invalid mode: ${t.mode}, must be "dev" or "prod"`),!1;if("prod"===t.mode)return console.error('[Authorization] Production mode not yet supported - use mode="dev"'),!1;const r=e();console.log("[Authorization] Message hash:",r),console.log("[Authorization] Recovering signer from signature..."),console.log(" signature:",t.signature);const i=await this.recoverSigner(r,t.signature);console.log(" recoveredAddress:",i),console.log("[Authorization] Checking authorization:"),console.log(" recoveredAddress:",i),console.log(" recoveredAddress (lowercase):",i.toLowerCase());const a=o(i);return console.log(" match:",a?"\u2705 YES":"\u274c NO"),a||console.error("[Authorization] Not authorized:",`\n Recovered: ${i}`),a}static async verifyAmountPositionActionAuthorization(t,e){return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action)),o="string"==typeof t.amount?BigInt(t.amount):t.amount;console.log("[Authorization] Building message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" amount (string):",t.amount),console.log(" amount (BigInt):",o.toString()),console.log(" action:",t.action),console.log(" actionHash:",e);const r=[t.positionId,t.timestamp,t.chainId,o.toString(),e],i=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],r);return console.log(" messageHash (solidityKeccak256):",i),i}),(t=>t.toLowerCase()===e.toLowerCase()))}static async verifyMintAuthorization(t,e){return"mint-ucd"!==t.action?(console.error(`[Authorization] Invalid action for mint: expected "mint-ucd", got "${t.action}"`),!1):this.verifyAmountPositionActionAuthorization(t,e)}static async verifyWithdrawAuthorization(t,e){if("withdraw-btc"!==t.action)return console.error(`[Authorization] Invalid action for withdrawal: expected "withdraw-btc", got "${t.action}"`),!1;return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action)),o="string"==typeof t.amount?BigInt(t.amount):t.amount;console.log("[Authorization] Building withdrawal message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" amount (string):",t.amount),console.log(" amount (BigInt):",o.toString()),console.log(" destinationAddress:",t.destinationAddress),console.log(" action:",t.action),console.log(" actionHash:",e);const r=[t.positionId,t.timestamp,t.chainId,o.toString(),t.destinationAddress,e],i=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","string","bytes32"],r);return console.log(" messageHash (solidityKeccak256):",i),i}),(t=>t.toLowerCase()===e.toLowerCase()))}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");if(!t.mode)throw new Error("auth.mode is required");if("dev"!==t.mode&&"prod"!==t.mode)throw new Error(`auth.mode must be "dev" or "prod", got: ${t.mode}`);return t}static async verifyTermPositionActionAuthorization(t,e){return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action));console.log("[Authorization] Building term message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" newTerm:",t.newTerm),console.log(" action:",t.action),console.log(" actionHash:",e);const o=[t.positionId,t.timestamp,t.chainId,t.newTerm,e],r=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],o);return console.log(" messageHash (solidityKeccak256):",r),r}),(t=>t.toLowerCase()===e.toLowerCase()))}static async verifyExtendAuthorization(t,e){return"extend-position"!==t.action?(console.error(`[Authorization] Invalid action for extension: expected "extend-position", got "${t.action}"`),!1):this.verifyTermPositionActionAuthorization(t,e)}static async verifyBtcExecutionAuthorization(t,e){if("execute-btc-withdrawal"!==t.action)return console.error(`[Authorization] Invalid action for BTC execution: expected "execute-btc-withdrawal", got "${t.action}"`),!1;return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action));console.log("[Authorization] Building BTC execution message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" utxoIdentifier:",t.utxoIdentifier),console.log(" networkFee:",t.networkFee),console.log(" action:",t.action),console.log(" actionHash:",e);const o=[t.positionId,t.timestamp,t.chainId,t.utxoIdentifier,t.networkFee,e],r=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","string","uint256","bytes32"],o);return console.log(" messageHash (solidityKeccak256):",r),r}),(t=>t.toLowerCase()===e.toLowerCase()))}},p=class{stepStart(t,e){}stepEnd(t,e){}log(t,e){}warn(t,e){}error(t,e){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},w=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(),o=Math.random().toString(36).substring(2,9);this.executionId=`exec_${e}_${o}`,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 o=Date.now()-this.executionStartTime,r=this.TIMEOUT_MS-o;console.log(`[Step ${t}] ${e}...`),console.log(`[Step ${t}] Elapsed: ${o}ms | Remaining: ${r}ms`)}stepEnd(t,e=5e3){const o=this.stepStartTimes.get(t);if(!o)return console.warn(`[Step ${t}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const r=Date.now()-o;this.stepDurations.set(t,r),console.log(`[Step ${t}] Duration: ${r}ms`),r>e&&console.warn(`\u26a0\ufe0f [Step ${t}] Took ${r}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){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,o]of this.stepDurations.entries()){const r=(o/t*100).toFixed(1);console.log(`[${this.actionName}] Step ${e}: ${o}ms (${r}%)`)}}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}},f=class{static create(t="LIT Action",e){return e??globalThis.debugAction??!1?new w(t):new p}},y=class{constructor(t){this.config=t,this.timeout=t.timeout||15e3,this.logger=f.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 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),!o.ok)throw new Error(`Failed to fetch block height: ${o.status} ${o.statusText}`);const r=await o.text(),i=parseInt(r.trim(),10);if(isNaN(i))throw new Error(`Invalid block height response: ${r}`);return this.logger.stepEnd("0"),i}catch(t){if(clearTimeout(e),this.logger.stepEnd("0"),"AbortError"===t.name)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: ${t.message}`),this.config.fallbackProviders&&this.config.fallbackProviders.length>0){const t=[...this.config.fallbackProviders].sort(((t,e)=>t.priority-e.priority));for(const o of t)try{return this.logger.log("0",`Trying fallback: ${o.name} (${o.url})`),await this.fetchUTXOsFromProvider(o.url,e)}catch(t){this.logger.warn("0",`Fallback ${o.name} failed: ${t.message}`);continue}}throw new Error(`Failed to fetch UTXOs: ${t.message}`)}}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");return t.map((t=>{let o=0;return t.status&&"object"==typeof t.status?t.status.confirmed&&(o=void 0!==t.status.block_height&&t.status.block_height>0?e-t.status.block_height+1:1):void 0!==t.confirmations&&(o=t.confirmations),{txid:t.txid,vout:void 0!==t.vout?t.vout:t.n,satoshis:BigInt(void 0!==t.value?t.value:t.satoshis),confirmations:o}}))}async fetchUTXOsFromProvider(t,e){this.logger.stepStart("1","fetchUTXOsFromProvider");const o=`${t}/address/${e}/utxos`;this.logger.log("1",`Fetching UTXOs from ${o}`);const r=await this.getCurrentBlockHeight();this.logger.log("1",`Current block height: ${r}`);const i=new AbortController,a=setTimeout((()=>i.abort()),this.timeout);try{const t=await fetch(o,{signal:i.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(a),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();this.logger.log("1",`Raw API Response: ${JSON.stringify(e,null,2)}`);const n=this.parseBlockstreamUTXOs(e,r);return this.logger.stepEnd("1"),n}catch(t){if(clearTimeout(a),this.logger.stepEnd("1"),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOSet(t,e){let o=[],r=null;for(let e=1;e<=3;e++)try{this.logger.log("2",`UTXO query attempt ${e}/3...`),o=await this.getUTXOs(t),this.logger.log("2",`UTXO query succeeded on attempt ${e}`),r=null;break}catch(t){r=t,this.logger.error("2",`UTXO query failed on attempt ${e}: ${t.message}`),e<3&&(this.logger.log("2","Waiting 500ms before retry..."),await new Promise((t=>setTimeout(t,500))))}if(r)throw new Error(`Failed to query UTXOs after 3 attempts: ${r.message}`);const i=o.reduce(((t,e)=>t+e.satoshis),0n),a=o.filter((t=>t.confirmations>=e)).reduce(((t,e)=>t+e.satoshis),0n),n=i-a;return{utxos:o,totalBalance:i,totalUTXOs:o.length,confirmedBalance:a,unconfirmedBalance:n}}async getBalance(t){const e=this.stripNetworkPrefix(t),o=`${this.config.providerUrl}/address/${e}`;this.logger.log("3",`Fetching balance from ${o}`);const r=new AbortController,i=setTimeout((()=>r.abort()),this.timeout);try{const t=await fetch(o,{signal:r.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),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();if(!e.chain_stats)throw new Error("Invalid response: missing chain_stats");const a=e.chain_stats.funded_txo_sum,n=e.chain_stats.spent_txo_sum;if(void 0===a||void 0===n)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${a}, spent_txo_sum: ${n}`);const s=BigInt(a-n);return this.logger.log("3",`Balance: ${s.toString()} sats`),this.logger.log("3",` - Funded: ${a} sats, Spent: ${n} sats`),s}catch(t){if(clearTimeout(i),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);const o=t.message||"Unknown error";throw this.logger.log("3",`Balance fetch failed: ${o}`),new Error(`Failed to fetch Bitcoin balance for address ${e}: ${o}`)}}async getTransaction(t){if(this.config.rpcHelper)try{const e=this.config.rpcWallet||"",o=await this.config.rpcHelper.getTransaction(e,t);return{txid:o.txid,confirmations:o.confirmations||0}}catch(t){if(t.message?.includes("Invalid or non-wallet transaction")||-5===t.code)return null;throw t}const e=new AbortController,o=setTimeout((()=>e.abort()),this.timeout);try{const r=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(o),!r.ok){if(404===r.status)return null;throw new Error(`Bitcoin provider error: ${r.status} ${r.statusText}`)}const i=await r.json();return i.status?{txid:i.txid,confirmations:i.status.confirmed&&i.status.block_height?1:0}:null}catch(t){if(clearTimeout(o),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);if(t.message?.includes("404"))return null;throw t}}},b=class{constructor(t,e,o){this.mode=o,e&&Array.isArray(e)?this.sources=e:t&&Array.isArray(t)?this.sources=this.buildSources(t):this.sources=this.buildSources(),this.sources.sort(((t,e)=>t.priority-e.priority)),this.validateProviderCount()}validateProviderCount(){if("prod"===this.mode&&this.sources.length<3)throw new Error(`Production mode requires at least 3 price providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported providers: CoinGecko, Binance, Coinbase, CryptoCompare (with API key)`);if(0===this.sources.length)throw new Error("No price providers configured. At least one provider is required.")}buildSources(t){const e=[];if(!t||0===t.length)return this.getDefaultSources();let o=1;for(const r of t){switch(r.name.toLowerCase()){case"coingecko":e.push(this.createCoinGeckoSource(r.apiKey,o++));break;case"binance":e.push(this.createBinanceSource(r.apiKey,r.apiSecret,o++));break;case"coinbase":e.push(this.createCoinbaseSource(r.apiKey,o++));break;case"cryptocompare":if(!r.apiKey)throw new Error("CryptoCompare requires an API key. Please provide apiKey in priceProviders config.");e.push(this.createCryptoCompareSource(r.apiKey,o++));break;default:console.warn(`[Price Oracle] Unknown provider: ${r.name} - skipping`)}}return e}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 o=await(async t=>{const e=new AbortController,o=setTimeout((()=>e.abort()),5e3);try{const r=await fetch(t,{signal:e.signal,headers:{Accept:"application/json"}});if(clearTimeout(o),!r.ok)throw new Error(`HTTP ${r.status} ${r.statusText}`);return await r.json()}catch(t){if(clearTimeout(o),"AbortError"===t.name)throw new Error("Request timeout after 5000ms");throw t}})(e.toString()),r=Number(o?.USDT);if(!Number.isFinite(r)||r<=0)throw new Error("Invalid CryptoCompare price payload");const i=(await Lit.Actions.broadcastAndCollect({name:"cryptoComparePrice",value:r.toString()})).map((t=>parseFloat(t)));return i.sort(((t,e)=>t-e)),i[Math.floor(i.length/2)]},priority:e}}createFetchJson(){return async(t,e)=>{const o=new AbortController,r=setTimeout((()=>o.abort()),5e3);try{const r=await fetch(t,{headers:{Accept:"application/json",...e||{}},signal:o.signal});if(!r.ok)throw new Error(`HTTP ${r.status} ${r.statusText}`);return await r.json()}finally{clearTimeout(r)}}}getDefaultSources(){return[this.createCoinGeckoSource(void 0,1),this.createBinanceSource(void 0,void 0,2),this.createCoinbaseSource(void 0,3)]}createCoinGeckoSource(t,e=1){const o=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const t=new URL("https://api.coingecko.com/api/v3/simple/price");t.searchParams.set("ids","bitcoin"),t.searchParams.set("vs_currencies","usd");const e=await o(t.toString()),r=Number(e?.bitcoin?.usd);if(!Number.isFinite(r)||r<=0)throw new Error("Invalid CoinGecko price payload");const i=(await Lit.Actions.broadcastAndCollect({name:"coinGeckoPrice",value:r.toString()})).map((t=>parseFloat(t)));return i.sort(((t,e)=>t-e)),i[Math.floor(i.length/2)]},priority:e}}createBinanceSource(t,e,o=2){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=t?{"X-MBX-APIKEY":t}:void 0,i=await r(e.toString(),o),a=Number(i?.price);if(!Number.isFinite(a)||a<=0)throw new Error("Invalid Binance price payload");const n=(await Lit.Actions.broadcastAndCollect({name:"binancePrice",value:a.toString()})).map((t=>parseFloat(t)));return n.sort(((t,e)=>t-e)),n[Math.floor(n.length/2)]},priority:o}}createCoinbaseSource(t,e=3){const o=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const e=t?{"CB-ACCESS-KEY":t}:void 0,r=await o("https://api.coinbase.com/v2/prices/BTC-USD/spot",e),i=Number(r?.data?.amount);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid Coinbase price payload");const a=(await Lit.Actions.broadcastAndCollect({name:"coinbasePrice",value:i.toString()})).map((t=>parseFloat(t)));return a.sort(((t,e)=>t-e)),a[Math.floor(a.length/2)]},priority:e}}async getBTCPrice(){const t=Date.now();console.log("[Price Oracle] Fetching BTC price from external sources (parallel)..."),console.log(`[Price Oracle] Start time: ${t}`);const e=new Promise(((e,o)=>{setTimeout((()=>{const e=Date.now()-t;console.error(`[Price Oracle] \u274c GLOBAL TIMEOUT after ${e}ms`),o(new Error("Price oracle global timeout after 15000ms"))}),15e3)})),o=this.sources.map((async t=>{const e=Date.now();try{console.log(`[Price Oracle] [${e}] Querying ${t.name}...`);const o=await t.fetchPrice(),r=Date.now()-e;return console.log(`[Price Oracle] \u2705 [+${r}ms] ${t.name}: $${o.toLocaleString()}`),{source:t.name,priceUSD:o}}catch(o){const r=Date.now()-e;throw console.warn(`[Price Oracle] \u26a0\ufe0f [+${r}ms] ${t.name} failed: ${o.message}`),o}}));try{console.log("[Price Oracle] Waiting for first success (race pattern)...");const r=await Promise.race([Promise.race(o.map((t=>t.catch((t=>({error:t})))))),e]);if("error"in r){console.log("[Price Oracle] First result was error, waiting for any success...");const e=Date.now(),r=new Promise(((t,o)=>{setTimeout((()=>{const t=Date.now()-e;console.error(`[Price Oracle] \u274c FALLBACK TIMEOUT after ${t}ms`),o(new Error("Price oracle fallback timeout after 10000ms"))}),1e4)})),i=await Promise.race([Promise.allSettled(o),r]),a=Date.now()-e;console.log(`[Price Oracle] Fallback completed in ${a}ms`);const n=i.find((t=>"fulfilled"===t.status));if(!n){const e=Date.now()-t;throw console.error(`[Price Oracle] All sources failed after ${e}ms`),new Error("All price sources failed")}const{source:s,priceUSD:c}=n.value,l=Date.now()-t;console.log(`[Price Oracle] Using ${s} (first successful after failures) - total time: ${l}ms`);const u=Math.round(100*c),d=1000000n*BigInt(u);return console.log(`[Price Oracle] Price with 8 decimals: ${d}`),d}const{source:i,priceUSD:a}=r,n=Date.now()-t;console.log(`[Price Oracle] Using ${i} (fastest response) - total time: ${n}ms`);const s=Math.round(100*a),c=1000000n*BigInt(s);return console.log(`[Price Oracle] Price with 8 decimals: ${c}`),c}catch(e){const o=Date.now()-t;throw console.error(`[Price Oracle] Failed after ${o}ms: ${e.message}`),new Error(`All price sources failed: ${e.message}`)}}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 o=e[Math.floor(e.length/2)],r=e[0],i=e[e.length-1]/r,a=t.filter((t=>Math.abs(t.price-o)/o<=.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-o)/o;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 r=a.map((t=>t.price));r.sort(((t,e)=>t-e));const i=r[Math.floor(r.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): $${o.toLocaleString()}`);const n=Math.round(100*o);return 1000000n*BigInt(n)}},T=class{constructor(t){this.config=t,this.bitcoinProvider=t.bitcoinProvider}async calculateBalance(t,e){const o=await this.bitcoinProvider.getBalance(e),r=await this.getAuthorizedSpendsFromContract(t),i=r.reduce(((t,e)=>t+e.satoshis),0n),a=o>i?o-i:0n;return{totalUTXOs:[],totalBalance:o,authorizedUTXOs:r,authorizedBalance:i,authorizedSpendsHash:this.computeAuthorizedSpendsHash(t,r),availableUTXOs:[],availableBalance:a,vaultAddress:e,positionId:t,timestamp:Date.now()}}async calculateTrustedBalance(t,e,o){if(o)return await this.calculateBalance(t,e);const r=this.config.minConfirmations||6,i=await this.bitcoinProvider.getUTXOSet(e,r),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,o,r){return(await this.getAvailableUTXOs(t,e)).some((t=>t.txid===o&&t.vout===r))}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 o;o=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const r=new ethers.providers.StaticJsonRpcProvider(o,{name:"any",chainId: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"}],r);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 o=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;if(0===e.length)return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[o,[]]));const r=e.map((t=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.txid)),o=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(),o,t.targetAmount.toString(),t.timestamp]))}));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[o,r]))}};function S(t,e){if(0===t)return{termDurationDays:0,termLengthDays:30*e,isExpired:!1,daysUntilExpiry:30*e,daysIntoGracePeriod:0};const o=30*e,r=Math.floor(Date.now()/1e3)-t,i=Math.floor(r/86400);return{termDurationDays:i,termLengthDays:o,isExpired:i>o,daysUntilExpiry:Math.max(0,o-i),daysIntoGracePeriod:Math.max(0,i-o)}}function $(t,e){if(!t)return 13e3;const o=Math.min(e,30);return 11e3+9e3*(o*o)/900}function v(t,o,r){const i=t*o/10000000000000000n;if(0n===r)return{collateralValueUsd:i,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:i,collateralRatioBps:Number(i*e*10000n/r)}}function A(e,o){if(e<0n)throw new Error("BTC amount cannot be negative");if(o<=0n)throw new Error("BTC price must be positive");return e*o/t}function E(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 o=Number(10000n*(10000000000n*t)/e);if(o<0)throw new Error(`Collateral ratio out of bounds: ${o} bps (${o/100}%)`);return!isFinite(o)||o>Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:o}function I(t){return t/100}var P=class{constructor(t){this.termManagerAddress=t.termManagerAddress,this.loanOpsManagerAddress=t.loanOpsManagerAddress,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(t,{name:"any",chainId:this.chainId}),o=[{inputs:[],name:"liquidationThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],r=new ethers.Contract(this.loanOpsManagerAddress,o,e),i=await r.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 o=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),r=[{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,r,o),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}),o=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),r=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"}],o);return await i.getAuthorizedSpendsHash(r)}catch(t){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",t.message),new Error(`Failed to fetch authorized spends hash: ${t.message}`)}}};function B(t){return new P(t)}var C=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 o=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress),r=await this.config.priceOracle.getBTCPrice(),i=B({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[a,n]=await Promise.all([i.getLiquidationThreshold(),i.getTermFees(e.selectedTerm)]),s=S(e.termStartTimestamp,e.selectedTerm),c=$(s.isExpired,s.daysIntoGracePeriod),l=v(o.availableBalance,r,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;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,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:r,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:s.termDurationDays,termLengthDays:s.termLengthDays,isExpired:s.isExpired,daysUntilExpiry:s.daysUntilExpiry,daysIntoGracePeriod:s.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:a,originationFeeBps:n.originationFeeBps,extensionFeeBps:n.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 o=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress,!0),r=await this.config.priceOracle.getBTCPrice(),i=B({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[a,n]=await Promise.all([i.getLiquidationThreshold(),i.getTermFees(e.selectedTerm)]),s=S(e.termStartTimestamp,e.selectedTerm),c=$(s.isExpired,s.daysIntoGracePeriod),l=v(o.availableBalance,r,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;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,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:r,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:s.termDurationDays,termLengthDays:s.termLengthDays,isExpired:s.isExpired,daysUntilExpiry:s.daysUntilExpiry,daysIntoGracePeriod:s.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:a,originationFeeBps:n.originationFeeBps,extensionFeeBps:n.extensionFeeBps,timestamp:Date.now()}}async queryPositionState(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let o;o=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const r=new ethers.providers.StaticJsonRpcProvider(o,{name:"any",chainId:this.config.chainId}),i=new ethers.Contract(this.config.contractAddress,[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],r),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:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],r),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 u=Number(s.expiryAt),d=Number(s.selectedTerm),h=(m=d,0===(g=u)?0:g-30*m*86400);var g,m;return{positionId:s.positionId,pkpId:s.pkpId,borrower:s.borrower,vaultAddress:this.parseVaultAddress(s.vaultAddress,l(this.config.chain)),ucdDebt:BigInt(s.ucdDebt.toString()),termStartTimestamp:h,selectedTerm:d,status:c,expiryAt:u}}parseVaultAddress(t,e){try{const o=JSON.parse(t);if("object"==typeof o&&null!==o){if("sepolia"===e.toLowerCase())return o.regtest||o.testnet||o.mainnet;return"testnet"===u(e)&&(o.testnet||o.regtest)||o.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,o,r){const i=await this.getVaultSnapshot(t);return function(t,o,r,i){const a=o+r;return 0n===a||Number(t*e*10000n/a)>=i}(i.collateralValueUsd,i.ucdDebt,o,r)}};(async()=>{const t=f.create("BTC Withdrawal Validator"),e=1000000000000n,o=100000000000000n;console.log("[Step 0] Validating configuration..."),t.stepStart("0","Validate configuration parameters");const i=globalThis.chain,n=globalThis.bitcoinProviderUrl;if(!i)throw new Error('Missing required parameter: "chain". Must be "sepolia" or "ethereum"');if(!n)throw new Error('Missing required parameter: "bitcoinProviderUrl". Must be an approved Bitcoin RPC provider URL');const s=globalThis.auth?.mode||"prod";let h,g,p;if("prod"===s){console.log(" \ud83d\udd12 Running in PRODUCTION mode - strict validation enabled");const t=l(i);g=t,p=u(t),h=function(t){for(const e of Object.values(a)){const o=c[e].find((e=>e.url===t));if(o)return o}const e=Object.entries(c).flatMap((([t,e])=>e.map((e=>`${t}: ${e.name} (${e.url})`)))).join(", ");throw new Error(`Bitcoin provider not approved. Provided URL: ${t}. Approved providers: ${e}`)}(n),console.log(` \u2705 Chain: ${i} (${g})`),console.log(` \u2705 Bitcoin Network: ${p}`),console.log(` \u2705 Bitcoin Provider: ${h.name} (${h.url})`),console.log(` \u2705 Min Confirmations: ${h.minConfirmations}`)}else console.log(" \u26a0\ufe0f Running in DEVELOPMENT mode - accepting any Bitcoin provider"),g=i,p="sepolia"===i?"testnet":"regtest",h={name:"Custom Provider",url:n,minConfirmations:1,network:p},console.log(` \u2705 EVM Chain: ${i}`),console.log(` \u2705 Bitcoin Provider: ${n} (unvalidated)`),console.log(` \u2705 Min Confirmations: ${h.minConfirmations}`);if(!globalThis.contractAddresses||"object"!=typeof globalThis.contractAddresses)throw new Error('Required "contractAddresses" object with: PositionManager, LoanOperationsManagerModule, TermManagerModule');const w=globalThis.contractAddresses.PositionManager,S=globalThis.contractAddresses.LoanOperationsManagerModule,$=globalThis.contractAddresses.TermManagerModule;if(!w||!S||!$)throw new Error("All contract addresses are required: PositionManager, LoanOperationsManagerModule, TermManagerModule");console.log(" \u2705 Contract addresses loaded and validated"),console.log(` PositionManager: ${w}`),console.log(` LoanOpsManager: ${S}`),t.stepEnd("0"),console.log("[Step 0.5] Critical system health checks..."),t.stepStart("0.5","Critical system health checks"),t.warn("0.5","System health checks temporarily disabled (SystemHealthModule not available)"),t.log("0.5","\u2705 System health checks complete (skipped)"),t.stepEnd("0.5");const v=globalThis.auth;if(!v||"object"!=typeof v)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!v.positionId)throw new Error("auth.positionId is required");if("number"!=typeof v.timestamp)throw new Error("auth.timestamp must be a number");if(!v.signature)throw new Error("auth.signature is required");const P=BigInt(v.amount);if(P<=0n)throw new Error(`Withdrawal amount must be positive, got: ${P.toString()} sats`);if(P<546n)throw new Error(`Withdrawal amount ${P.toString()} sats is below minimum ${546n.toString()} sats (Bitcoin dust limit). This amount is not economically viable for Bitcoin network transactions.`);const B=v.destinationAddress;if(!B||"string"!=typeof B)throw new Error('Missing or invalid "destinationAddress" parameter - must be a valid Bitcoin address');const U=globalThis.publicKey;if(!U)throw new Error("publicKey must not be blank");if("string"!=typeof U||0===U.length)throw new Error("publicKey must not be blank");const x=U;if(!(()=>{try{const t=/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,35}$/.test(B),e=/^(bc1|tb1|bcrt1)[0-9a-zA-Z]{11,87}$/.test(B);return!(!t&&!e)&&("testnet"===p||"regtest"===p?B.startsWith("m")||B.startsWith("n")||B.startsWith("2")||B.startsWith("tb1")||B.startsWith("bcrt1"):"mainnet"===p&&(B.startsWith("1")||B.startsWith("3")||B.startsWith("bc1")))}catch(t){return!1}})())throw new Error(`Invalid Bitcoin address format for ${p} network: ${B}. Expected ${"mainnet"===p?"address starting with 1, 3, or bc1":"address starting with m, n, 2, tb1, or bcrt1"}`);console.log(` \u2705 Destination address validated: ${B} (${p})`);let O="0.5";try{console.log("[BTC Withdrawal] Started"),console.log(` Position: ${v.positionId}`),console.log(` Withdrawal Amount: ${P.toString()} sats`),console.log(` Destination Address: ${B}`);const a=new y({providerUrl:h.url}),n=new b,c=globalThis.customRpcUrl;let l;c&&"string"==typeof c?(console.log(` Using custom RPC URL: ${c}`),l=c):(l=await Lit.Actions.getRpcUrl({chain:i}),console.log(` Using LIT-provided RPC URL for chain: ${i}`));const u=(k={contractAddress:S,chain:i,chainId:"prod"===s?d(g):v.chainId,rpcUrl:l,bitcoinProvider:a,minConfirmations:"prod"===s?h.minConfirmations:1},new T(k));O="1",console.log("[Step 1] Getting vault snapshot..."),t.stepStart("1","Get vault snapshot");const p=function(t){return new C(t)}({contractAddress:w,termManagerAddress:$,loanOpsManagerAddress:S,chain:i,chainId:"prod"===s?d(g):v.chainId,rpcUrl:l,vaultBalance:u,priceOracle:n}),f=await p.getVaultSnapshot(v.positionId);console.log(` \u2705 Vault balance: ${f.availableBTCSats.toString()} sats`),console.log(` \u2705 Current debt: ${f.ucdDebt.toString()} wei`),t.stepEnd("1"),O="2",console.log("[Step 2] Authenticating caller..."),t.stepStart("2","Authenticate caller");const U=t.getElapsed();console.log(`[TIMING] Step 2 started at ${U}ms`);if(!await m.verifyWithdrawAuthorization(v,f.borrower))throw new Error("Caller not authorized");console.log(" \u2705 Caller authorized"),t.stepEnd("2"),O="2.5",console.log("[Step 2.5] Validating position status..."),t.stepStart("2.5","Validate position status");const M=t.getElapsed();console.log(`[TIMING] Step 2.5 started at ${M}ms`);if(![0,1,2,6].includes(f.status))throw new Error(`Position status ${r(f.status)} does not allow withdrawal. Valid statuses: PENDING_DEPOSIT, PENDING_MINT, ACTIVE, REPAID.`);console.log(` \u2705 Position status valid for withdrawal: ${r(f.status)}`),t.stepEnd("2.5"),O="3",console.log("[Step 3] Validating post-withdrawal collateral ratio..."),t.stepStart("3","Validate collateral ratio");const D=t.getElapsed();console.log(`[TIMING] Step 3 started at ${D}ms`);const N=P;if(0n===f.ucdDebt)console.log(" \u2705 Position has zero debt - withdrawal allowed");else{if(f.btcPriceUsd<e||f.btcPriceUsd>o)throw new Error(`Bitcoin price ${f.btcPriceUsd} is outside acceptable range (1000000000000 - 100000000000000). This may indicate oracle manipulation or stale price data. Please try again later.`);console.log(` \u2705 BTC price validation passed: $${(Number(f.btcPriceUsd)/1e8).toFixed(2)}`);const t=f.availableBTCSats-N,r=E(A(t,f.btcPriceUsd),f.ucdDebt);if(r<f.liquidationThresholdBps)throw new Error(`Insufficient collateral: post-withdrawal ratio ${I(r)}% < liquidation threshold ${I(f.liquidationThresholdBps)}%. Withdrawal would result in insufficient collateralization.`);console.log(` \u2705 Post-withdrawal collateral ratio: ${I(r)}%`),console.log(` \u2705 Exceeds liquidation threshold: ${I(f.liquidationThresholdBps)}%`)}t.stepEnd("3"),O="4",console.log("[Step 4] Validating withdrawal amount and balance..."),t.stepStart("4","Validate withdrawal amount and balance");const z=t.getElapsed();console.log(`[TIMING] Step 4 started at ${z}ms`);const L="string"==typeof v.amount?BigInt(v.amount):v.amount;if(L!==P)throw new Error(`Authorization amount mismatch: auth.amount=${L.toString()} sats, but requested withdrawal=${P.toString()} sats. The signed authorization must match the requested withdrawal amount.`);if(console.log(" \u2705 Authorization amount matches withdrawal amount"),N>f.availableBTCSats)throw new Error(`Insufficient vault balance: need ${N.toString()} sats but only ${f.availableBTCSats.toString()} sats available`);console.log(" \u2705 Total deduction is within available balance");const F=f.availableBTCSats-N,R=f.ucdDebt,X=A(F,f.btcPriceUsd),q=R>0n?E(X,R):void 0;if(console.log(` Post-withdrawal collateral: ${F.toString()} sats`),console.log(` Post-withdrawal collateral value: $${(Number(X)/1e8).toFixed(2)}`),console.log(` Post-withdrawal debt: ${R.toString()} wei`),console.log(` Post-withdrawal collateral ratio: ${R>0n&&void 0!==q?I(Number(q)):"\u221e"}%`),F<0n)throw new Error(`Withdrawal would result in negative balance: ${F.toString()} sats`);console.log(" \u2705 Withdrawal validation passed"),t.stepEnd("4"),O="4.5",console.log("[Step 4.5] Querying vault UTXOs from Bitcoin network..."),t.stepStart("4.5","Query and select UTXO");const H=t.getElapsed();console.log(`[TIMING] Step 4.5 started at ${H}ms`);const V=f.vaultAddress;if(!V)throw new Error("Vault address not found in snapshot");console.log(` Vault address: ${V}`);let _=[],G=null;const W=3,j=500;for(let t=1;t<=W;t++)try{console.log(` \ud83d\udd04 UTXO query attempt ${t}/${W}...`),_=await a.getUTXOs(V),console.log(` \u2705 UTXO query succeeded on attempt ${t}`),G=null;break}catch(e){G=e,console.error(` \u274c UTXO query failed on attempt ${t}:`,e.message),t<W&&(console.log(` \u23f3 Waiting ${j}ms before retry...`),await new Promise((t=>setTimeout(t,j))))}if(G)throw new Error(`Failed to query UTXOs after ${W} attempts: ${G.message}`);if(!_||0===_.length)throw new Error(`No UTXOs found in vault ${V}`);console.log(` \u2705 Found ${_.length} UTXO(s) in vault`);const K=h.minConfirmations,J=_.filter((t=>t.confirmations>=K));if(0===J.length)throw new Error(`No confirmed UTXOs found in vault. Required confirmations: ${K}, found ${_.length} UTXO(s) with insufficient confirmations.`);console.log(` \u2705 Found ${J.length} confirmed UTXO(s)`);const Q=J.filter((t=>t.satoshis>=N));if(0===Q.length){const t=J.reduce(((t,e)=>e.satoshis>t?e.satoshis:t),0n);throw new Error(`No single UTXO large enough for withdrawal. Need ${N.toString()} sats, largest UTXO: ${t.toString()} sats. Multiple-input withdrawals not yet supported.`)}const Y=Q.sort(((t,e)=>Number(t.satoshis-e.satoshis)))[0];console.log(` \u2705 Selected UTXO: ${Y.txid}:${Y.vout}`),console.log(` \u2705 UTXO value: ${Y.satoshis.toString()} sats`),console.log(` \u2705 UTXO confirmations: ${Y.confirmations}`);const Z=f.availableBTCSats;if(Y.satoshis<Z&&console.warn(` \u26a0\ufe0f Selected UTXO value (${Y.satoshis.toString()}) < calculated balance (${Z.toString()}). This is expected for multi-UTXO vaults where we select one UTXO.`),Y.satoshis<N)throw new Error(`Selected UTXO value (${Y.satoshis.toString()} sats) < withdrawal amount (${N.toString()} sats). This should never happen after eligibility filtering.`);console.log(" \u2705 UTXO validation passed"),t.stepEnd("4.5"),O="5",console.log("[Step 5] Building authorization message..."),t.stepStart("5","Build authorization message");const tt=t.getElapsed();console.log(`[TIMING] Step 5 started at ${tt}ms`);const et=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(v.action)),ot=f.authorizedSpendsHash,rt=ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["uint256"],[f.ucdDebt])),it=ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["address","address","address"],[w,S,$])),at=BigInt(v.timestamp),nt=F,st=f.btcPriceUsd,ct=BigInt(1e4)*BigInt(10**8),lt=BigInt(1e6)*BigInt(10**8);if(st<ct||st>lt)throw new Error(`BTC price ${st.toString()} (8 decimals) is outside acceptable range (${ct.toString()} - ${lt.toString()})`);console.log("[Step 5] DEBUG: Message Hash Parameters:"),console.log(" positionId:",v.positionId),console.log(" action:",v.action),console.log(" actionHash:",et),console.log(" authorizedSpendsHash:",ot),console.log(" ucdDebtHash:",rt),console.log(" withdrawalAddress:",B),console.log(" totalDeduction:",N.toString()),console.log(" newCollateral:",nt.toString()),console.log(" quantumTimestamp:",at.toString()),console.log(" btcPrice:",st.toString()),console.log(" utxoTxid:",Y.txid),console.log(" utxoVout:",Y.vout);const ut=ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32","bytes32","bytes32","bytes32","string","uint256","uint256","uint256","uint256","string","uint32"],[v.positionId,et,ot,rt,it,B,N.toString(),nt.toString(),at.toString(),st.toString(),Y.txid,Y.vout]));console.log("[Step 5] Contract message hash for signing:",ut),console.log("[Step 5] Message hash bytes length:",ethers.utils.arrayify(ut).length),t.stepEnd("5"),O="6",console.log("[Step 6] Signing contract message hash..."),t.stepStart("6","Sign message");const dt=t.getElapsed();console.log(`[TIMING] Step 6 started at ${dt}ms`),t.log("6","========================================"),t.log("6","\ud83d\udd0d FINAL HASH INPUTS - ALL LIT NODES MUST MATCH"),t.log("6","========================================"),t.log("6",`Position ID: ${v.positionId}`),t.log("6",`Action Hash: ${et}`),t.log("6",`Authorized Spends Hash: ${ot}`),t.log("6",`UCD Debt Hash: ${rt}`),t.log("6",`Contract Bundle Hash: ${it}`),t.log("6",`Destination Address: ${B}`),t.log("6",`Total Deduction: ${N.toString()}`),t.log("6",`New Collateral: ${nt.toString()}`),t.log("6",`Quantum Timestamp: ${at.toString()}`),t.log("6",`BTC Price: ${st.toString()}`),t.log("6",`Selected UTXO Txid: ${Y.txid}`),t.log("6",`Selected UTXO Vout: ${Y.vout}`),t.log("6",`Selected UTXO Satoshis: ${Y.satoshis.toString()}`),t.log("6",`Selected UTXO Confirmations: ${Y.confirmations}`),t.log("6","---"),t.log("6",`\ud83d\udcdd FINAL CONTRACT MESSAGE HASH: ${ut}`),t.log("6","========================================"),t.log("6","Preparing signEcdsa call"),t.log("6",`contractMessageHash: ${ut}`),t.log("6",`publicKey: ${x}`),t.log("6","publicKey with 0x prefix: "+("0x"+x));const ht=ethers.utils.arrayify(ut);t.log("6",`messageBytes length: ${ht.length}`),t.log("6",`messageBytes: ${JSON.stringify(Array.from(ht))}`),t.log("6","About to call Lit.Actions.signEcdsa...");const gt=Date.now(),mt=await Lit.Actions.signEcdsa({toSign:ht,publicKey:"0x"+x,sigName:"btcWithdrawalAuth"}),pt=Date.now()-gt;t.log("6",`Real PKP signature created in ${pt}ms`),t.log("6",`signature: ${JSON.stringify(mt)}`),t.stepEnd("6"),console.log("[BTC Withdrawal] \u2705 Complete"),t.summary(),Lit.Actions.setResponse({response:JSON.stringify({approved:!0,positionId:v.positionId,actionHash:et,authorizedSpendsHash:ot,ucdDebtHash:rt,totalDeduction:N.toString(),remainingCollateral:F.toString(),newCollateralRatioBps:R>0n&&void 0!==q?Number(q):void 0,destinationAddress:B,signature:mt,timestamp:at.toString(),btcPrice:st.toString(),utxoTxid:Y.txid,utxoVout:Y.vout,utxoSatoshis:Y.satoshis.toString()})})}catch(e){console.error("[BTC Withdrawal] \u274c Failed:",e.message),console.error(`[BTC Withdrawal] Failed at step: ${O}`),t.error("error",`Failed: ${e.message}`),Lit.Actions.setResponse({response:JSON.stringify({approved:!1,reason:e.message||e.toString(),failedStep:O,positionId:v?.positionId,timestamp:Date.now()})})}var k})()})();
@@ -0,0 +1 @@
1
+ 76eae3a68bbd251aa92aa4ba72cf8e66af7088b7bb396e8117062daf741afb5e
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "btc-withdrawal",
3
+ "originalSize": 126362,
4
+ "minifiedSize": 54605,
5
+ "compressionRatio": 0.5678685047720042,
6
+ "hash": "76eae3a68bbd251aa92aa4ba72cf8e66af7088b7bb396e8117062daf741afb5e",
7
+ "buildTime": 1768236994526,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{var t=100000000n,e=1000000000000000000n,o=100;var r=(t=>(t.SEPOLIA="sepolia",t.ETHEREUM="ethereum",t.HARDHAT="hardhat",t))(r||{}),i=(t=>(t.TESTNET="testnet",t.MAINNET="mainnet",t.REGTEST="regtest",t))(i||{}),n={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},a={ethereum:1,sepolia:11155111,hardhat:31337},s={regtest:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://127.0.0.1:18443",network:"regtest",minConfirmations:1,name:"Local Regtest"},{url:"https://313561dc30c5.ngrok-free.app",network:"regtest",minConfirmations:1,name:"Ngrok Bitcoin Tunnel"}],testnet:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"}],mainnet:[{url:"https://blockstream.info/api",network:"mainnet",minConfirmations:6,name:"Blockstream Mainnet"},{url:"https://mempool.space/api",network:"mainnet",minConfirmations:6,name:"Mempool.space Mainnet"}]};function c(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(r).join(", ")}`)}function l(t){return n[t]}function u(t){return a[t]}function d(t){return Math.floor(t/o)*o}function h(){return d(Math.floor(Date.now()/1e3))}async function p(){const t=Math.floor(Date.now()/1e3),e=t%o;if(!function(t){const e=(t??Math.floor(Date.now()/1e3))%o;return e>=92||e<8}(t))return;let r;r=e>=92?100-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: ${h()}`)}0,0;var m=class{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");if(!t.mode)throw new Error("auth.mode is required");if("dev"!==t.mode&&"prod"!==t.mode)throw new Error(`auth.mode must be "dev" or "prod", got: ${t.mode}`);return t}static async recoverSigner(t,e){try{const o=ethers.utils.arrayify(t),r=ethers.utils.hashMessage(o);return ethers.utils.recoverAddress(r,e)}catch(t){throw console.error("[Authorization] Signature recovery failed:",t),new Error(`Failed to recover signer: ${t instanceof Error?t.message:String(t)}`)}}static async verifyActionAuthorization(t,e,o){await p();try{!function(t,e){const o=d(e??Math.floor(Date.now()/1e3)),r=d(t);if(r!==o)throw new Error(`[Quantum] Invalid timestamp: signature from quantum ${r}, current quantum is ${o}`)}(t.timestamp)}catch(t){return console.error("[Authorization] Quantum timestamp validation failed:",t),!1}if(!t.mode)return console.error("[Authorization] Missing mode field"),!1;if("dev"!==t.mode&&"prod"!==t.mode)return console.error(`[Authorization] Invalid mode: ${t.mode}, must be "dev" or "prod"`),!1;if("prod"===t.mode)return console.error('[Authorization] Production mode not yet supported - use mode="dev"'),!1;const r=e();console.log("[Authorization] Message hash:",r),console.log("[Authorization] Recovering signer from signature..."),console.log(" signature:",t.signature);const i=await this.recoverSigner(r,t.signature);console.log(" recoveredAddress:",i),console.log("[Authorization] Checking authorization:"),console.log(" recoveredAddress:",i),console.log(" recoveredAddress (lowercase):",i.toLowerCase());const n=o(i);return console.log(" match:",n?"\u2705 YES":"\u274c NO"),n||console.error("[Authorization] Not authorized:",`\n Recovered: ${i}`),n}static async verifyAmountPositionActionAuthorization(t,e){return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action)),o="string"==typeof t.amount?BigInt(t.amount):t.amount;console.log("[Authorization] Building message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" amount (string):",t.amount),console.log(" amount (BigInt):",o.toString()),console.log(" action:",t.action),console.log(" actionHash:",e);const r=[t.positionId,t.timestamp,t.chainId,o.toString(),e],i=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],r);return console.log(" messageHash (solidityKeccak256):",i),i}),(t=>t.toLowerCase()===e.toLowerCase()))}static async verifyMintAuthorization(t,e){return"mint-ucd"!==t.action?(console.error(`[Authorization] Invalid action for mint: expected "mint-ucd", got "${t.action}"`),!1):this.verifyAmountPositionActionAuthorization(t,e)}static async verifyWithdrawAuthorization(t,e){if("withdraw-btc"!==t.action)return console.error(`[Authorization] Invalid action for withdrawal: expected "withdraw-btc", got "${t.action}"`),!1;return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action)),o="string"==typeof t.amount?BigInt(t.amount):t.amount;console.log("[Authorization] Building withdrawal message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" amount (string):",t.amount),console.log(" amount (BigInt):",o.toString()),console.log(" destinationAddress:",t.destinationAddress),console.log(" action:",t.action),console.log(" actionHash:",e);const r=[t.positionId,t.timestamp,t.chainId,o.toString(),t.destinationAddress,e],i=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","string","bytes32"],r);return console.log(" messageHash (solidityKeccak256):",i),i}),(t=>t.toLowerCase()===e.toLowerCase()))}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");if(!t.mode)throw new Error("auth.mode is required");if("dev"!==t.mode&&"prod"!==t.mode)throw new Error(`auth.mode must be "dev" or "prod", got: ${t.mode}`);return t}static async verifyTermPositionActionAuthorization(t,e){return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action));console.log("[Authorization] Building term message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" newTerm:",t.newTerm),console.log(" action:",t.action),console.log(" actionHash:",e);const o=[t.positionId,t.timestamp,t.chainId,t.newTerm,e],r=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],o);return console.log(" messageHash (solidityKeccak256):",r),r}),(t=>t.toLowerCase()===e.toLowerCase()))}static async verifyExtendAuthorization(t,e){return"extend-position"!==t.action?(console.error(`[Authorization] Invalid action for extension: expected "extend-position", got "${t.action}"`),!1):this.verifyTermPositionActionAuthorization(t,e)}static async verifyBtcExecutionAuthorization(t,e){if("execute-btc-withdrawal"!==t.action)return console.error(`[Authorization] Invalid action for BTC execution: expected "execute-btc-withdrawal", got "${t.action}"`),!1;return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action));console.log("[Authorization] Building BTC execution message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" utxoIdentifier:",t.utxoIdentifier),console.log(" networkFee:",t.networkFee),console.log(" action:",t.action),console.log(" actionHash:",e);const o=[t.positionId,t.timestamp,t.chainId,t.utxoIdentifier,t.networkFee,e],r=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","string","uint256","bytes32"],o);return console.log(" messageHash (solidityKeccak256):",r),r}),(t=>t.toLowerCase()===e.toLowerCase()))}},g=class{stepStart(t,e){}stepEnd(t,e){}log(t,e){}warn(t,e){}error(t,e){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},w=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(),o=Math.random().toString(36).substring(2,9);this.executionId=`exec_${e}_${o}`,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 o=Date.now()-this.executionStartTime,r=this.TIMEOUT_MS-o;console.log(`[Step ${t}] ${e}...`),console.log(`[Step ${t}] Elapsed: ${o}ms | Remaining: ${r}ms`)}stepEnd(t,e=5e3){const o=this.stepStartTimes.get(t);if(!o)return console.warn(`[Step ${t}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const r=Date.now()-o;this.stepDurations.set(t,r),console.log(`[Step ${t}] Duration: ${r}ms`),r>e&&console.warn(`\u26a0\ufe0f [Step ${t}] Took ${r}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){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,o]of this.stepDurations.entries()){const r=(o/t*100).toFixed(1);console.log(`[${this.actionName}] Step ${e}: ${o}ms (${r}%)`)}}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}},f=class{static create(t="LIT Action",e){return e??globalThis.debugAction??!1?new w(t):new g}},y=class{constructor(t){this.config=t,this.timeout=t.timeout||15e3,this.logger=f.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 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),!o.ok)throw new Error(`Failed to fetch block height: ${o.status} ${o.statusText}`);const r=await o.text(),i=parseInt(r.trim(),10);if(isNaN(i))throw new Error(`Invalid block height response: ${r}`);return this.logger.stepEnd("0"),i}catch(t){if(clearTimeout(e),this.logger.stepEnd("0"),"AbortError"===t.name)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: ${t.message}`),this.config.fallbackProviders&&this.config.fallbackProviders.length>0){const t=[...this.config.fallbackProviders].sort(((t,e)=>t.priority-e.priority));for(const o of t)try{return this.logger.log("0",`Trying fallback: ${o.name} (${o.url})`),await this.fetchUTXOsFromProvider(o.url,e)}catch(t){this.logger.warn("0",`Fallback ${o.name} failed: ${t.message}`);continue}}throw new Error(`Failed to fetch UTXOs: ${t.message}`)}}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");return t.map((t=>{let o=0;return t.status&&"object"==typeof t.status?t.status.confirmed&&(o=void 0!==t.status.block_height&&t.status.block_height>0?e-t.status.block_height+1:1):void 0!==t.confirmations&&(o=t.confirmations),{txid:t.txid,vout:void 0!==t.vout?t.vout:t.n,satoshis:BigInt(void 0!==t.value?t.value:t.satoshis),confirmations:o}}))}async fetchUTXOsFromProvider(t,e){this.logger.stepStart("1","fetchUTXOsFromProvider");const o=`${t}/address/${e}/utxos`;this.logger.log("1",`Fetching UTXOs from ${o}`);const r=await this.getCurrentBlockHeight();this.logger.log("1",`Current block height: ${r}`);const i=new AbortController,n=setTimeout((()=>i.abort()),this.timeout);try{const t=await fetch(o,{signal:i.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(n),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();this.logger.log("1",`Raw API Response: ${JSON.stringify(e,null,2)}`);const a=this.parseBlockstreamUTXOs(e,r);return this.logger.stepEnd("1"),a}catch(t){if(clearTimeout(n),this.logger.stepEnd("1"),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOSet(t,e){let o=[],r=null;for(let e=1;e<=3;e++)try{this.logger.log("2",`UTXO query attempt ${e}/3...`),o=await this.getUTXOs(t),this.logger.log("2",`UTXO query succeeded on attempt ${e}`),r=null;break}catch(t){r=t,this.logger.error("2",`UTXO query failed on attempt ${e}: ${t.message}`),e<3&&(this.logger.log("2","Waiting 500ms before retry..."),await new Promise((t=>setTimeout(t,500))))}if(r)throw new Error(`Failed to query UTXOs after 3 attempts: ${r.message}`);const i=o.reduce(((t,e)=>t+e.satoshis),0n),n=o.filter((t=>t.confirmations>=e)).reduce(((t,e)=>t+e.satoshis),0n),a=i-n;return{utxos:o,totalBalance:i,totalUTXOs:o.length,confirmedBalance:n,unconfirmedBalance:a}}async getBalance(t){const e=this.stripNetworkPrefix(t),o=`${this.config.providerUrl}/address/${e}`;this.logger.log("3",`Fetching balance from ${o}`);const r=new AbortController,i=setTimeout((()=>r.abort()),this.timeout);try{const t=await fetch(o,{signal:r.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),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();if(!e.chain_stats)throw new Error("Invalid response: missing chain_stats");const n=e.chain_stats.funded_txo_sum,a=e.chain_stats.spent_txo_sum;if(void 0===n||void 0===a)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${n}, spent_txo_sum: ${a}`);const s=BigInt(n-a);return this.logger.log("3",`Balance: ${s.toString()} sats`),this.logger.log("3",` - Funded: ${n} sats, Spent: ${a} sats`),s}catch(t){if(clearTimeout(i),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);const o=t.message||"Unknown error";throw this.logger.log("3",`Balance fetch failed: ${o}`),new Error(`Failed to fetch Bitcoin balance for address ${e}: ${o}`)}}async getTransaction(t){if(this.config.rpcHelper)try{const e=this.config.rpcWallet||"",o=await this.config.rpcHelper.getTransaction(e,t);return{txid:o.txid,confirmations:o.confirmations||0}}catch(t){if(t.message?.includes("Invalid or non-wallet transaction")||-5===t.code)return null;throw t}const e=new AbortController,o=setTimeout((()=>e.abort()),this.timeout);try{const r=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(o),!r.ok){if(404===r.status)return null;throw new Error(`Bitcoin provider error: ${r.status} ${r.statusText}`)}const i=await r.json();return i.status?{txid:i.txid,confirmations:i.status.confirmed&&i.status.block_height?1:0}:null}catch(t){if(clearTimeout(o),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);if(t.message?.includes("404"))return null;throw t}}};function b(t,e){if(0===t)return{termDurationDays:0,termLengthDays:30*e,isExpired:!1,daysUntilExpiry:30*e,daysIntoGracePeriod:0};const o=30*e,r=Math.floor(Date.now()/1e3)-t,i=Math.floor(r/86400);return{termDurationDays:i,termLengthDays:o,isExpired:i>o,daysUntilExpiry:Math.max(0,o-i),daysIntoGracePeriod:Math.max(0,i-o)}}function T(t,e){if(!t)return 13e3;const o=Math.min(e,30);return 11e3+9e3*(o*o)/900}function v(t,o,r){const i=t*o/10000000000000000n;if(0n===r)return{collateralValueUsd:i,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:i,collateralRatioBps:Number(i*e*10000n/r)}}function A(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 o=Number(10000n*(10000000000n*t)/e);if(o<0)throw new Error(`Collateral ratio out of bounds: ${o} bps (${o/100}%)`);return!isFinite(o)||o>Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:o}function E(t){return t/100}var S=class{constructor(t,e,o){this.mode=o,e&&Array.isArray(e)?this.sources=e:t&&Array.isArray(t)?this.sources=this.buildSources(t):this.sources=this.buildSources(),this.sources.sort(((t,e)=>t.priority-e.priority)),this.validateProviderCount()}validateProviderCount(){if("prod"===this.mode&&this.sources.length<3)throw new Error(`Production mode requires at least 3 price providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported providers: CoinGecko, Binance, Coinbase, CryptoCompare (with API key)`);if(0===this.sources.length)throw new Error("No price providers configured. At least one provider is required.")}buildSources(t){const e=[];if(!t||0===t.length)return this.getDefaultSources();let o=1;for(const r of t){switch(r.name.toLowerCase()){case"coingecko":e.push(this.createCoinGeckoSource(r.apiKey,o++));break;case"binance":e.push(this.createBinanceSource(r.apiKey,r.apiSecret,o++));break;case"coinbase":e.push(this.createCoinbaseSource(r.apiKey,o++));break;case"cryptocompare":if(!r.apiKey)throw new Error("CryptoCompare requires an API key. Please provide apiKey in priceProviders config.");e.push(this.createCryptoCompareSource(r.apiKey,o++));break;default:console.warn(`[Price Oracle] Unknown provider: ${r.name} - skipping`)}}return e}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 o=await(async t=>{const e=new AbortController,o=setTimeout((()=>e.abort()),5e3);try{const r=await fetch(t,{signal:e.signal,headers:{Accept:"application/json"}});if(clearTimeout(o),!r.ok)throw new Error(`HTTP ${r.status} ${r.statusText}`);return await r.json()}catch(t){if(clearTimeout(o),"AbortError"===t.name)throw new Error("Request timeout after 5000ms");throw t}})(e.toString()),r=Number(o?.USDT);if(!Number.isFinite(r)||r<=0)throw new Error("Invalid CryptoCompare price payload");const i=(await Lit.Actions.broadcastAndCollect({name:"cryptoComparePrice",value:r.toString()})).map((t=>parseFloat(t)));return i.sort(((t,e)=>t-e)),i[Math.floor(i.length/2)]},priority:e}}createFetchJson(){return async(t,e)=>{const o=new AbortController,r=setTimeout((()=>o.abort()),5e3);try{const r=await fetch(t,{headers:{Accept:"application/json",...e||{}},signal:o.signal});if(!r.ok)throw new Error(`HTTP ${r.status} ${r.statusText}`);return await r.json()}finally{clearTimeout(r)}}}getDefaultSources(){return[this.createCoinGeckoSource(void 0,1),this.createBinanceSource(void 0,void 0,2),this.createCoinbaseSource(void 0,3)]}createCoinGeckoSource(t,e=1){const o=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const t=new URL("https://api.coingecko.com/api/v3/simple/price");t.searchParams.set("ids","bitcoin"),t.searchParams.set("vs_currencies","usd");const e=await o(t.toString()),r=Number(e?.bitcoin?.usd);if(!Number.isFinite(r)||r<=0)throw new Error("Invalid CoinGecko price payload");const i=(await Lit.Actions.broadcastAndCollect({name:"coinGeckoPrice",value:r.toString()})).map((t=>parseFloat(t)));return i.sort(((t,e)=>t-e)),i[Math.floor(i.length/2)]},priority:e}}createBinanceSource(t,e,o=2){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=t?{"X-MBX-APIKEY":t}:void 0,i=await r(e.toString(),o),n=Number(i?.price);if(!Number.isFinite(n)||n<=0)throw new Error("Invalid Binance price payload");const a=(await Lit.Actions.broadcastAndCollect({name:"binancePrice",value:n.toString()})).map((t=>parseFloat(t)));return a.sort(((t,e)=>t-e)),a[Math.floor(a.length/2)]},priority:o}}createCoinbaseSource(t,e=3){const o=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const e=t?{"CB-ACCESS-KEY":t}:void 0,r=await o("https://api.coinbase.com/v2/prices/BTC-USD/spot",e),i=Number(r?.data?.amount);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid Coinbase price payload");const n=(await Lit.Actions.broadcastAndCollect({name:"coinbasePrice",value:i.toString()})).map((t=>parseFloat(t)));return n.sort(((t,e)=>t-e)),n[Math.floor(n.length/2)]},priority:e}}async getBTCPrice(){const t=Date.now();console.log("[Price Oracle] Fetching BTC price from external sources (parallel)..."),console.log(`[Price Oracle] Start time: ${t}`);const e=new Promise(((e,o)=>{setTimeout((()=>{const e=Date.now()-t;console.error(`[Price Oracle] \u274c GLOBAL TIMEOUT after ${e}ms`),o(new Error("Price oracle global timeout after 15000ms"))}),15e3)})),o=this.sources.map((async t=>{const e=Date.now();try{console.log(`[Price Oracle] [${e}] Querying ${t.name}...`);const o=await t.fetchPrice(),r=Date.now()-e;return console.log(`[Price Oracle] \u2705 [+${r}ms] ${t.name}: $${o.toLocaleString()}`),{source:t.name,priceUSD:o}}catch(o){const r=Date.now()-e;throw console.warn(`[Price Oracle] \u26a0\ufe0f [+${r}ms] ${t.name} failed: ${o.message}`),o}}));try{console.log("[Price Oracle] Waiting for first success (race pattern)...");const r=await Promise.race([Promise.race(o.map((t=>t.catch((t=>({error:t})))))),e]);if("error"in r){console.log("[Price Oracle] First result was error, waiting for any success...");const e=Date.now(),r=new Promise(((t,o)=>{setTimeout((()=>{const t=Date.now()-e;console.error(`[Price Oracle] \u274c FALLBACK TIMEOUT after ${t}ms`),o(new Error("Price oracle fallback timeout after 10000ms"))}),1e4)})),i=await Promise.race([Promise.allSettled(o),r]),n=Date.now()-e;console.log(`[Price Oracle] Fallback completed in ${n}ms`);const a=i.find((t=>"fulfilled"===t.status));if(!a){const e=Date.now()-t;throw console.error(`[Price Oracle] All sources failed after ${e}ms`),new Error("All price sources failed")}const{source:s,priceUSD:c}=a.value,l=Date.now()-t;console.log(`[Price Oracle] Using ${s} (first successful after failures) - total time: ${l}ms`);const u=Math.round(100*c),d=1000000n*BigInt(u);return console.log(`[Price Oracle] Price with 8 decimals: ${d}`),d}const{source:i,priceUSD:n}=r,a=Date.now()-t;console.log(`[Price Oracle] Using ${i} (fastest response) - total time: ${a}ms`);const s=Math.round(100*n),c=1000000n*BigInt(s);return console.log(`[Price Oracle] Price with 8 decimals: ${c}`),c}catch(e){const o=Date.now()-t;throw console.error(`[Price Oracle] Failed after ${o}ms: ${e.message}`),new Error(`All price sources failed: ${e.message}`)}}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 o=e[Math.floor(e.length/2)],r=e[0],i=e[e.length-1]/r,n=t.filter((t=>Math.abs(t.price-o)/o<=.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-o)/o;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 r=n.map((t=>t.price));r.sort(((t,e)=>t-e));const i=r[Math.floor(r.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): $${o.toLocaleString()}`);const a=Math.round(100*o);return 1000000n*BigInt(a)}},$=class{constructor(t){this.config=t,this.bitcoinProvider=t.bitcoinProvider}async calculateBalance(t,e){const o=await this.bitcoinProvider.getBalance(e),r=await this.getAuthorizedSpendsFromContract(t),i=r.reduce(((t,e)=>t+e.satoshis),0n),n=o>i?o-i:0n;return{totalUTXOs:[],totalBalance:o,authorizedUTXOs:r,authorizedBalance:i,authorizedSpendsHash:this.computeAuthorizedSpendsHash(t,r),availableUTXOs:[],availableBalance:n,vaultAddress:e,positionId:t,timestamp:Date.now()}}async calculateTrustedBalance(t,e,o){if(o)return await this.calculateBalance(t,e);const r=this.config.minConfirmations||6,i=await this.bitcoinProvider.getUTXOSet(e,r),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,o,r){return(await this.getAvailableUTXOs(t,e)).some((t=>t.txid===o&&t.vout===r))}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 o;o=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const r=new ethers.providers.StaticJsonRpcProvider(o,{name:"any",chainId: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"}],r);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 o=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;if(0===e.length)return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[o,[]]));const r=e.map((t=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.txid)),o=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(),o,t.targetAmount.toString(),t.timestamp]))}));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[o,r]))}};var I=class{constructor(t){this.termManagerAddress=t.termManagerAddress,this.loanOpsManagerAddress=t.loanOpsManagerAddress,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(t,{name:"any",chainId:this.chainId}),o=[{inputs:[],name:"liquidationThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],r=new ethers.Contract(this.loanOpsManagerAddress,o,e),i=await r.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 o=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),r=[{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,r,o),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}),o=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),r=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"}],o);return await i.getAuthorizedSpendsHash(r)}catch(t){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",t.message),new Error(`Failed to fetch authorized spends hash: ${t.message}`)}}};function P(t){return new I(t)}var x=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 o=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress),r=await this.config.priceOracle.getBTCPrice(),i=P({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[n,a]=await Promise.all([i.getLiquidationThreshold(),i.getTermFees(e.selectedTerm)]),s=b(e.termStartTimestamp,e.selectedTerm),c=T(s.isExpired,s.daysIntoGracePeriod),l=v(o.availableBalance,r,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;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,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:r,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:s.termDurationDays,termLengthDays:s.termLengthDays,isExpired:s.isExpired,daysUntilExpiry:s.daysUntilExpiry,daysIntoGracePeriod:s.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:n,originationFeeBps:a.originationFeeBps,extensionFeeBps:a.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 o=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress,!0),r=await this.config.priceOracle.getBTCPrice(),i=P({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[n,a]=await Promise.all([i.getLiquidationThreshold(),i.getTermFees(e.selectedTerm)]),s=b(e.termStartTimestamp,e.selectedTerm),c=T(s.isExpired,s.daysIntoGracePeriod),l=v(o.availableBalance,r,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;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,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:r,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:s.termDurationDays,termLengthDays:s.termLengthDays,isExpired:s.isExpired,daysUntilExpiry:s.daysUntilExpiry,daysIntoGracePeriod:s.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:n,originationFeeBps:a.originationFeeBps,extensionFeeBps:a.extensionFeeBps,timestamp:Date.now()}}async queryPositionState(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let o;o=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const r=new ethers.providers.StaticJsonRpcProvider(o,{name:"any",chainId:this.config.chainId}),i=new ethers.Contract(this.config.contractAddress,[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],r),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:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],r),s=await a.getPositionDetails(e),l=Number(s.status);if(l<0||l>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:",l,"(expected 0-7)"),console.error(" Full position:",JSON.stringify(s,null,2)),new Error(`Invalid position status decoded from contract: ${l} (expected 0-7). This indicates an ABI decoding issue. Position: ${e}`);const u=Number(s.expiryAt),d=Number(s.selectedTerm),h=(m=d,0===(p=u)?0:p-30*m*86400);var p,m;return{positionId:s.positionId,pkpId:s.pkpId,borrower:s.borrower,vaultAddress:this.parseVaultAddress(s.vaultAddress,c(this.config.chain)),ucdDebt:BigInt(s.ucdDebt.toString()),termStartTimestamp:h,selectedTerm:d,status:l,expiryAt:u}}parseVaultAddress(t,e){try{const o=JSON.parse(t);if("object"==typeof o&&null!==o){if("sepolia"===e.toLowerCase())return o.regtest||o.testnet||o.mainnet;return"testnet"===l(e)&&(o.testnet||o.regtest)||o.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,o,r){const i=await this.getVaultSnapshot(t);return function(t,o,r,i){const n=o+r;return 0n===n||Number(t*e*10000n/n)>=i}(i.collateralValueUsd,i.ucdDebt,o,r)}};var B=1000000000000n,C=100000000000000n,U=[{inputs:[{internalType:"uint256",name:"term",type:"uint256"}],name:"getTermFees",outputs:[{internalType:"uint256",name:"originationFee",type:"uint256"},{internalType:"uint88",name:"extensionFee",type:"uint88"}],stateMutability:"view",type:"function"}],k=[{inputs:[{internalType:"uint256",name:"_termMonths",type:"uint256"}],name:"isValidTerm",outputs:[{internalType:"bool",name:"",type:"bool"}],stateMutability:"view",type:"function"}],D=[{inputs:[{internalType:"bytes32",name:"positionId",type:"bytes32"}],name:"isExtensionPending",outputs:[{internalType:"bool",name:"",type:"bool"}],stateMutability:"view",type:"function"}];(async()=>{let e="0";try{console.log("[Extend Position Validator] Starting..."),console.log("[Extend Position Validator] DEBUG: globalThis.priceProviders:",globalThis.priceProviders),console.log("[Extend Position Validator] DEBUG: priceProviders type:",typeof globalThis.priceProviders),globalThis.priceProviders&&(console.log("[Extend Position Validator] DEBUG: priceProviders isArray:",Array.isArray(globalThis.priceProviders)),console.log("[Extend Position Validator] DEBUG: priceProviders length:",globalThis.priceProviders.length),globalThis.priceProviders.length>0&&console.log("[Extend Position Validator] DEBUG: First provider:",globalThis.priceProviders[0])),e="0";const r=globalThis.chain,n=globalThis.bitcoinProviderUrl,a=globalThis.selectedTerm;if(!r)throw new Error('Missing required parameter: "chain"');if(!n)throw new Error('Missing required parameter: "bitcoinProviderUrl"');if(!a||"number"!=typeof a)throw new Error('Missing required parameter: "selectedTerm"');const d=m.verifyTermPositionActionAuthorizationStructure(globalThis.auth),p=d.mode;let g,w,f,b,T,v,I;if("prod"===p?(w=c(r),f=l(w),g=function(t){for(const e of Object.values(i)){const o=s[e].find((e=>e.url===t));if(o)return o}const e=Object.entries(s).flatMap((([t,e])=>e.map((e=>`${t}: ${e.name} (${e.url})`)))).join(", ");throw new Error(`Bitcoin provider not approved. Provided URL: ${t}. Approved providers: ${e}`)}(n)):(w=r,f="sepolia"===r?"testnet":"regtest",g={name:"Custom Provider",url:n,minConfirmations:1,network:f}),"dev"!==p){u(w);throw new Error("Production mode contract addresses not yet configured. Use dev mode for testing.")}if(!globalThis.contractAddresses||"object"!=typeof globalThis.contractAddresses)throw new Error('Dev mode requires "contractAddresses" in globalThis');if(b=globalThis.contractAddresses.PositionManager,T=globalThis.contractAddresses.LoanOperationsManagerModule,v=globalThis.contractAddresses.TermManagerModule,I=globalThis.contractAddresses.UCDController,!(b&&T&&v&&I))throw new Error("Missing required contract addresses");const P=globalThis.customRpcUrl,M="prod"===p?u(w):d.chainId,O=P?new ethers.providers.JsonRpcProvider(P):new ethers.providers.JsonRpcProvider("sepolia"===r?"https://rpc.sepolia.org":"https://eth.llamarpc.com"),F=new y({providerUrl:g.url,network:g.network}),N=new S(globalThis.priceProviders,void 0),z=(o={contractAddress:T,chain:r,chainId:M,rpcUrl:P,bitcoinProvider:F,minConfirmations:"prod"===p?g.minConfirmations:1},new $(o));e="1";const L=function(t){return new x(t)}({contractAddress:b,termManagerAddress:v,loanOpsManagerAddress:T,chain:r,chainId:M,rpcUrl:P,vaultBalance:z,priceOracle:N});console.log("[Extend Position Validator] Using fast balance path (skipUTXOValidation: true)");const R=await L.getVaultSnapshot(d.positionId,!0),q=await N.getBTCPrice(),X=BigInt(q.toString());if(X<B||X>C)throw new Error(`Bitcoin price ${X} is outside acceptable range (1000000000000 - 100000000000000)`);e="2";if(!await m.verifyExtendAuthorization(d,R.borrower))throw new Error("Caller not authorized");e="3";if(![2,3].includes(R.status))throw new Error(`Invalid position status for extension: ${function(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})`}}(R.status)}. Must be ACTIVE or EXPIRED.`);const H=d.positionId.startsWith("0x")?d.positionId:`0x${d.positionId.padStart(64,"0")}`,_=new ethers.Contract(b,[...D],O),V=new ethers.Contract(v,[...k,...U],O),[G,j]=await Promise.all([_.isExtensionPending(H),V.isValidTerm(a)]);if(G)throw new Error("Extension already in progress for this position. Please wait for the current extension to complete.");if(!j)throw new Error(`Invalid extension term: ${a} months. This term is not configured in TermManager.`);if(R.isLiquidatable)throw new Error("Position is liquidatable - extension rejected. Add collateral first.");const K=R.selectedTerm,J=K+a,W=65535;if(J>W)throw new Error(`Extension would cause term overflow: ${K} + ${a} = ${J} months, exceeds max ${W} months`);const Q=Math.floor(Date.now()/1e3),Y=86400,Z=12===a?365*Y:a*30*Y,tt=R.expiryAt+Z,et=3===R.status;if(tt-Q>2*Z)throw new Error("Invalid requested expiry date: cannot extend beyond double term period from now");if(et&&tt<=Q)throw new Error("Invalid requested expiry date: extension must extend into the future for expired loans");e="4";const[,ot]=await V.getTermFees(a),rt=BigInt(ot.toString());e="5";const it=R.ucdDebt*rt/10000n,nt=R.ucdDebt+it;e="6";const at=function(e,o){if(e<0n)throw new Error("BTC amount cannot be negative");if(o<=0n)throw new Error("BTC price must be positive");return e*o/t}(R.availableBTCSats,X),st=A(at,nt);if(!function(t,e,o){if(o<0||o>1e6)throw new Error("Minimum ratio must be between 0 and 1000000 bps (0% to 10000%)");return A(t,e)>=o}(at,nt,R.liquidationThresholdBps))throw new Error(`Insufficient collateral after extension: ${E(st)}% < ${E(R.liquidationThresholdBps)}%`);e="7";const ct=10,lt=h()+ct,ut=ethers.utils.defaultAbiCoder.encode(["bytes32","uint256","uint256","uint256","uint256"],[d.positionId,a,X.toString(),R.availableBTCSats.toString(),lt]),dt=ethers.utils.keccak256(ut),ht=globalThis.publicKey;if(!ht)throw new Error("Missing required parameter: publicKey");e="8";const pt=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(dt),publicKey:"0x"+ht,sigName:"extendPositionAuth"});Lit.Actions.setResponse({response:JSON.stringify({approved:!0,positionId:d.positionId,selectedTerm:a,btcPrice:X.toString(),availableBTCBalance:R.availableBTCSats.toString(),extensionFee:it.toString(),newTotalDebt:nt.toString(),newCollateralRatioBps:st.toString(),signature:pt,timestamp:d.timestamp,quantumTimestamp:lt,validatorPkp:ht})})}catch(t){console.error(`[Extend Position Validator] Failed at step ${e}:`,t.message),Lit.Actions.setResponse({response:JSON.stringify({approved:!1,reason:t.message||t.toString(),failedStep:e,positionId:globalThis.auth?.positionId,timestamp:Date.now()})})}var o})()})();
@@ -0,0 +1 @@
1
+ 5324bdbab36af9a57ce6799bbb492a020bf6fadaaf19ee62e844458c08e65433
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "extend-position-validator",
3
+ "originalSize": 112413,
4
+ "minifiedSize": 46265,
5
+ "compressionRatio": 0.5884372803857205,
6
+ "hash": "5324bdbab36af9a57ce6799bbb492a020bf6fadaaf19ee62e844458c08e65433",
7
+ "buildTime": 1768236994751,
8
+ "version": "0.1.0"
9
+ }