@gvnrdao/dh-lit-actions 0.0.288 → 0.0.293
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/address-probe-signer.meta.json +1 -1
- package/dist/address-probe.meta.json +1 -1
- package/dist/admin-liquidation-validator.action.js +1 -1
- package/dist/admin-liquidation-validator.hash +1 -1
- package/dist/admin-liquidation-validator.meta.json +5 -5
- package/dist/always-signer.meta.json +1 -1
- package/dist/authorization-dummy-b.meta.json +1 -1
- package/dist/authorization-dummy.meta.json +1 -1
- package/dist/btc-transaction-signer.action.js +1 -1
- package/dist/btc-transaction-signer.hash +1 -1
- package/dist/btc-transaction-signer.meta.json +5 -5
- package/dist/btc-withdrawal.action.js +1 -1
- package/dist/btc-withdrawal.hash +1 -1
- package/dist/btc-withdrawal.meta.json +5 -5
- package/dist/encrypt-parameter.meta.json +1 -1
- package/dist/ethers-version-probe.action.js +1 -0
- package/dist/ethers-version-probe.hash +1 -0
- package/dist/ethers-version-probe.meta.json +9 -0
- package/dist/extend-position-validator.action.js +1 -1
- package/dist/extend-position-validator.hash +1 -1
- package/dist/extend-position-validator.meta.json +5 -5
- package/dist/liquidation-validator.action.js +1 -1
- package/dist/liquidation-validator.hash +1 -1
- package/dist/liquidation-validator.meta.json +5 -5
- package/dist/loan-vault-btc-balance.action.js +1 -1
- package/dist/loan-vault-btc-balance.hash +1 -1
- package/dist/loan-vault-btc-balance.meta.json +5 -5
- package/dist/pkp-validator.meta.json +1 -1
- package/dist/price-oracle.action.js +1 -1
- package/dist/price-oracle.hash +1 -1
- package/dist/price-oracle.meta.json +5 -5
- package/dist/process-payment-sign-only.meta.json +1 -1
- package/dist/process-payment-validator.action.js +1 -1
- package/dist/process-payment-validator.hash +1 -1
- package/dist/process-payment-validator.meta.json +5 -5
- package/dist/ucd-mint-validator.action.js +1 -1
- package/dist/ucd-mint-validator.hash +1 -1
- package/dist/ucd-mint-validator.meta.json +5 -5
- package/package.json +1 -1
- package/pkg-dist/pkg-src/constants/chunks/lit-actions-registry.d.ts.map +1 -1
- package/pkg-dist/pkg-src/index.d.ts +1 -0
- package/pkg-dist/pkg-src/index.d.ts.map +1 -1
- package/pkg-dist/pkg-src/index.js +2570 -45
- package/pkg-dist/pkg-src/index.js.map +4 -4
- package/pkg-dist/pkg-src/interfaces/chunks/diamond-hands-lit-actions.i.d.ts +2 -0
- package/pkg-dist/pkg-src/interfaces/chunks/diamond-hands-lit-actions.i.d.ts.map +1 -1
- package/pkg-dist/src/constants/index.d.ts +1 -1
- package/pkg-dist/src/constants/index.d.ts.map +1 -1
- package/pkg-dist/src/constants/message-hash-domains.d.ts +14 -0
- package/pkg-dist/src/constants/message-hash-domains.d.ts.map +1 -1
- package/pkg-dist/src/modules/authorization.module.d.ts +49 -6
- package/pkg-dist/src/modules/authorization.module.d.ts.map +1 -1
- package/pkg-dist/src/modules/bitcoin-provider-registry.d.ts +24 -0
- package/pkg-dist/src/modules/bitcoin-provider-registry.d.ts.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
var _LIT_ACTION_=(()=>{var e=Object.defineProperty,t=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a={};((t,r)=>{for(var i in r)e(t,i,{get:r[i],enumerable:!0})})(a,{main:()=>main});var o=11e3,n=1000000000000000000n,s=1000000000000n,c=100000000000000n,l=60,u=10,d=8e3;function p(e){switch(e){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(${e})`}}var h="DiamondHands.Protocol",g="1",m="admin-liquidation",f=(e=>(e.SEPOLIA="sepolia",e.ETHEREUM="ethereum",e.HARDHAT="hardhat",e))(f||{}),w={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},y=["infura.io","drpc.org"],b={ethereum:1,sepolia:11155111,hardhat:1337};function T(e){const t=e.toLowerCase().trim();if("sepolia"===t)return"sepolia";if("ethereum"===t||"mainnet"===t)return"ethereum";if("hardhat"===t)return"hardhat";throw new Error(`Unsupported EVM chain: "${e}". Supported chains: ${Object.values(f).join(", ")}`)}function P(e){return b[e]}function v(e){if(!0===e)return!0;if(!1===e)return!1;const t=globalThis;return!0===t.debugAction||(!0===t.LIT_ACTION_DEBUG||("true"===t.LIT_ACTION_DEBUG||function(){try{if("undefined"==typeof process||!process.env)return!1;const e=process.env.LIT_ACTION_DEBUG,t=process.env.DEBUG;return"1"===e||"true"===e||"1"===t||"true"===t}catch{return!1}}()))}function S(...e){v()&&console.log(...e)}var E=class{stepStart(e,t){}stepEnd(e,t){}log(e,t,...r){}warn(e,t){}error(e,t){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},A=class{constructor(e){this.stepStartTimes=new Map,this.stepDurations=new Map,this.TIMEOUT_MS=3e4,this.actionName=e,this.executionStartTime=Date.now();const t=Date.now(),r=Math.random().toString(36).substring(2,9);this.executionId=`exec_${t}_${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(e,t){this.stepStartTimes.set(e,Date.now());const r=Date.now()-this.executionStartTime,i=this.TIMEOUT_MS-r;console.log(`[Step ${e}] ${t}...`),console.log(`[Step ${e}] Elapsed: ${r}ms | Remaining: ${i}ms`)}stepEnd(e,t=5e3){const r=this.stepStartTimes.get(e);if(!r)return console.warn(`[Step ${e}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const i=Date.now()-r;this.stepDurations.set(e,i),console.log(`[Step ${e}] Duration: ${i}ms`),i>t&&console.warn(`\u26a0\ufe0f [Step ${e}] Took ${i}ms (> ${t}ms threshold)`);const a=Date.now()-this.executionStartTime,o=this.TIMEOUT_MS-a;o<5e3&&console.warn(`\u26a0\ufe0f [Step ${e}] Time remaining: ${o}ms (approaching timeout)`)}log(e,t,...r){r.length>0?console.log(`[Step ${e}] ${t}`,...r):console.log(`[Step ${e}] ${t}`)}warn(e,t){console.warn(`\u26a0\ufe0f [Step ${e}] ${t}`)}error(e,t){console.error(`\u274c [Step ${e}] ${t}`)}getElapsed(){return Date.now()-this.executionStartTime}getRemaining(){return this.TIMEOUT_MS-this.getElapsed()}summary(){const e=Date.now()-this.executionStartTime,t=this.TIMEOUT_MS-e;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: ${(e/1e3).toFixed(2)}s`),console.log(`[${this.actionName}] Time remaining: ${t}ms`),this.stepDurations.size>0){console.log(`[${this.actionName}] Step breakdown:`);for(const[t,r]of this.stepDurations.entries()){const i=(r/e*100).toFixed(1);console.log(`[${this.actionName}] Step ${t}: ${r}ms (${i}%)`)}}e>2e4&&console.warn(`\u26a0\ufe0f [${this.actionName}] Slow execution: ${(e/1e3).toFixed(2)}s (${t}ms remaining)`),t<5e3&&console.warn(`\u26a0\ufe0f [${this.actionName}] CRITICAL: Only ${t}ms remaining before timeout!`),console.log(`[${this.actionName}] ========================================`)}getExecutionId(){return this.executionId}isDebugEnabled(){return!0}},I=class{static create(e="LIT Action",t){return v(t)?new A(e):new E}};function $(e){if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}}function C(e){return e instanceof Error&&"AbortError"===e.name}var B=class{constructor(e){this.config=e,this.timeout=e.timeout||15e3,this.logger=I.create("BitcoinDataProvider")}async getCurrentBlockHeight(){if(this.logger.stepStart("0","getCurrentBlockHeight"),this.config.rpcHelper){const e=await this.config.rpcHelper.getBlockCount();return this.logger.stepEnd("0"),e}const e=new AbortController,t=setTimeout((()=>e.abort()),this.timeout);try{const r=Date.now(),i=await fetch(`${this.config.providerUrl}/blocks/tip/height`,{signal:e.signal,headers:{Accept:"text/plain","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(t),S(`[BitcoinDataProvider] blocks/tip/height ${Date.now()-r}ms (timeout budget ${this.timeout}ms)`),!i.ok)throw new Error(`Failed to fetch block height: ${i.status} ${i.statusText}`);const a=await i.text();if(a.trim().startsWith("<!DOCTYPE")||a.trim().startsWith("<html"))throw new Error("Bitcoin RPC endpoint returned HTML instead of block height. URL may be pointing to web interface instead of API endpoint. Expected: plain text number, Got: HTML document. Check that URL ends with /api/esplora (not root /)");const o=parseInt(a.trim(),10);if(isNaN(o)){const e=a.length>200?a.substring(0,200)+"... (truncated)":a;throw new Error(`Invalid block height response: ${e}`)}return this.logger.stepEnd("0"),o}catch(e){if(clearTimeout(t),this.logger.stepEnd("0"),C(e))throw new Error(`Request timeout after ${this.timeout}ms`);throw e}}async getUTXOs(e){this.logger.log("0",`Original address: "${e}"`);const t=this.stripNetworkPrefix(e);if(this.logger.log("0",`Cleaned address: "${t}"`),this.config.rpcHelper)return await this.fetchUTXOsFromRPC(t);try{return await this.fetchUTXOsFromProvider(this.config.providerUrl,t)}catch(e){if(this.logger.warn("0",`Primary provider failed: ${$(e)}`),this.config.fallbackProviders&&this.config.fallbackProviders.length>0){const e=[...this.config.fallbackProviders].sort(((e,t)=>e.priority-t.priority));for(const r of e)try{return this.logger.log("0",`Trying fallback: ${r.name} (${r.url})`),await this.fetchUTXOsFromProvider(r.url,t)}catch(e){this.logger.warn("0",`Fallback ${r.name} failed: ${$(e)}`);continue}}throw new Error(`Failed to fetch UTXOs: ${$(e)}`)}}stripNetworkPrefix(e){return e.replace(/^(REGTEST_|TESTNET_|MAINNET_)/,"")}async fetchUTXOsFromRPC(e){if(!this.config.rpcHelper)throw new Error("RPC helper not configured");const t=this.config.rpcWallet||"";return(await this.config.rpcHelper.listUnspent(t,e)).map((e=>({txid:e.txid,vout:e.vout,satoshis:BigInt(Math.round(1e8*e.amount)),confirmations:e.confirmations})))}parseBlockstreamUTXOs(e,t){if(!Array.isArray(e))throw new Error("Invalid Blockstream response format: expected an array");const r=e,i=[];for(const e of r){let r=0;e.status&&"object"==typeof e.status?e.status.confirmed&&(r=void 0!==e.status.block_height&&e.status.block_height>0?t-e.status.block_height+1:1):void 0!==e.confirmations&&(r=e.confirmations);const a=e.txid,o=void 0!==e.vout?e.vout:e.n,n=void 0!==e.value?e.value:e.satoshis;if(void 0===a||void 0===o||void 0===n)throw new Error("Invalid UTXO row: missing txid, vout/n, or value/satoshis");i.push({txid:a,vout:o,satoshis:BigInt(n),confirmations:r})}return i}async fetchUTXOsFromProvider(e,t){this.logger.stepStart("1","fetchUTXOsFromProvider");const r=`${e}/address/${t}/utxos`;this.logger.log("1",`Fetching UTXOs from ${r}`);const i=new AbortController,a=setTimeout((()=>i.abort()),this.timeout);try{const e=Date.now(),[o,n]=await Promise.all([this.getCurrentBlockHeight(),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),S(`[BitcoinDataProvider] parallel(tipHeight + GET utxos) wall ${Date.now()-e}ms endpoint tail .../${t.slice(0,12)}.../utxos (timeout budget ${this.timeout}ms)`),this.logger.log("1",`Current block height: ${o}`),!n.ok)throw new Error(`Bitcoin provider error: ${n.status} ${n.statusText}`);const s=await n.text();if(s.trim().startsWith("<!DOCTYPE")||s.trim().startsWith("<html"))throw new Error("Bitcoin RPC endpoint returned HTML instead of JSON. URL may be pointing to web interface instead of API endpoint. Expected: JSON array of UTXOs, Got: HTML document. Check that URL ends with /api/esplora (not root /)");let c;try{c=JSON.parse(s)}catch(e){const t=s.length>200?s.substring(0,200)+"... (truncated)":s;throw new Error(`Failed to parse JSON response: ${$(e)}. Response: ${t}`)}this.logger.log("1",`Raw API Response: ${JSON.stringify(c,null,2)}`);const l=this.parseBlockstreamUTXOs(c,o);return this.logger.stepEnd("1"),l}catch(e){if(clearTimeout(a),this.logger.stepEnd("1"),C(e))throw new Error(`Request timeout after ${this.timeout}ms`);throw e}}async getUTXOSet(e,t){let r=[],i=null;for(let t=1;t<=3;t++)try{this.logger.log("2",`UTXO query attempt ${t}/3...`),r=await this.getUTXOs(e),this.logger.log("2",`UTXO query succeeded on attempt ${t}`),i=null;break}catch(e){i=e instanceof Error?e:new Error($(e)),this.logger.error("2",`UTXO query failed on attempt ${t}: ${$(e)}`),t<3&&(this.logger.log("2","Waiting 500ms before retry..."),await new Promise((e=>setTimeout(e,500))))}if(i)throw new Error(`Failed to query UTXOs after 3 attempts: ${i.message}`);const a=r.reduce(((e,t)=>e+t.satoshis),0n),o=r.filter((e=>e.confirmations>=t)).reduce(((e,t)=>e+t.satoshis),0n),n=a-o;return{utxos:r,totalBalance:a,totalUTXOs:r.length,confirmedBalance:o,unconfirmedBalance:n}}async getBalance(e){const t=this.stripNetworkPrefix(e),r=`${this.config.providerUrl}/address/${t}`;this.logger.log("3",`Fetching balance from ${r}`);const i=new AbortController,a=setTimeout((()=>i.abort()),this.timeout);try{const e=Date.now(),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),S(`[BitcoinDataProvider] GET address chain_stats ${Date.now()-e}ms (timeout budget ${this.timeout}ms)`),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const o=await t.json();if(!o.chain_stats)throw new Error("Invalid response: missing chain_stats");const n=o.chain_stats.funded_txo_sum,s=o.chain_stats.spent_txo_sum;if(void 0===n||void 0===s)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${n}, spent_txo_sum: ${s}`);const c=BigInt(n-s);return this.logger.log("3",`Balance: ${c.toString()} sats`),this.logger.log("3",` - Funded: ${n} sats, Spent: ${s} sats`),c}catch(e){if(clearTimeout(a),C(e))throw new Error(`Request timeout after ${this.timeout}ms`);const r=$(e);throw this.logger.log("3",`Balance fetch failed: ${r}`),new Error(`Failed to fetch Bitcoin balance for address ${t}: ${r}`)}}async getTransaction(e){if(this.config.rpcHelper)try{const t=this.config.rpcWallet||"",r=await this.config.rpcHelper.getTransaction(t,e);return{txid:r.txid,confirmations:r.confirmations||0}}catch(e){const t=$(e),r=null!==e&&"object"==typeof e&&"code"in e&&"number"==typeof e.code?e.code:void 0;if(t.includes("Invalid or non-wallet transaction")||-5===r)return null;throw e}const t=new AbortController,r=setTimeout((()=>t.abort()),this.timeout);try{const i=Date.now(),a=await fetch(`${this.config.providerUrl}/tx/${e}`,{signal:t.signal});if(clearTimeout(r),S(`[BitcoinDataProvider] GET tx/${e.slice(0,10)}\u2026 ${Date.now()-i}ms (timeout budget ${this.timeout}ms)`),!a.ok){if(404===a.status)return null;throw new Error(`Bitcoin provider error: ${a.status} ${a.statusText}`)}const o=await a.json();return o.status?{txid:o.txid??e,confirmations:o.status.confirmed&&o.status.block_height?1:0}:null}catch(e){if(clearTimeout(r),C(e))throw new Error(`Request timeout after ${this.timeout}ms`);if($(e).includes("404"))return null;throw e}}async getTransactionOutputs(e){if(!this.config.providerUrl)return null;const t=new AbortController,r=setTimeout((()=>t.abort()),this.timeout);try{const i=await fetch(`${this.config.providerUrl}/tx/${e}`,{signal:t.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.vout&&Array.isArray(a.vout)?a.vout.map(((e,t)=>({index:t,scriptpubkey:e.scriptpubkey,scriptpubkey_address:e.scriptpubkey_address,value:e.value??0}))):null}catch(e){if(clearTimeout(r),C(e))throw new Error(`Request timeout after ${this.timeout}ms`);throw e}}};function M(e,t){if(0===e)return{termDurationDays:0,termLengthDays:30*t,isExpired:!1,daysUntilExpiry:30*t,daysIntoGracePeriod:0};const r=30*t,i=Math.floor(Date.now()/1e3)-e,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 U(e,t,r,i){if(!e)return r??13e3;const a=i??o,n=Math.min(t,30);return o+n*n*(a-o)/900}function O(e,t,r){const i=e*t/10000000000000000n;if(0n===r)return{collateralValueUsd:i,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:i,collateralRatioBps:Number(i*n*10000n/r)}}function D(e){return e/100}var x=class e{constructor(t,r,i){if(r&&Array.isArray(r))this.sources=r;else{if(!Array.isArray(t))throw new Error("Price oracle requires a priceProviders array \u2014 defaults and fallbacks have been removed. Pass 3 distinct {name, apiKey} entries from the supported set: "+e.ACTIVE_PROVIDER_NAMES.join(", "));this.sources=this.buildSources(t)}this.sources.sort(((e,t)=>e.priority-t.priority)),this.validateProviderCount()}validateProviderCount(){if(this.sources.length<e.MIN_DISTINCT_PROVIDERS)throw new Error(`Price oracle requires at least ${e.MIN_DISTINCT_PROVIDERS} eligible providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported active providers: ${e.ACTIVE_PROVIDER_NAMES.join(", ")}`)}buildSources(t){if(0===t.length)throw new Error("priceProviders must be a non-empty array of 3 distinct providers, each with an apiKey.");const r=[],i=new Set;let a=1;for(const o of t){if(!o||"string"!=typeof o.name)throw new Error("priceProviders entries must be {name, apiKey, apiSecret?} objects");const t=o.name.toLowerCase();if(!e.ACTIVE_PROVIDER_NAMES.includes(t))throw new Error(`Unsupported price provider "${o.name}". Supported: ${e.ACTIVE_PROVIDER_NAMES.join(", ")}`);if(i.has(t))throw new Error(`Duplicate price provider "${t}" \u2014 the oracle requires 3 DISTINCT providers so a single upstream failure or manipulation cannot compromise consensus. Use 3 different provider names from: ${e.ACTIVE_PROVIDER_NAMES.join(", ")}`);switch(i.add(t),t){case"binance":if(!o.apiKey)throw new Error("Binance provider requires an apiKey");r.push(this.createBinanceSource(o.apiKey,a++));break;case"coinbase":if(!o.apiKey)throw new Error("Coinbase provider requires an apiKey");r.push(this.createCoinbaseSource(o.apiKey,a++));break;case"cryptocompare":if(!o.apiKey)throw new Error("CryptoCompare provider requires an apiKey");r.push(this.createCryptoCompareSource(o.apiKey,a++));break;case"coinmarketcap":if(!o.apiKey)throw new Error("CoinMarketCap provider requires an apiKey");r.push(this.createCoinMarketCapSource(o.apiKey,a++));break;case"coingecko":if(!o.apiKey)throw new Error("CoinGecko provider requires an apiKey (Demo or Pro). Unauthenticated CoinGecko access is not accepted \u2014 the free tier is rate-limited and edge-cached, which breaks median consensus under load.");r.push(this.createCoinGeckoSource(o.apiKey,a++));break;default:throw new Error(`Unhandled provider: ${t}`)}}if(r.length<e.MIN_DISTINCT_PROVIDERS)throw new Error(`Insufficient price providers: need at least ${e.MIN_DISTINCT_PROVIDERS} distinct entries, got ${r.length}.`);return this.selectSampledSources(r)}createCryptoCompareSource(e,t){return{name:"CryptoCompare",fetchPrice:async()=>{const t=new URL("https://min-api.cryptocompare.com/data/price");t.searchParams.set("fsym","BTC"),t.searchParams.set("tsyms","USDT"),t.searchParams.set("api_key",e);const r=await(async e=>{const t=new AbortController,r=setTimeout((()=>t.abort()),5e3);try{const i=await fetch(e,{signal:t.signal,headers:{Accept:"application/json"}});if(clearTimeout(r),!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}catch(e){if(clearTimeout(r),"AbortError"===e.name)throw new Error("Request timeout after 5000ms");throw e}})(t.toString()),i=Number(r?.USDT);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CryptoCompare price payload");return i},priority:t}}createFetchJson(){return async(e,t)=>{const r=new AbortController,i=setTimeout((()=>r.abort()),5e3);try{const i=await fetch(e,{headers:{Accept:"application/json",...t||{}},signal:r.signal});if(!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}finally{clearTimeout(i)}}}getSelectionSeed(){const e="undefined"!=typeof process?process.env.PRICE_PROVIDER_SELECTION_SEED:void 0,t=e?Number(e):NaN;return Number.isFinite(t)?t:("undefined"!=typeof process,0,Math.floor(Date.now()+1e6*Math.random()))}selectSampledSources(e){const t=[...e];let r=this.getSelectionSeed()>>>0;for(let e=t.length-1;e>0;e--){const i=Math.floor((r=1664525*r+1013904223>>>0,r/4294967296*(e+1)));[t[e],t[i]]=[t[i],t[e]]}return t.slice(0,3).map(((e,t)=>({...e,priority:t+1})))}createCoinGeckoSource(e,t){if(!e)throw new Error("createCoinGeckoSource requires an apiKey");const r=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const t=e.startsWith("pro:"),i=t?e.slice(4):e,a=t?{"x-cg-pro-api-key":i}:{"x-cg-demo-api-key":i},o=new URL("https://api.coingecko.com/api/v3/simple/price");o.searchParams.set("ids","bitcoin"),o.searchParams.set("vs_currencies","usd");const n=await r(o.toString(),a),s=Number(n?.bitcoin?.usd);if(!Number.isFinite(s)||s<=0)throw new Error("Invalid CoinGecko price payload");return s},priority:t}}createBinanceSource(e,t){if(!e)throw new Error("createBinanceSource requires an apiKey");const r=this.createFetchJson();return{name:"Binance",fetchPrice:async()=>{const t=new URL("https://api.binance.com/api/v3/ticker/price");t.searchParams.set("symbol","BTCUSDT");const i=await r(t.toString(),{"X-MBX-APIKEY":e}),a=Number(i?.price);if(!Number.isFinite(a)||a<=0)throw new Error("Invalid Binance price payload");return a},priority:t}}createCoinbaseSource(e,t){if(!e)throw new Error("createCoinbaseSource requires an apiKey");const r=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const t=await r("https://api.coinbase.com/v2/prices/BTC-USDT/spot",{"CB-ACCESS-KEY":e}),i=Number(t?.data?.amount);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid Coinbase price payload");return i},priority:t}}createCoinMarketCapSource(e,t){const r=this.createFetchJson();return{name:"CoinMarketCap",fetchPrice:async()=>{const t=new URL("https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest");t.searchParams.set("symbol","BTC"),t.searchParams.set("convert","USDT");const i=await r(t.toString(),{"X-CMC_PRO_API_KEY":e}),a=Number(i?.data?.BTC?.[0]?.quote?.USDT?.price??i?.data?.BTC?.quote?.USDT?.price);if(!Number.isFinite(a)||a<=0)throw new Error("Invalid CoinMarketCap price payload");return a},priority:t}}async getBTCPrice(){const e=Date.now();if(3!==this.sources.length)throw new Error(`Price oracle requires exactly 3 providers. Currently configured: ${this.sources.length}`);const t=new Promise(((e,t)=>{setTimeout((()=>{t(new Error("Price oracle global timeout after 8000ms"))}),8e3)})),r=Promise.all(this.sources.map((async e=>{const t=Date.now(),r=await e.fetchPrice();return console.log(`[Price Oracle] \u2705 [+${Date.now()-t}ms] ${e.name}: $${r.toLocaleString()}`),r}))),i=[...await Promise.race([r,t])].sort(((e,t)=>e-t)),a=i[1];if((i[2]-i[0])/i[0]>.01){const e=this.sources.map((e=>e.name)).join(", "),t=i.map((e=>`$${e.toLocaleString()}`)).join(", ");throw new Error(`Price consensus failed: prices not within 1% tolerance. Providers: [${e}]. Prices: [${t}]`)}const o=Math.round(100*a),n=1000000n*BigInt(o),s=Date.now()-e;return console.log(`[Price Oracle] Median price: $${a.toLocaleString()} \u2014 total time: ${s}ms`),console.log(`[Price Oracle] Price with 8 decimals: ${n}`),n}async getBTCPriceConsensus(){console.log("[Price Oracle] Fetching BTC price with consensus...");const e=(await Promise.allSettled(this.sources.map((async e=>{const t=await e.fetchPrice();return{source:e.name,price:t}})))).filter((e=>"fulfilled"===e.status)).map((e=>e.value));if(0===e.length)throw new Error("No price sources returned data");console.log(`[Price Oracle] Got prices from ${e.length}/${this.sources.length} sources:`),e.forEach((e=>{console.log(` ${e.source}: $${e.price.toLocaleString()}`)}));const t=e.map((e=>e.price));t.sort(((e,t)=>e-t));const r=t[Math.floor(t.length/2)],i=t[0],a=t[t.length-1]/i,o=e.filter((e=>Math.abs(e.price-r)/r<=.02));if(a>1.05&&o.length===e.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(a-1)).toFixed(1)}% spread)`);if(o.length<e.length){const t=e.filter((e=>!o.find((t=>t.source===e.source))));if(console.log(`[Price Oracle] \u26a0\ufe0f Detected ${t.length} outlier(s):`),t.forEach((e=>{const t=Math.abs(e.price-r)/r;console.log(` ${e.source}: $${e.price.toLocaleString()} (${(100*t).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((e=>e.price));i.sort(((e,t)=>e-t));const a=i[Math.floor(i.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${a.toLocaleString()}`);const n=Math.round(100*a);return 1000000n*BigInt(n)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${r.toLocaleString()}`);const n=Math.round(100*r);return 1000000n*BigInt(n)}};x.ACTIVE_PROVIDER_NAMES=["cryptocompare","coinbase","binance","coinmarketcap","coingecko"],x.MIN_DISTINCT_PROVIDERS=3;var k=x;function q(){const e=Math.floor(Date.now()/1e3);return t=e,Math.floor(t/l)*l;var t}var R=class{constructor(e){this.config=e,this.bitcoinProvider=e.bitcoinProvider}async calculateBalance(e,t){const r=await this.bitcoinProvider.getBalance(t),i=await this.getAuthorizedSpendsFromContract(e),a=i.reduce(((e,t)=>e+t.satoshis),0n),o=r>a?r-a:0n;return{totalUTXOs:[],totalBalance:r,authorizedUTXOs:i,authorizedBalance:a,authorizedSpendsHash:this.computeAuthorizedSpendsHash(e,i),availableUTXOs:[],availableBalance:o,vaultAddress:t,positionId:e,timestamp:Date.now()}}async calculateTrustedBalance(e,t,r){if(r)return await this.calculateBalance(e,t);const i=this.config.minConfirmations||6,a=await this.bitcoinProvider.getUTXOSet(t,i),o=await this.getAuthorizedSpendsFromContract(e);for(const e of o){if(a.utxos.some((t=>t.txid===e.txid&&t.vout===e.vout)))throw new Error(`Authorized UTXO ${e.txid}:${e.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=o.reduce(((e,t)=>e+t.satoshis),0n),s=a.utxos.filter((e=>!this.isUTXOAuthorized(e,o))),c=s.reduce(((e,t)=>e+t.satoshis),0n),l=this.computeAuthorizedSpendsHash(e,o);return{totalUTXOs:a.utxos,totalBalance:a.totalBalance,authorizedUTXOs:o,authorizedBalance:n,authorizedSpendsHash:l,availableUTXOs:s,availableBalance:c,vaultAddress:t,positionId:e,timestamp:Date.now()}}async getTrustedBalance(e,t){return(await this.calculateTrustedBalance(e,t)).availableBalance}async getAvailableUTXOs(e,t){return(await this.calculateTrustedBalance(e,t)).availableUTXOs}async isUTXOAvailable(e,t,r,i){return(await this.getAvailableUTXOs(e,t)).some((e=>e.txid===r&&e.vout===i))}isUTXOAuthorized(e,t){return t.some((t=>t.txid===e.txid&&t.vout===e.vout))}async getAuthorizedSpendsFromContract(e){const t=e.startsWith("0x")?e:`0x${e.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({url:r,timeout:d},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(t)).map((t=>({txid:t.txid,vout:Number(t.vout),satoshis:BigInt(t.satoshis.toString()),positionId:e,targetAddress:t.targetAddress,targetAmount:BigInt(t.targetAmount.toString()),timestamp:Number(t.authorizedAt)})))}computeAuthorizedSpendsHash(e,t){const r=e.startsWith("0x")?e:`0x${e.padStart(64,"0")}`;if(0===t.length)return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,[]]));const i=t.map((e=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.txid)),r=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.targetAddress));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","uint32","uint256","bytes32","uint256","uint256"],[t,e.vout,e.satoshis.toString(),r,e.targetAmount.toString(),e.timestamp]))}));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,i]))}};var L=class{constructor(e){this.termManagerAddress=e.termManagerAddress,this.loanOpsManagerAddress=e.loanOpsManagerAddress,this.liquidationManagerAddress=e.liquidationManagerAddress,this.chain=e.chain,this.chainId=e.chainId,this.rpcUrl=e.rpcUrl}async getLiquidationThreshold(){try{let e;e=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const t=new ethers.providers.StaticJsonRpcProvider({url:e,timeout:d},{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,t),a=await i.liquidationThreshold();return Number(a.toString())}catch(e){throw console.error("[ProtocolParameters] Error fetching liquidation threshold:",e.message),new Error(`Failed to fetch liquidation threshold: ${e.message}`)}}async getTermFees(e){try{let t;t=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const r=new ethers.providers.StaticJsonRpcProvider({url:t,timeout:d},{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(e);return{originationFeeBps:Number(o.originationFee?.toString?.()??o[0]?.toString?.()??o[0]),extensionFeeBps:Number(o.extensionFee?.toString?.()??o[1]?.toString?.()??o[1])}}catch(e){throw console.error("[ProtocolParameters] Error fetching term fees:",e.message),new Error(`Failed to fetch term fees: ${e.message}`)}}async getAuthorizedSpendsHash(e){try{const t=this.rpcUrl||await Lit.Actions.getRpcUrl({chain:this.chain}),r=new ethers.providers.StaticJsonRpcProvider({url:t,timeout:d},{name:"any",chainId:this.chainId}),i=e.startsWith("0x")?e:`0x${e.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(e){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",e.message),new Error(`Failed to fetch authorized spends hash: ${e.message}`)}}async getMaxEscalatedThreshold(){try{let e;e=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const t=new ethers.providers.StaticJsonRpcProvider({url:e,timeout:d},{name:"any",chainId:this.chainId}),r=[{inputs:[],name:"maxEscalatedThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],i=new ethers.Contract(this.liquidationManagerAddress,r,t),a=await i.maxEscalatedThreshold();return Number(a.toString())}catch(e){throw console.error("[ProtocolParameters] Error fetching max escalated threshold:",e.message),new Error(`Failed to fetch max escalated threshold: ${e.message}`)}}};function N(e){return new L(e)}var F=class{constructor(e){this.config=e}async getVaultSnapshot(e){const t=await this.queryPositionState(e);console.log(`[Vault Snapshot] Raw vault address from contract: "${t.vaultAddress}"`);const r=!0===this.config.debugOverrides?.useStubbedBtcData,[i,a]=r?this.buildStubbedBtcSnapshotInputs(t):await Promise.all([this.config.vaultBalance.calculateTrustedBalance(e,t.vaultAddress),this.config.priceOracle.getBTCPriceConsensus()]),o=N({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,liquidationManagerAddress:this.config.liquidationManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),n=this.config.termOverride??t.selectedTerm,[s,c,l]=await Promise.all([o.getLiquidationThreshold(),o.getMaxEscalatedThreshold(),o.getTermFees(n)]),u=M(t.termStartTimestamp,t.selectedTerm),d=U(u.isExpired,u.daysIntoGracePeriod,s,c),p=O(i.availableBalance,a,t.ucdDebt),h=p.collateralRatioBps<d,g=p.collateralRatioBps-d;return{positionId:t.positionId,pkpId:t.pkpId,borrower:t.borrower,vaultAddress:t.vaultAddress,ucdDebt:t.ucdDebt,termStartTimestamp:t.termStartTimestamp,selectedTerm:t.selectedTerm,status:t.status,expiryAt:t.expiryAt,previousExpiryAt:t.previousExpiryAt,totalTerm:t.totalTerm,totalBTCSats:i.totalBalance,totalUTXOs:i.totalUTXOs,authorizedSpendsSats:i.authorizedBalance,authorizedSpendsHash:i.authorizedSpendsHash,availableBTCSats:i.availableBalance,availableUTXOs:i.availableUTXOs,btcPriceUsd:a,collateralValueUsd:p.collateralValueUsd,collateralRatioBps:p.collateralRatioBps,termDurationDays:u.termDurationDays,termLengthDays:u.termLengthDays,isExpired:u.isExpired,daysUntilExpiry:u.daysUntilExpiry,daysIntoGracePeriod:u.daysIntoGracePeriod,currentLiquidationThreshold:d,isLiquidatable:h,marginToLiquidationBps:g,liquidationThresholdBps:s,originationFeeBps:l.originationFeeBps,extensionFeeBps:l.extensionFeeBps,timestamp:Date.now()}}async getVaultSnapshotFast(e){const t=await this.queryPositionState(e);console.log(`[Vault Snapshot] Raw vault address from contract: "${t.vaultAddress}"`);const r=!0===this.config.debugOverrides?.useStubbedBtcData,[i,a]=r?this.buildStubbedBtcSnapshotInputs(t):await Promise.all([this.config.vaultBalance.calculateTrustedBalance(e,t.vaultAddress,!0),this.config.priceOracle.getBTCPriceConsensus()]),o=N({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,liquidationManagerAddress:this.config.liquidationManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),n=this.config.termOverride??t.selectedTerm,[s,c,l]=await Promise.all([o.getLiquidationThreshold(),o.getMaxEscalatedThreshold(),o.getTermFees(n)]),u=M(t.termStartTimestamp,t.selectedTerm),d=U(u.isExpired,u.daysIntoGracePeriod,s,c),p=O(i.availableBalance,a,t.ucdDebt),h=p.collateralRatioBps<d,g=p.collateralRatioBps-d;return{positionId:t.positionId,pkpId:t.pkpId,borrower:t.borrower,vaultAddress:t.vaultAddress,ucdDebt:t.ucdDebt,termStartTimestamp:t.termStartTimestamp,selectedTerm:t.selectedTerm,status:t.status,expiryAt:t.expiryAt,previousExpiryAt:t.previousExpiryAt,totalTerm:t.totalTerm,totalBTCSats:i.totalBalance,totalUTXOs:i.totalUTXOs,authorizedSpendsSats:i.authorizedBalance,authorizedSpendsHash:i.authorizedSpendsHash,availableBTCSats:i.availableBalance,availableUTXOs:i.availableUTXOs,btcPriceUsd:a,collateralValueUsd:p.collateralValueUsd,collateralRatioBps:p.collateralRatioBps,termDurationDays:u.termDurationDays,termLengthDays:u.termLengthDays,isExpired:u.isExpired,daysUntilExpiry:u.daysUntilExpiry,daysIntoGracePeriod:u.daysIntoGracePeriod,currentLiquidationThreshold:d,isLiquidatable:h,marginToLiquidationBps:g,liquidationThresholdBps:s,originationFeeBps:l.originationFeeBps,extensionFeeBps:l.extensionFeeBps,timestamp:Date.now()}}buildStubbedBtcSnapshotInputs(e){const t=BigInt(this.config.debugOverrides?.stubbedAvailableBtcSats??"10000000"),r=BigInt(this.config.debugOverrides?.stubbedBtcPriceUsd??"5000000000000");return[{totalUTXOs:[],totalBalance:t,authorizedUTXOs:[],authorizedBalance:0n,authorizedSpendsHash:this.config.debugOverrides?.stubbedAuthorizedSpendsHash??ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[e.positionId,[]])),availableUTXOs:[],availableBalance:t,vaultAddress:e.vaultAddress,positionId:e.positionId,timestamp:Date.now()},r]}async queryPositionState(e){const t=e.startsWith("0x")?e:`0x${e.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({url:r,timeout:d},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:"uint40",name:"previousExpiryAt",type:"uint40"},{internalType:"uint16",name:"totalTerm",type:"uint16"}],internalType:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],i),s=await n.getPositionDetails(t),c=Number(s.status);if(c<0||c>7)throw console.error("[VaultSnapshot] INVALID STATUS - ABI decoding error detected"),console.error(" Position ID:",t),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: ${t}`);const l=Number(s.expiryAt),u=Number(s.selectedTerm),p=Number(s.totalTerm),h=Number(s.previousExpiryAt),g=(f=u,0===(m=l)?0:m-30*f*86400);var m,f;return{positionId:s.positionId,pkpId:s.pkpId,borrower:s.borrower,vaultAddress:this.parseVaultAddress(s.vaultAddress,T(this.config.chain)),ucdDebt:BigInt(s.ucdDebt.toString()),termStartTimestamp:g,selectedTerm:u,status:c,expiryAt:l,previousExpiryAt:h,totalTerm:p}}parseVaultAddress(e,t){try{const r=JSON.parse(e);if("object"==typeof r&&null!==r){if("sepolia"===t.toLowerCase())return r.regtest||r.testnet||r.mainnet;const e=function(e){return w[e]}(t);return"testnet"===e&&(r.testnet||r.regtest)||r.mainnet}}catch(e){}return e}async isLiquidatable(e){return(await this.getVaultSnapshot(e)).isLiquidatable}async getCollateralRatio(e){return(await this.getVaultSnapshot(e)).collateralRatioBps}async getAvailableBalance(e){return(await this.getVaultSnapshot(e)).availableBTCSats}async hasSufficientCollateral(e,t,r){const i=await this.getVaultSnapshot(e);return function(e,t,r,i){const a=t+r;return 0n===a||Number(e*n*10000n/a)>=i}(i.collateralValueUsd,i.ucdDebt,t,r)}};var _="function getProtocolPauseStatus() view returns (tuple(bool positionManagerPaused, bool positionManagerLiquidationsPaused, bool positionManagerCorePaused, bool loanOperationsPaused, bool collateralManagerPaused, bool liquidationManagerPaused, bool circuitBreakerPaused, bool ucdControllerPaused, bool ucdTokenPaused, bool simplePsmPaused))",V={mintUcd:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],withdrawBtc:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],processPayment:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused"],extendPosition:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],liquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],adminLiquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],btcTransactionSign:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused"]};function X(e){return{positionManagerPaused:e.positionManagerPaused,positionManagerLiquidationsPaused:e.positionManagerLiquidationsPaused,positionManagerCorePaused:e.positionManagerCorePaused,loanOperationsPaused:e.loanOperationsPaused,collateralManagerPaused:e.collateralManagerPaused,liquidationManagerPaused:e.liquidationManagerPaused,circuitBreakerPaused:e.circuitBreakerPaused,ucdControllerPaused:e.ucdControllerPaused,ucdTokenPaused:e.ucdTokenPaused,simplePsmPaused:e.simplePsmPaused}}var z=new ethers.utils.Interface(["function views() view returns (address)","function paused() view returns (bool)","function liquidationPaused() view returns (bool)","function simplePsm() view returns (address)"]),H=new ethers.utils.Interface(["function core() view returns (address)","function loanOps() view returns (address)","function collateralManager() view returns (address)","function liquidationManager() view returns (address)","function circuitBreaker() view returns (address)"]),j=new ethers.utils.Interface(["function getUcdController() view returns (address)","function ucdToken() view returns (address)"]),G=new ethers.utils.Interface(["function paused() view returns (bool)"]);async function K(e,t,r){return e.call({to:t,data:r})}async function J(e,t){const r=new ethers.utils.Interface([_]).encodeFunctionData("getProtocolPauseStatus",[]);let i;try{return function(e){const t=new ethers.utils.Interface([_]).decodeFunctionResult("getProtocolPauseStatus",e)[0];return Array.isArray(t)?X({positionManagerPaused:t[0],positionManagerLiquidationsPaused:t[1],positionManagerCorePaused:t[2],loanOperationsPaused:t[3],collateralManagerPaused:t[4],liquidationManagerPaused:t[5],circuitBreakerPaused:t[6],ucdControllerPaused:t[7],ucdTokenPaused:t[8],simplePsmPaused:t[9]}):X(t)}(await e.call({to:t,data:r}))}catch(e){i=e}try{return await async function(e,t){const r=await K(e,t,z.encodeFunctionData("views",[])),[i]=z.decodeFunctionResult("views",r),[a,o,n,s,c,l,u,d]=await Promise.all([K(e,t,z.encodeFunctionData("paused",[])),K(e,t,z.encodeFunctionData("liquidationPaused",[])),K(e,t,z.encodeFunctionData("simplePsm",[])),K(e,i,H.encodeFunctionData("core",[])),K(e,i,H.encodeFunctionData("loanOps",[])),K(e,i,H.encodeFunctionData("collateralManager",[])),K(e,i,H.encodeFunctionData("liquidationManager",[])),K(e,i,H.encodeFunctionData("circuitBreaker",[]))]),p=z.decodeFunctionResult("paused",a)[0],h=z.decodeFunctionResult("liquidationPaused",o)[0],g=z.decodeFunctionResult("simplePsm",n)[0],m=H.decodeFunctionResult("core",s)[0],f=H.decodeFunctionResult("loanOps",c)[0],w=H.decodeFunctionResult("collateralManager",l)[0],y=H.decodeFunctionResult("liquidationManager",u)[0],b=H.decodeFunctionResult("circuitBreaker",d)[0],T=await K(e,f,j.encodeFunctionData("getUcdController",[])),P=await K(e,f,j.encodeFunctionData("ucdToken",[])),v=j.decodeFunctionResult("getUcdController",T)[0],S=j.decodeFunctionResult("ucdToken",P)[0],E=async t=>{if(!t||t===ethers.constants.AddressZero)return!1;try{const r=await K(e,t,G.encodeFunctionData("paused",[]));return G.decodeFunctionResult("paused",r)[0]}catch{return!1}},[A,I,$,C,B,M,U,O]=await Promise.all([E(m),E(f),E(w),E(y),E(b),E(v),E(S),E(g)]);return X({positionManagerPaused:p,positionManagerLiquidationsPaused:h,positionManagerCorePaused:A,loanOperationsPaused:I,collateralManagerPaused:$,liquidationManagerPaused:C,circuitBreakerPaused:B,ucdControllerPaused:M,ucdTokenPaused:U,simplePsmPaused:O})}(e,t)}catch(e){const t=i instanceof Error?i.message:String(i),r=e instanceof Error?e.message:String(e);throw new Error(`getProtocolPauseStatus failed (aggregate: ${t}; decomposed: ${r})`)}}var W={hardhat:{chain:"hardhat",chainId:b.hardhat,bitcoinNetwork:w.hardhat,allowArbitraryBitcoinProvider:!0,allowArbitraryEvmRpc:!0,minBitcoinConfirmations:1},sepolia:{chain:"sepolia",chainId:b.sepolia,bitcoinNetwork:w.sepolia,allowArbitraryBitcoinProvider:!1,allowArbitraryEvmRpc:!1,minBitcoinConfirmations:1},ethereum:{chain:"ethereum",chainId:b.ethereum,bitcoinNetwork:w.ethereum,allowArbitraryBitcoinProvider:!1,allowArbitraryEvmRpc:!1,minBitcoinConfirmations:6}};var Y=[{inputs:[{internalType:"uint256",name:"chainId",type:"uint256"}],name:"getProviders",outputs:[{internalType:"bytes32[]",name:"ids",type:"bytes32[]"},{components:[{internalType:"bytes",name:"ciphertext",type:"bytes"},{internalType:"bytes32",name:"pkpId",type:"bytes32"},{internalType:"uint64",name:"addedAt",type:"uint64"}],internalType:"struct BitcoinProviderRegistry.ProviderEntry[]",name:"entries",type:"tuple[]"}],stateMutability:"view",type:"function"}];async function Q(e,t){const r=66===t.length?"0x"+t.slice(-40):t;try{const t=await Lit.Actions.Decrypt({pkpId:r,ciphertext:e}),i="string"==typeof t?t:String(t??"");if(!i)throw new Error("Decrypt returned empty plaintext");return i}catch(e){throw new Error(`Failed to decrypt Bitcoin provider URL (pkpId=${t}): ${e?.message??String(e)}`)}}async function Z(e){const{policy:t,bitcoinProviderUrl:r,ethersProvider:i,registryAddress:a}=e;if(!r||"string"!=typeof r)throw new Error("resolveBitcoinProviderForPolicy: bitcoinProviderUrl is required");if(t.allowArbitraryBitcoinProvider)return{name:"Custom Provider",url:r,minConfirmations:t.minBitcoinConfirmations,network:t.bitcoinNetwork};if(!a)throw new Error(`Bitcoin provider registry address is required on chain "${t.chain}" \u2014 set contractAddresses.BitcoinProviderRegistry. Hardcoded allowlists have been removed (see audit C-1/H-6 fix).`);const o=await async function(e,t,r){if(!r||"string"!=typeof r)throw new Error("fetchBitcoinProviderEntries: registryAddress (BitcoinProviderRegistry) is required");if(!t)throw new Error("fetchBitcoinProviderEntries: ethers provider is required");const i=new ethers.Contract(r,Y,t),[a,o]=await i.getProviders(e),n=[];for(let e=0;e<a.length;e++)n.push({providerId:a[e],ciphertext:o[e].ciphertext,pkpId:o[e].pkpId,addedAt:Number(o[e].addedAt)});return n}(t.chainId,i,a);if(0===o.length)throw new Error(`No Bitcoin providers registered for chainId=${t.chainId} in ${a}. Admin must addProvider(...) via AdminModule before LIT Actions can sign.`);const n=await async function(e,t){if(!t)throw new Error("matchAllowedProviderUrl: candidateUrl is required");for(const r of e)if(await Q(r.ciphertext,r.pkpId)===t)return r.providerId;return null}(o,r);if(!n)throw new Error(`Bitcoin provider URL not in any registered encrypted slot for chainId=${t.chainId}`);return{name:`Registry Provider ${n.slice(0,10)}`,url:r,minConfirmations:t.minBitcoinConfirmations,network:t.bitcoinNetwork}}var ee,te=[{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 function main(e){for(const[t,r]of Object.entries(e))globalThis[t]=r;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.positionId:",globalThis.positionId),console.log("[Admin Liquidation Validator] ========================================");let t,r,i,a,o,n="0";try{n="0a",console.log("[Step 0a] Validating configuration...");const e=globalThis.chain,d=globalThis.bitcoinProviderUrl,f=globalThis.positionId;if(!e)throw new Error('Missing required parameter: "chain"');if(!d)throw new Error('Missing required parameter: "bitcoinProviderUrl"');if(!f)throw new Error('Missing required parameter: "positionId"');n="0b";const w=function(e){const t=T(e),r=W[t];if(!r)throw new Error(`No chain policy registered for "${e}"`);return r}(e),b=w.chain;n="0c";const v=globalThis.contractAddresses;if(!v||"object"!=typeof v)throw new Error("Missing or invalid 'contractAddresses' parameter");if(r=v.PositionManager,i=v.LoanOperationsManagerModule,a=v.LiquidationManagerModule??v.LiquidationManager??"",o=v.BTCSpendAuthorizer||i,!r||!i||!a)throw new Error("Missing one or more required contract addresses (PositionManager, LoanOperationsManagerModule, LiquidationManager or LiquidationManagerModule)");let S;globalThis.rpcUrl&&"string"==typeof globalThis.rpcUrl?(!function(e,t){if(e.allowArbitraryEvmRpc)return;if(!t||"string"!=typeof t)throw new Error("validateEvmRpcForPolicy: rpcUrl must be a non-empty string");let r;try{r=new URL(t).hostname}catch(e){throw new Error(`Invalid rpcUrl: ${e?.message??String(e)}`)}if(!y.some((e=>r===e||r.endsWith(`.${e}`))))throw new Error(`Custom RPC domain "${r}" is not in the approved list: ${y.join(", ")}`)}(w,globalThis.rpcUrl),S=globalThis.rpcUrl):S=await Lit.Actions.getRpcUrl({chain:e}),t=new ethers.providers.JsonRpcProvider(S);const E=await Z({policy:w,bitcoinProviderUrl:d,ethersProvider:t,registryAddress:v.BitcoinProviderRegistry});console.log(` \u2705 Bitcoin Provider: ${E.name} (min confs ${E.minConfirmations})`);!function(e,t){const r=V[e],i=[];for(const e of r)t[e]&&i.push(e);if(i.length>0)throw new Error(JSON.stringify({code:"PROTOCOL_PAUSED",operation:e,paused:i,status:t}))}("adminLiquidation",await J(t,r));const A=new k(globalThis.priceProviders),I=new B({providerUrl:E.url}),$=(l={contractAddress:o,chain:e,chainId:w.chainId,rpcUrl:globalThis.rpcUrl,bitcoinProvider:I,minConfirmations:E.minConfirmations},new R(l)),C=function(e){return new F(e)}({contractAddress:r,termManagerAddress:v.TermManagerModule,loanOpsManagerAddress:i,liquidationManagerAddress:a,chain:e,chainId:P(b),rpcUrl:globalThis.rpcUrl,vaultBalance:$,priceOracle:A});n="1",console.log("[Step 1] Getting vault snapshot...");const M=await C.getVaultSnapshot(f);if(M.btcPriceUsd<s||M.btcPriceUsd>c)throw new Error(`Bitcoin price ${M.btcPriceUsd} is outside acceptable range (${s} - ${c}). This may indicate oracle manipulation or stale price data. Please try again later.`);console.log(` \u2705 BTC price validation passed: $${(Number(M.btcPriceUsd)/1e8).toFixed(2)}`),n="2",console.log("[Step 2] Validating position state...");const U=["ACTIVE","EXPIRED","LIQUIDATABLE"],O=p(M.status);if(!U.includes(O))throw new Error(`Invalid position status for liquidation: ${O}. Must be ACTIVE, EXPIRED, or LIQUIDATABLE.`);if(console.log(` \u2705 Position status valid for liquidation: ${p(M.status)}`),n="3",console.log("[Step 3] Checking liquidation eligibility..."),!M.isLiquidatable)throw new Error(`Position is not liquidatable (${D(M.collateralRatioBps)}% >= ${D(M.currentLiquidationThreshold)}%) - liquidation rejected.`);console.log(" \u2705 Position is liquidatable"),console.log(` - Current ratio: ${D(M.collateralRatioBps)}%`),console.log(` - Liquidation threshold: ${D(M.currentLiquidationThreshold)}%`),n="4",console.log("[Step 4] Building admin liquidation EIP-712 digest...");const x=q()+u,L=P(b),N=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(m)),_={name:h,version:g,chainId:L,verifyingContract:ethers.utils.getAddress(a)},X={AdminLiquidationAuth:[{name:"actionTag",type:"bytes32"},{name:"positionId",type:"bytes32"},{name:"quantumTimestamp",type:"uint256"},{name:"btcPrice",type:"uint256"},{name:"btcVaultBalance",type:"uint256"}]},z={actionTag:N,positionId:f,quantumTimestamp:x,btcPrice:M.btcPriceUsd.toString(),btcVaultBalance:M.availableBTCSats.toString()};globalThis.publicKey;n="5",console.log("[Step 5] Signing authorization...");const H=globalThis.validatorWalletAddress;if(!H||!H.startsWith("0x"))throw new Error("validatorWalletAddress required \u2014 must be a createWallet-derived address");const j=await Lit.Actions.getPrivateKey({pkpId:H}),G="string"==typeof j?j.startsWith("0x")?j:`0x${j}`:String(j),K=new ethers.Wallet(G),Y=await K._signTypedData(_,X,z);let Q=null;if(globalThis.callIsLiquidatable&&a){n="6",console.log("[Step 6] Calling isLiquidatable() on contract...");try{new ethers.Contract(a,te,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(e){console.warn(` \u26a0\ufe0f Failed to call isLiquidatable(): ${e.message}`)}}return console.log("[Admin Liquidation Validator] \u2705 Complete"),{approved:!0,positionId:f,btcPrice:M.btcPriceUsd.toString(),btcAmountSats:M.availableBTCSats.toString(),signature:Y,timestamp:x,validatorPkp:K.address,isLiquidatable:M.isLiquidatable,isLiquidatableOnChain:Q}}catch(e){return console.error("[Admin Liquidation Validator] \u274c Failed:",e.message),console.error(`[Admin Liquidation Validator] Failed at step: ${n}`),{approved:!1,reason:e.message||e.toString(),positionId:globalThis.positionId,timestamp:Date.now()}}var l}return ee=a,((a,o,n,s)=>{if(o&&"object"==typeof o||"function"==typeof o)for(let c of r(o))i.call(a,c)||c===n||e(a,c,{get:()=>o[c],enumerable:!(s=t(o,c))||s.enumerable});return a})(e({},"__esModule",{value:!0}),ee)})();if("object"==typeof _LIT_ACTION_)if("function"==typeof _LIT_ACTION_.main)var main=_LIT_ACTION_.main;else"function"==typeof _LIT_ACTION_.go&&_LIT_ACTION_.go();
|
|
1
|
+
var _LIT_ACTION_=(()=>{var e=Object.defineProperty,t=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a={};((t,r)=>{for(var i in r)e(t,i,{get:r[i],enumerable:!0})})(a,{main:()=>main});var o=11e3,n=1000000000000000000n,s=1000000000000n,c=100000000000000n,l=60,u=10,d=8e3;function p(e){switch(e){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(${e})`}}var h="DiamondHands.Protocol",g="1",m="admin-liquidation",f=(e=>(e.SEPOLIA="sepolia",e.ETHEREUM="ethereum",e.HARDHAT="hardhat",e))(f||{}),w={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},y=["infura.io","drpc.org"],b={ethereum:1,sepolia:11155111,hardhat:1337};function T(e){const t=e.toLowerCase().trim();if("sepolia"===t)return"sepolia";if("ethereum"===t||"mainnet"===t)return"ethereum";if("hardhat"===t)return"hardhat";throw new Error(`Unsupported EVM chain: "${e}". Supported chains: ${Object.values(f).join(", ")}`)}function P(e){return b[e]}function v(e){if(!0===e)return!0;if(!1===e)return!1;const t=globalThis;return!0===t.debugAction||(!0===t.LIT_ACTION_DEBUG||("true"===t.LIT_ACTION_DEBUG||function(){try{if("undefined"==typeof process||!process.env)return!1;const e=process.env.LIT_ACTION_DEBUG,t=process.env.DEBUG;return"1"===e||"true"===e||"1"===t||"true"===t}catch{return!1}}()))}function S(...e){v()&&console.log(...e)}var E=class{stepStart(e,t){}stepEnd(e,t){}log(e,t,...r){}warn(e,t){}error(e,t){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},A=class{constructor(e){this.stepStartTimes=new Map,this.stepDurations=new Map,this.TIMEOUT_MS=3e4,this.actionName=e,this.executionStartTime=Date.now();const t=Date.now(),r=Math.random().toString(36).substring(2,9);this.executionId=`exec_${t}_${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(e,t){this.stepStartTimes.set(e,Date.now());const r=Date.now()-this.executionStartTime,i=this.TIMEOUT_MS-r;console.log(`[Step ${e}] ${t}...`),console.log(`[Step ${e}] Elapsed: ${r}ms | Remaining: ${i}ms`)}stepEnd(e,t=5e3){const r=this.stepStartTimes.get(e);if(!r)return console.warn(`[Step ${e}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const i=Date.now()-r;this.stepDurations.set(e,i),console.log(`[Step ${e}] Duration: ${i}ms`),i>t&&console.warn(`\u26a0\ufe0f [Step ${e}] Took ${i}ms (> ${t}ms threshold)`);const a=Date.now()-this.executionStartTime,o=this.TIMEOUT_MS-a;o<5e3&&console.warn(`\u26a0\ufe0f [Step ${e}] Time remaining: ${o}ms (approaching timeout)`)}log(e,t,...r){r.length>0?console.log(`[Step ${e}] ${t}`,...r):console.log(`[Step ${e}] ${t}`)}warn(e,t){console.warn(`\u26a0\ufe0f [Step ${e}] ${t}`)}error(e,t){console.error(`\u274c [Step ${e}] ${t}`)}getElapsed(){return Date.now()-this.executionStartTime}getRemaining(){return this.TIMEOUT_MS-this.getElapsed()}summary(){const e=Date.now()-this.executionStartTime,t=this.TIMEOUT_MS-e;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: ${(e/1e3).toFixed(2)}s`),console.log(`[${this.actionName}] Time remaining: ${t}ms`),this.stepDurations.size>0){console.log(`[${this.actionName}] Step breakdown:`);for(const[t,r]of this.stepDurations.entries()){const i=(r/e*100).toFixed(1);console.log(`[${this.actionName}] Step ${t}: ${r}ms (${i}%)`)}}e>2e4&&console.warn(`\u26a0\ufe0f [${this.actionName}] Slow execution: ${(e/1e3).toFixed(2)}s (${t}ms remaining)`),t<5e3&&console.warn(`\u26a0\ufe0f [${this.actionName}] CRITICAL: Only ${t}ms remaining before timeout!`),console.log(`[${this.actionName}] ========================================`)}getExecutionId(){return this.executionId}isDebugEnabled(){return!0}},I=class{static create(e="LIT Action",t){return v(t)?new A(e):new E}};function $(e){if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}}function B(e){return e instanceof Error&&"AbortError"===e.name}var C=class{constructor(e){this.config=e,this.timeout=e.timeout||15e3,this.logger=I.create("BitcoinDataProvider")}async getCurrentBlockHeight(){if(this.logger.stepStart("0","getCurrentBlockHeight"),this.config.rpcHelper){const e=await this.config.rpcHelper.getBlockCount();return this.logger.stepEnd("0"),e}const e=new AbortController,t=setTimeout((()=>e.abort()),this.timeout);try{const r=Date.now(),i=await fetch(`${this.config.providerUrl}/blocks/tip/height`,{signal:e.signal,headers:{Accept:"text/plain","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(t),S(`[BitcoinDataProvider] blocks/tip/height ${Date.now()-r}ms (timeout budget ${this.timeout}ms)`),!i.ok)throw new Error(`Failed to fetch block height: ${i.status} ${i.statusText}`);const a=await i.text();if(a.trim().startsWith("<!DOCTYPE")||a.trim().startsWith("<html"))throw new Error("Bitcoin RPC endpoint returned HTML instead of block height. URL may be pointing to web interface instead of API endpoint. Expected: plain text number, Got: HTML document. Check that URL ends with /api/esplora (not root /)");const o=parseInt(a.trim(),10);if(isNaN(o)){const e=a.length>200?a.substring(0,200)+"... (truncated)":a;throw new Error(`Invalid block height response: ${e}`)}return this.logger.stepEnd("0"),o}catch(e){if(clearTimeout(t),this.logger.stepEnd("0"),B(e))throw new Error(`Request timeout after ${this.timeout}ms`);throw e}}async getUTXOs(e){this.logger.log("0",`Original address: "${e}"`);const t=this.stripNetworkPrefix(e);if(this.logger.log("0",`Cleaned address: "${t}"`),this.config.rpcHelper)return await this.fetchUTXOsFromRPC(t);try{return await this.fetchUTXOsFromProvider(this.config.providerUrl,t)}catch(e){if(this.logger.warn("0",`Primary provider failed: ${$(e)}`),this.config.fallbackProviders&&this.config.fallbackProviders.length>0){const e=[...this.config.fallbackProviders].sort(((e,t)=>e.priority-t.priority));for(const r of e)try{return this.logger.log("0",`Trying fallback: ${r.name} (${r.url})`),await this.fetchUTXOsFromProvider(r.url,t)}catch(e){this.logger.warn("0",`Fallback ${r.name} failed: ${$(e)}`);continue}}throw new Error(`Failed to fetch UTXOs: ${$(e)}`)}}stripNetworkPrefix(e){return e.replace(/^(REGTEST_|TESTNET_|MAINNET_)/,"")}async fetchUTXOsFromRPC(e){if(!this.config.rpcHelper)throw new Error("RPC helper not configured");const t=this.config.rpcWallet||"";return(await this.config.rpcHelper.listUnspent(t,e)).map((e=>({txid:e.txid,vout:e.vout,satoshis:BigInt(Math.round(1e8*e.amount)),confirmations:e.confirmations})))}parseBlockstreamUTXOs(e,t){if(!Array.isArray(e))throw new Error("Invalid Blockstream response format: expected an array");const r=e,i=[];for(const e of r){let r=0;e.status&&"object"==typeof e.status?e.status.confirmed&&(r=void 0!==e.status.block_height&&e.status.block_height>0?t-e.status.block_height+1:1):void 0!==e.confirmations&&(r=e.confirmations);const a=e.txid,o=void 0!==e.vout?e.vout:e.n,n=void 0!==e.value?e.value:e.satoshis;if(void 0===a||void 0===o||void 0===n)throw new Error("Invalid UTXO row: missing txid, vout/n, or value/satoshis");i.push({txid:a,vout:o,satoshis:BigInt(n),confirmations:r})}return i}async fetchUTXOsFromProvider(e,t){this.logger.stepStart("1","fetchUTXOsFromProvider");const r=`${e}/address/${t}/utxos`;this.logger.log("1",`Fetching UTXOs from ${r}`);const i=new AbortController,a=setTimeout((()=>i.abort()),this.timeout);try{const e=Date.now(),[o,n]=await Promise.all([this.getCurrentBlockHeight(),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),S(`[BitcoinDataProvider] parallel(tipHeight + GET utxos) wall ${Date.now()-e}ms endpoint tail .../${t.slice(0,12)}.../utxos (timeout budget ${this.timeout}ms)`),this.logger.log("1",`Current block height: ${o}`),!n.ok)throw new Error(`Bitcoin provider error: ${n.status} ${n.statusText}`);const s=await n.text();if(s.trim().startsWith("<!DOCTYPE")||s.trim().startsWith("<html"))throw new Error("Bitcoin RPC endpoint returned HTML instead of JSON. URL may be pointing to web interface instead of API endpoint. Expected: JSON array of UTXOs, Got: HTML document. Check that URL ends with /api/esplora (not root /)");let c;try{c=JSON.parse(s)}catch(e){const t=s.length>200?s.substring(0,200)+"... (truncated)":s;throw new Error(`Failed to parse JSON response: ${$(e)}. Response: ${t}`)}this.logger.log("1",`Raw API Response: ${JSON.stringify(c,null,2)}`);const l=this.parseBlockstreamUTXOs(c,o);return this.logger.stepEnd("1"),l}catch(e){if(clearTimeout(a),this.logger.stepEnd("1"),B(e))throw new Error(`Request timeout after ${this.timeout}ms`);throw e}}async getUTXOSet(e,t){let r=[],i=null;for(let t=1;t<=3;t++)try{this.logger.log("2",`UTXO query attempt ${t}/3...`),r=await this.getUTXOs(e),this.logger.log("2",`UTXO query succeeded on attempt ${t}`),i=null;break}catch(e){i=e instanceof Error?e:new Error($(e)),this.logger.error("2",`UTXO query failed on attempt ${t}: ${$(e)}`),t<3&&(this.logger.log("2","Waiting 500ms before retry..."),await new Promise((e=>setTimeout(e,500))))}if(i)throw new Error(`Failed to query UTXOs after 3 attempts: ${i.message}`);const a=r.reduce(((e,t)=>e+t.satoshis),0n),o=r.filter((e=>e.confirmations>=t)).reduce(((e,t)=>e+t.satoshis),0n),n=a-o;return{utxos:r,totalBalance:a,totalUTXOs:r.length,confirmedBalance:o,unconfirmedBalance:n}}async getBalance(e){const t=this.stripNetworkPrefix(e),r=`${this.config.providerUrl}/address/${t}`;this.logger.log("3",`Fetching balance from ${r}`);const i=new AbortController,a=setTimeout((()=>i.abort()),this.timeout);try{const e=Date.now(),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),S(`[BitcoinDataProvider] GET address chain_stats ${Date.now()-e}ms (timeout budget ${this.timeout}ms)`),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const o=await t.json();if(!o.chain_stats)throw new Error("Invalid response: missing chain_stats");const n=o.chain_stats.funded_txo_sum,s=o.chain_stats.spent_txo_sum;if(void 0===n||void 0===s)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${n}, spent_txo_sum: ${s}`);const c=BigInt(n-s);return this.logger.log("3",`Balance: ${c.toString()} sats`),this.logger.log("3",` - Funded: ${n} sats, Spent: ${s} sats`),c}catch(e){if(clearTimeout(a),B(e))throw new Error(`Request timeout after ${this.timeout}ms`);const r=$(e);throw this.logger.log("3",`Balance fetch failed: ${r}`),new Error(`Failed to fetch Bitcoin balance for address ${t}: ${r}`)}}async getTransaction(e){if(this.config.rpcHelper)try{const t=this.config.rpcWallet||"",r=await this.config.rpcHelper.getTransaction(t,e);return{txid:r.txid,confirmations:r.confirmations||0}}catch(e){const t=$(e),r=null!==e&&"object"==typeof e&&"code"in e&&"number"==typeof e.code?e.code:void 0;if(t.includes("Invalid or non-wallet transaction")||-5===r)return null;throw e}const t=new AbortController,r=setTimeout((()=>t.abort()),this.timeout);try{const i=Date.now(),a=await fetch(`${this.config.providerUrl}/tx/${e}`,{signal:t.signal});if(clearTimeout(r),S(`[BitcoinDataProvider] GET tx/${e.slice(0,10)}\u2026 ${Date.now()-i}ms (timeout budget ${this.timeout}ms)`),!a.ok){if(404===a.status)return null;throw new Error(`Bitcoin provider error: ${a.status} ${a.statusText}`)}const o=await a.json();return o.status?{txid:o.txid??e,confirmations:o.status.confirmed&&o.status.block_height?1:0}:null}catch(e){if(clearTimeout(r),B(e))throw new Error(`Request timeout after ${this.timeout}ms`);if($(e).includes("404"))return null;throw e}}async getTransactionOutputs(e){if(!this.config.providerUrl)return null;const t=new AbortController,r=setTimeout((()=>t.abort()),this.timeout);try{const i=await fetch(`${this.config.providerUrl}/tx/${e}`,{signal:t.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.vout&&Array.isArray(a.vout)?a.vout.map(((e,t)=>({index:t,scriptpubkey:e.scriptpubkey,scriptpubkey_address:e.scriptpubkey_address,value:e.value??0}))):null}catch(e){if(clearTimeout(r),B(e))throw new Error(`Request timeout after ${this.timeout}ms`);throw e}}};function M(e,t){if(0===e)return{termDurationDays:0,termLengthDays:30*t,isExpired:!1,daysUntilExpiry:30*t,daysIntoGracePeriod:0};const r=30*t,i=Math.floor(Date.now()/1e3)-e,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 U(e,t,r,i){if(!e)return r??13e3;const a=i??o,n=Math.min(t,30);return o+n*n*(a-o)/900}function D(e,t,r){const i=e*t/10000000000000000n;if(0n===r)return{collateralValueUsd:i,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:i,collateralRatioBps:Number(i*n*10000n/r)}}function O(e){return e/100}var x=class e{constructor(t,r,i){if(r&&Array.isArray(r))this.sources=r;else{if(!Array.isArray(t))throw new Error("Price oracle requires a priceProviders array \u2014 defaults and fallbacks have been removed. Pass 3 distinct {name, apiKey} entries from the supported set: "+e.ACTIVE_PROVIDER_NAMES.join(", "));this.sources=this.buildSources(t)}this.sources.sort(((e,t)=>e.priority-t.priority)),this.validateProviderCount()}validateProviderCount(){if(this.sources.length<e.MIN_DISTINCT_PROVIDERS)throw new Error(`Price oracle requires at least ${e.MIN_DISTINCT_PROVIDERS} eligible providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported active providers: ${e.ACTIVE_PROVIDER_NAMES.join(", ")}`)}buildSources(t){if(0===t.length)throw new Error("priceProviders must be a non-empty array of 3 distinct providers, each with an apiKey.");const r=[],i=new Set;let a=1;for(const o of t){if(!o||"string"!=typeof o.name)throw new Error("priceProviders entries must be {name, apiKey, apiSecret?} objects");const t=o.name.toLowerCase();if(!e.ACTIVE_PROVIDER_NAMES.includes(t))throw new Error(`Unsupported price provider "${o.name}". Supported: ${e.ACTIVE_PROVIDER_NAMES.join(", ")}`);if(i.has(t))throw new Error(`Duplicate price provider "${t}" \u2014 the oracle requires 3 DISTINCT providers so a single upstream failure or manipulation cannot compromise consensus. Use 3 different provider names from: ${e.ACTIVE_PROVIDER_NAMES.join(", ")}`);switch(i.add(t),t){case"binance":if(!o.apiKey)throw new Error("Binance provider requires an apiKey");r.push(this.createBinanceSource(o.apiKey,a++));break;case"coinbase":if(!o.apiKey)throw new Error("Coinbase provider requires an apiKey");r.push(this.createCoinbaseSource(o.apiKey,a++));break;case"cryptocompare":if(!o.apiKey)throw new Error("CryptoCompare provider requires an apiKey");r.push(this.createCryptoCompareSource(o.apiKey,a++));break;case"coinmarketcap":if(!o.apiKey)throw new Error("CoinMarketCap provider requires an apiKey");r.push(this.createCoinMarketCapSource(o.apiKey,a++));break;case"coingecko":if(!o.apiKey)throw new Error("CoinGecko provider requires an apiKey (Demo or Pro). Unauthenticated CoinGecko access is not accepted \u2014 the free tier is rate-limited and edge-cached, which breaks median consensus under load.");r.push(this.createCoinGeckoSource(o.apiKey,a++));break;default:throw new Error(`Unhandled provider: ${t}`)}}if(r.length<e.MIN_DISTINCT_PROVIDERS)throw new Error(`Insufficient price providers: need at least ${e.MIN_DISTINCT_PROVIDERS} distinct entries, got ${r.length}.`);return this.selectSampledSources(r)}createCryptoCompareSource(e,t){return{name:"CryptoCompare",fetchPrice:async()=>{const t=new URL("https://min-api.cryptocompare.com/data/price");t.searchParams.set("fsym","BTC"),t.searchParams.set("tsyms","USDT"),t.searchParams.set("api_key",e);const r=await(async e=>{const t=new AbortController,r=setTimeout((()=>t.abort()),5e3);try{const i=await fetch(e,{signal:t.signal,headers:{Accept:"application/json"}});if(clearTimeout(r),!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}catch(e){if(clearTimeout(r),"AbortError"===e.name)throw new Error("Request timeout after 5000ms");throw e}})(t.toString()),i=Number(r?.USDT);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CryptoCompare price payload");return i},priority:t}}createFetchJson(){return async(e,t)=>{const r=new AbortController,i=setTimeout((()=>r.abort()),5e3);try{const i=await fetch(e,{headers:{Accept:"application/json",...t||{}},signal:r.signal});if(!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}finally{clearTimeout(i)}}}getSelectionSeed(){const e="undefined"!=typeof process?process.env.PRICE_PROVIDER_SELECTION_SEED:void 0,t=e?Number(e):NaN;return Number.isFinite(t)?t:("undefined"!=typeof process,0,Math.floor(Date.now()+1e6*Math.random()))}selectSampledSources(e){const t=[...e];let r=this.getSelectionSeed()>>>0;for(let e=t.length-1;e>0;e--){const i=Math.floor((r=1664525*r+1013904223>>>0,r/4294967296*(e+1)));[t[e],t[i]]=[t[i],t[e]]}return t.slice(0,3).map(((e,t)=>({...e,priority:t+1})))}createCoinGeckoSource(e,t){if(!e)throw new Error("createCoinGeckoSource requires an apiKey");const r=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const t=e.startsWith("pro:"),i=t?e.slice(4):e,a=t?{"x-cg-pro-api-key":i}:{"x-cg-demo-api-key":i},o=new URL("https://api.coingecko.com/api/v3/simple/price");o.searchParams.set("ids","bitcoin"),o.searchParams.set("vs_currencies","usd");const n=await r(o.toString(),a),s=Number(n?.bitcoin?.usd);if(!Number.isFinite(s)||s<=0)throw new Error("Invalid CoinGecko price payload");return s},priority:t}}createBinanceSource(e,t){if(!e)throw new Error("createBinanceSource requires an apiKey");const r=this.createFetchJson();return{name:"Binance",fetchPrice:async()=>{const t=new URL("https://api.binance.com/api/v3/ticker/price");t.searchParams.set("symbol","BTCUSDT");const i=await r(t.toString(),{"X-MBX-APIKEY":e}),a=Number(i?.price);if(!Number.isFinite(a)||a<=0)throw new Error("Invalid Binance price payload");return a},priority:t}}createCoinbaseSource(e,t){if(!e)throw new Error("createCoinbaseSource requires an apiKey");const r=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const t=await r("https://api.coinbase.com/v2/prices/BTC-USDT/spot",{"CB-ACCESS-KEY":e}),i=Number(t?.data?.amount);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid Coinbase price payload");return i},priority:t}}createCoinMarketCapSource(e,t){const r=this.createFetchJson();return{name:"CoinMarketCap",fetchPrice:async()=>{const t=new URL("https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest");t.searchParams.set("symbol","BTC"),t.searchParams.set("convert","USDT");const i=await r(t.toString(),{"X-CMC_PRO_API_KEY":e}),a=Number(i?.data?.BTC?.[0]?.quote?.USDT?.price??i?.data?.BTC?.quote?.USDT?.price);if(!Number.isFinite(a)||a<=0)throw new Error("Invalid CoinMarketCap price payload");return a},priority:t}}async getBTCPrice(){const e=Date.now();if(3!==this.sources.length)throw new Error(`Price oracle requires exactly 3 providers. Currently configured: ${this.sources.length}`);const t=new Promise(((e,t)=>{setTimeout((()=>{t(new Error("Price oracle global timeout after 8000ms"))}),8e3)})),r=Promise.all(this.sources.map((async e=>{const t=Date.now(),r=await e.fetchPrice();return console.log(`[Price Oracle] \u2705 [+${Date.now()-t}ms] ${e.name}: $${r.toLocaleString()}`),r}))),i=[...await Promise.race([r,t])].sort(((e,t)=>e-t)),a=i[1];if((i[2]-i[0])/i[0]>.01){const e=this.sources.map((e=>e.name)).join(", "),t=i.map((e=>`$${e.toLocaleString()}`)).join(", ");throw new Error(`Price consensus failed: prices not within 1% tolerance. Providers: [${e}]. Prices: [${t}]`)}const o=Math.round(100*a),n=1000000n*BigInt(o),s=Date.now()-e;return console.log(`[Price Oracle] Median price: $${a.toLocaleString()} \u2014 total time: ${s}ms`),console.log(`[Price Oracle] Price with 8 decimals: ${n}`),n}async getBTCPriceConsensus(){console.log("[Price Oracle] Fetching BTC price with consensus...");const e=(await Promise.allSettled(this.sources.map((async e=>{const t=await e.fetchPrice();return{source:e.name,price:t}})))).filter((e=>"fulfilled"===e.status)).map((e=>e.value));if(0===e.length)throw new Error("No price sources returned data");console.log(`[Price Oracle] Got prices from ${e.length}/${this.sources.length} sources:`),e.forEach((e=>{console.log(` ${e.source}: $${e.price.toLocaleString()}`)}));const t=e.map((e=>e.price));t.sort(((e,t)=>e-t));const r=t[Math.floor(t.length/2)],i=t[0],a=t[t.length-1]/i,o=e.filter((e=>Math.abs(e.price-r)/r<=.02));if(a>1.05&&o.length===e.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(a-1)).toFixed(1)}% spread)`);if(o.length<e.length){const t=e.filter((e=>!o.find((t=>t.source===e.source))));if(console.log(`[Price Oracle] \u26a0\ufe0f Detected ${t.length} outlier(s):`),t.forEach((e=>{const t=Math.abs(e.price-r)/r;console.log(` ${e.source}: $${e.price.toLocaleString()} (${(100*t).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((e=>e.price));i.sort(((e,t)=>e-t));const a=i[Math.floor(i.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${a.toLocaleString()}`);const n=Math.round(100*a);return 1000000n*BigInt(n)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${r.toLocaleString()}`);const n=Math.round(100*r);return 1000000n*BigInt(n)}};x.ACTIVE_PROVIDER_NAMES=["cryptocompare","coinbase","binance","coinmarketcap","coingecko"],x.MIN_DISTINCT_PROVIDERS=3;var k=x;function q(){const e=Math.floor(Date.now()/1e3);return t=e,Math.floor(t/l)*l;var t}var R=class{constructor(e){this.config=e,this.bitcoinProvider=e.bitcoinProvider}async calculateBalance(e,t){const r=await this.bitcoinProvider.getBalance(t),i=await this.getAuthorizedSpendsFromContract(e),a=i.reduce(((e,t)=>e+t.satoshis),0n),o=r>a?r-a:0n;return{totalUTXOs:[],totalBalance:r,authorizedUTXOs:i,authorizedBalance:a,authorizedSpendsHash:this.computeAuthorizedSpendsHash(e,i),availableUTXOs:[],availableBalance:o,vaultAddress:t,positionId:e,timestamp:Date.now()}}async calculateTrustedBalance(e,t,r){if(r)return await this.calculateBalance(e,t);const i=this.config.minConfirmations||6,a=await this.bitcoinProvider.getUTXOSet(t,i),o=await this.getAuthorizedSpendsFromContract(e);for(const e of o){if(a.utxos.some((t=>t.txid===e.txid&&t.vout===e.vout)))throw new Error(`Authorized UTXO ${e.txid}:${e.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=o.reduce(((e,t)=>e+t.satoshis),0n),s=a.utxos.filter((e=>!this.isUTXOAuthorized(e,o))),c=s.reduce(((e,t)=>e+t.satoshis),0n),l=this.computeAuthorizedSpendsHash(e,o);return{totalUTXOs:a.utxos,totalBalance:a.totalBalance,authorizedUTXOs:o,authorizedBalance:n,authorizedSpendsHash:l,availableUTXOs:s,availableBalance:c,vaultAddress:t,positionId:e,timestamp:Date.now()}}async getTrustedBalance(e,t){return(await this.calculateTrustedBalance(e,t)).availableBalance}async getAvailableUTXOs(e,t){return(await this.calculateTrustedBalance(e,t)).availableUTXOs}async isUTXOAvailable(e,t,r,i){return(await this.getAvailableUTXOs(e,t)).some((e=>e.txid===r&&e.vout===i))}isUTXOAuthorized(e,t){return t.some((t=>t.txid===e.txid&&t.vout===e.vout))}async getAuthorizedSpendsFromContract(e){const t=e.startsWith("0x")?e:`0x${e.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({url:r,timeout:d},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(t)).map((t=>({txid:t.txid,vout:Number(t.vout),satoshis:BigInt(t.satoshis.toString()),positionId:e,targetAddress:t.targetAddress,targetAmount:BigInt(t.targetAmount.toString()),timestamp:Number(t.authorizedAt)})))}computeAuthorizedSpendsHash(e,t){const r=e.startsWith("0x")?e:`0x${e.padStart(64,"0")}`;if(0===t.length)return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,[]]));const i=t.map((e=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.txid)),r=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.targetAddress));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","uint32","uint256","bytes32","uint256","uint256"],[t,e.vout,e.satoshis.toString(),r,e.targetAmount.toString(),e.timestamp]))}));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,i]))}};var L=class{constructor(e){this.termManagerAddress=e.termManagerAddress,this.loanOpsManagerAddress=e.loanOpsManagerAddress,this.liquidationManagerAddress=e.liquidationManagerAddress,this.chain=e.chain,this.chainId=e.chainId,this.rpcUrl=e.rpcUrl}async getLiquidationThreshold(){try{let e;e=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const t=new ethers.providers.StaticJsonRpcProvider({url:e,timeout:d},{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,t),a=await i.liquidationThreshold();return Number(a.toString())}catch(e){throw console.error("[ProtocolParameters] Error fetching liquidation threshold:",e.message),new Error(`Failed to fetch liquidation threshold: ${e.message}`)}}async getTermFees(e){try{let t;t=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const r=new ethers.providers.StaticJsonRpcProvider({url:t,timeout:d},{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(e);return{originationFeeBps:Number(o.originationFee?.toString?.()??o[0]?.toString?.()??o[0]),extensionFeeBps:Number(o.extensionFee?.toString?.()??o[1]?.toString?.()??o[1])}}catch(e){throw console.error("[ProtocolParameters] Error fetching term fees:",e.message),new Error(`Failed to fetch term fees: ${e.message}`)}}async getAuthorizedSpendsHash(e){try{const t=this.rpcUrl||await Lit.Actions.getRpcUrl({chain:this.chain}),r=new ethers.providers.StaticJsonRpcProvider({url:t,timeout:d},{name:"any",chainId:this.chainId}),i=e.startsWith("0x")?e:`0x${e.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(e){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",e.message),new Error(`Failed to fetch authorized spends hash: ${e.message}`)}}async getMaxEscalatedThreshold(){try{let e;e=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const t=new ethers.providers.StaticJsonRpcProvider({url:e,timeout:d},{name:"any",chainId:this.chainId}),r=[{inputs:[],name:"maxEscalatedThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],i=new ethers.Contract(this.liquidationManagerAddress,r,t),a=await i.maxEscalatedThreshold();return Number(a.toString())}catch(e){throw console.error("[ProtocolParameters] Error fetching max escalated threshold:",e.message),new Error(`Failed to fetch max escalated threshold: ${e.message}`)}}};function N(e){return new L(e)}var F=class{constructor(e){this.config=e}async getVaultSnapshot(e){const t=await this.queryPositionState(e);console.log(`[Vault Snapshot] Raw vault address from contract: "${t.vaultAddress}"`);const r=!0===this.config.debugOverrides?.useStubbedBtcData,[i,a]=r?this.buildStubbedBtcSnapshotInputs(t):await Promise.all([this.config.vaultBalance.calculateTrustedBalance(e,t.vaultAddress),this.config.priceOracle.getBTCPriceConsensus()]),o=N({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,liquidationManagerAddress:this.config.liquidationManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),n=this.config.termOverride??t.selectedTerm,[s,c,l]=await Promise.all([o.getLiquidationThreshold(),o.getMaxEscalatedThreshold(),o.getTermFees(n)]),u=M(t.termStartTimestamp,t.selectedTerm),d=U(u.isExpired,u.daysIntoGracePeriod,s,c),p=D(i.availableBalance,a,t.ucdDebt),h=p.collateralRatioBps<d,g=p.collateralRatioBps-d;return{positionId:t.positionId,pkpId:t.pkpId,borrower:t.borrower,vaultAddress:t.vaultAddress,ucdDebt:t.ucdDebt,termStartTimestamp:t.termStartTimestamp,selectedTerm:t.selectedTerm,status:t.status,expiryAt:t.expiryAt,previousExpiryAt:t.previousExpiryAt,totalTerm:t.totalTerm,totalBTCSats:i.totalBalance,totalUTXOs:i.totalUTXOs,authorizedSpendsSats:i.authorizedBalance,authorizedSpendsHash:i.authorizedSpendsHash,availableBTCSats:i.availableBalance,availableUTXOs:i.availableUTXOs,btcPriceUsd:a,collateralValueUsd:p.collateralValueUsd,collateralRatioBps:p.collateralRatioBps,termDurationDays:u.termDurationDays,termLengthDays:u.termLengthDays,isExpired:u.isExpired,daysUntilExpiry:u.daysUntilExpiry,daysIntoGracePeriod:u.daysIntoGracePeriod,currentLiquidationThreshold:d,isLiquidatable:h,marginToLiquidationBps:g,liquidationThresholdBps:s,originationFeeBps:l.originationFeeBps,extensionFeeBps:l.extensionFeeBps,timestamp:Date.now()}}async getVaultSnapshotFast(e){const t=await this.queryPositionState(e);console.log(`[Vault Snapshot] Raw vault address from contract: "${t.vaultAddress}"`);const r=!0===this.config.debugOverrides?.useStubbedBtcData,[i,a]=r?this.buildStubbedBtcSnapshotInputs(t):await Promise.all([this.config.vaultBalance.calculateTrustedBalance(e,t.vaultAddress,!0),this.config.priceOracle.getBTCPriceConsensus()]),o=N({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,liquidationManagerAddress:this.config.liquidationManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),n=this.config.termOverride??t.selectedTerm,[s,c,l]=await Promise.all([o.getLiquidationThreshold(),o.getMaxEscalatedThreshold(),o.getTermFees(n)]),u=M(t.termStartTimestamp,t.selectedTerm),d=U(u.isExpired,u.daysIntoGracePeriod,s,c),p=D(i.availableBalance,a,t.ucdDebt),h=p.collateralRatioBps<d,g=p.collateralRatioBps-d;return{positionId:t.positionId,pkpId:t.pkpId,borrower:t.borrower,vaultAddress:t.vaultAddress,ucdDebt:t.ucdDebt,termStartTimestamp:t.termStartTimestamp,selectedTerm:t.selectedTerm,status:t.status,expiryAt:t.expiryAt,previousExpiryAt:t.previousExpiryAt,totalTerm:t.totalTerm,totalBTCSats:i.totalBalance,totalUTXOs:i.totalUTXOs,authorizedSpendsSats:i.authorizedBalance,authorizedSpendsHash:i.authorizedSpendsHash,availableBTCSats:i.availableBalance,availableUTXOs:i.availableUTXOs,btcPriceUsd:a,collateralValueUsd:p.collateralValueUsd,collateralRatioBps:p.collateralRatioBps,termDurationDays:u.termDurationDays,termLengthDays:u.termLengthDays,isExpired:u.isExpired,daysUntilExpiry:u.daysUntilExpiry,daysIntoGracePeriod:u.daysIntoGracePeriod,currentLiquidationThreshold:d,isLiquidatable:h,marginToLiquidationBps:g,liquidationThresholdBps:s,originationFeeBps:l.originationFeeBps,extensionFeeBps:l.extensionFeeBps,timestamp:Date.now()}}buildStubbedBtcSnapshotInputs(e){const t=BigInt(this.config.debugOverrides?.stubbedAvailableBtcSats??"10000000"),r=BigInt(this.config.debugOverrides?.stubbedBtcPriceUsd??"5000000000000");return[{totalUTXOs:[],totalBalance:t,authorizedUTXOs:[],authorizedBalance:0n,authorizedSpendsHash:this.config.debugOverrides?.stubbedAuthorizedSpendsHash??ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[e.positionId,[]])),availableUTXOs:[],availableBalance:t,vaultAddress:e.vaultAddress,positionId:e.positionId,timestamp:Date.now()},r]}async queryPositionState(e){const t=e.startsWith("0x")?e:`0x${e.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({url:r,timeout:d},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:"uint40",name:"previousExpiryAt",type:"uint40"},{internalType:"uint16",name:"totalTerm",type:"uint16"}],internalType:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],i),s=await n.getPositionDetails(t),c=Number(s.status);if(c<0||c>7)throw console.error("[VaultSnapshot] INVALID STATUS - ABI decoding error detected"),console.error(" Position ID:",t),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: ${t}`);const l=Number(s.expiryAt),u=Number(s.selectedTerm),p=Number(s.totalTerm),h=Number(s.previousExpiryAt),g=(f=u,0===(m=l)?0:m-30*f*86400);var m,f;return{positionId:s.positionId,pkpId:s.pkpId,borrower:s.borrower,vaultAddress:this.parseVaultAddress(s.vaultAddress,T(this.config.chain)),ucdDebt:BigInt(s.ucdDebt.toString()),termStartTimestamp:g,selectedTerm:u,status:c,expiryAt:l,previousExpiryAt:h,totalTerm:p}}parseVaultAddress(e,t){try{const r=JSON.parse(e);if("object"==typeof r&&null!==r){if("sepolia"===t.toLowerCase())return r.regtest||r.testnet||r.mainnet;const e=function(e){return w[e]}(t);return"testnet"===e&&(r.testnet||r.regtest)||r.mainnet}}catch(e){}return e}async isLiquidatable(e){return(await this.getVaultSnapshot(e)).isLiquidatable}async getCollateralRatio(e){return(await this.getVaultSnapshot(e)).collateralRatioBps}async getAvailableBalance(e){return(await this.getVaultSnapshot(e)).availableBTCSats}async hasSufficientCollateral(e,t,r){const i=await this.getVaultSnapshot(e);return function(e,t,r,i){const a=t+r;return 0n===a||Number(e*n*10000n/a)>=i}(i.collateralValueUsd,i.ucdDebt,t,r)}};var _="function getProtocolPauseStatus() view returns (tuple(bool positionManagerPaused, bool positionManagerLiquidationsPaused, bool positionManagerCorePaused, bool loanOperationsPaused, bool collateralManagerPaused, bool liquidationManagerPaused, bool circuitBreakerPaused, bool ucdControllerPaused, bool ucdTokenPaused, bool simplePsmPaused))",V={mintUcd:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],withdrawBtc:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],processPayment:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused"],extendPosition:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],liquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],adminLiquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],btcTransactionSign:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused"]};function X(e){return{positionManagerPaused:e.positionManagerPaused,positionManagerLiquidationsPaused:e.positionManagerLiquidationsPaused,positionManagerCorePaused:e.positionManagerCorePaused,loanOperationsPaused:e.loanOperationsPaused,collateralManagerPaused:e.collateralManagerPaused,liquidationManagerPaused:e.liquidationManagerPaused,circuitBreakerPaused:e.circuitBreakerPaused,ucdControllerPaused:e.ucdControllerPaused,ucdTokenPaused:e.ucdTokenPaused,simplePsmPaused:e.simplePsmPaused}}var z=new ethers.utils.Interface(["function views() view returns (address)","function paused() view returns (bool)","function liquidationPaused() view returns (bool)","function simplePsm() view returns (address)"]),H=new ethers.utils.Interface(["function core() view returns (address)","function loanOps() view returns (address)","function collateralManager() view returns (address)","function liquidationManager() view returns (address)","function circuitBreaker() view returns (address)"]),j=new ethers.utils.Interface(["function getUcdController() view returns (address)","function ucdToken() view returns (address)"]),G=new ethers.utils.Interface(["function paused() view returns (bool)"]);async function K(e,t,r){return e.call({to:t,data:r})}async function J(e,t){const r=new ethers.utils.Interface([_]).encodeFunctionData("getProtocolPauseStatus",[]);let i;try{return function(e){const t=new ethers.utils.Interface([_]).decodeFunctionResult("getProtocolPauseStatus",e)[0];return Array.isArray(t)?X({positionManagerPaused:t[0],positionManagerLiquidationsPaused:t[1],positionManagerCorePaused:t[2],loanOperationsPaused:t[3],collateralManagerPaused:t[4],liquidationManagerPaused:t[5],circuitBreakerPaused:t[6],ucdControllerPaused:t[7],ucdTokenPaused:t[8],simplePsmPaused:t[9]}):X(t)}(await e.call({to:t,data:r}))}catch(e){i=e}try{return await async function(e,t){const r=await K(e,t,z.encodeFunctionData("views",[])),[i]=z.decodeFunctionResult("views",r),[a,o,n,s,c,l,u,d]=await Promise.all([K(e,t,z.encodeFunctionData("paused",[])),K(e,t,z.encodeFunctionData("liquidationPaused",[])),K(e,t,z.encodeFunctionData("simplePsm",[])),K(e,i,H.encodeFunctionData("core",[])),K(e,i,H.encodeFunctionData("loanOps",[])),K(e,i,H.encodeFunctionData("collateralManager",[])),K(e,i,H.encodeFunctionData("liquidationManager",[])),K(e,i,H.encodeFunctionData("circuitBreaker",[]))]),p=z.decodeFunctionResult("paused",a)[0],h=z.decodeFunctionResult("liquidationPaused",o)[0],g=z.decodeFunctionResult("simplePsm",n)[0],m=H.decodeFunctionResult("core",s)[0],f=H.decodeFunctionResult("loanOps",c)[0],w=H.decodeFunctionResult("collateralManager",l)[0],y=H.decodeFunctionResult("liquidationManager",u)[0],b=H.decodeFunctionResult("circuitBreaker",d)[0],T=await K(e,f,j.encodeFunctionData("getUcdController",[])),P=await K(e,f,j.encodeFunctionData("ucdToken",[])),v=j.decodeFunctionResult("getUcdController",T)[0],S=j.decodeFunctionResult("ucdToken",P)[0],E=async t=>{if(!t||t===ethers.constants.AddressZero)return!1;try{const r=await K(e,t,G.encodeFunctionData("paused",[]));return G.decodeFunctionResult("paused",r)[0]}catch{return!1}},[A,I,$,B,C,M,U,D]=await Promise.all([E(m),E(f),E(w),E(y),E(b),E(v),E(S),E(g)]);return X({positionManagerPaused:p,positionManagerLiquidationsPaused:h,positionManagerCorePaused:A,loanOperationsPaused:I,collateralManagerPaused:$,liquidationManagerPaused:B,circuitBreakerPaused:C,ucdControllerPaused:M,ucdTokenPaused:U,simplePsmPaused:D})}(e,t)}catch(e){const t=i instanceof Error?i.message:String(i),r=e instanceof Error?e.message:String(e);throw new Error(`getProtocolPauseStatus failed (aggregate: ${t}; decomposed: ${r})`)}}var W={hardhat:{chain:"hardhat",chainId:b.hardhat,bitcoinNetwork:w.hardhat,allowArbitraryBitcoinProvider:!0,allowArbitraryEvmRpc:!0,minBitcoinConfirmations:1},sepolia:{chain:"sepolia",chainId:b.sepolia,bitcoinNetwork:w.sepolia,allowArbitraryBitcoinProvider:!1,allowArbitraryEvmRpc:!1,minBitcoinConfirmations:1},ethereum:{chain:"ethereum",chainId:b.ethereum,bitcoinNetwork:w.ethereum,allowArbitraryBitcoinProvider:!1,allowArbitraryEvmRpc:!1,minBitcoinConfirmations:6}};var Y=[{inputs:[{internalType:"uint256",name:"chainId",type:"uint256"}],name:"getProviders",outputs:[{internalType:"bytes32[]",name:"ids",type:"bytes32[]"},{components:[{internalType:"bytes",name:"ciphertext",type:"bytes"},{internalType:"bytes32",name:"pkpId",type:"bytes32"},{internalType:"uint64",name:"addedAt",type:"uint64"}],internalType:"struct BitcoinProviderRegistry.ProviderEntry[]",name:"entries",type:"tuple[]"}],stateMutability:"view",type:"function"}];async function Q(e,t){const r=66===t.length?"0x"+t.slice(-40):t;try{const t=await Lit.Actions.Decrypt({pkpId:r,ciphertext:e}),i="string"==typeof t?t:String(t??"");if(!i)throw new Error("Decrypt returned empty plaintext");return i}catch(e){throw new Error(`Failed to decrypt Bitcoin provider URL (pkpId=${t}): ${e?.message??String(e)}`)}}async function Z(e){const{policy:t,ethersProvider:r,registryAddress:i,devBitcoinProviderUrl:a}=e;if(t.allowArbitraryBitcoinProvider){if(!a)throw new Error("resolveDefaultBitcoinProvider: devBitcoinProviderUrl is required on hardhat (no on-chain registry)");return{name:"Dev Provider",url:a,minConfirmations:t.minBitcoinConfirmations,network:t.bitcoinNetwork}}if(!i)throw new Error(`resolveDefaultBitcoinProvider: BitcoinProviderRegistry address required on chain "${t.chain}"`);const o=await async function(e,t,r){if(e.allowArbitraryBitcoinProvider)throw new Error("fetchAllDecryptedProviderConfigs called with allowArbitraryBitcoinProvider policy \u2014 caller should bypass the registry");const i=await async function(e,t,r){if(!r||"string"!=typeof r)throw new Error("fetchBitcoinProviderEntries: registryAddress (BitcoinProviderRegistry) is required");if(!t)throw new Error("fetchBitcoinProviderEntries: ethers provider is required");const i=new ethers.Contract(r,Y,t),[a,o]=await i.getProviders(e),n=[];for(let e=0;e<a.length;e++)n.push({providerId:a[e],ciphertext:o[e].ciphertext,pkpId:o[e].pkpId,addedAt:Number(o[e].addedAt)});return n}(e.chainId,t,r);if(0===i.length)throw new Error(`No Bitcoin providers registered for chainId=${e.chainId} in ${r}. Admin must addProvider(...) via AdminModule before LIT Actions can sign.`);const a=[];for(const t of i){const r=await Q(t.ciphertext,t.pkpId);a.push({name:`Registry Provider ${t.providerId.slice(0,10)}`,url:r,minConfirmations:e.minBitcoinConfirmations,network:e.bitcoinNetwork})}return a}(t,r,i);return o[0]}var ee,te=[{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 function main(e){for(const[t,r]of Object.entries(e))globalThis[t]=r;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.positionId:",globalThis.positionId),console.log("[Admin Liquidation Validator] ========================================");let t,r,i,a,o,n="0";try{n="0a",console.log("[Step 0a] Validating configuration...");const e=globalThis.chain,d=globalThis.positionId;if(!e)throw new Error('Missing required parameter: "chain"');if(!d)throw new Error('Missing required parameter: "positionId"');n="0b";const f=function(e){const t=T(e),r=W[t];if(!r)throw new Error(`No chain policy registered for "${e}"`);return r}(e),w=f.chain;n="0c";const b=globalThis.contractAddresses;if(!b||"object"!=typeof b)throw new Error("Missing or invalid 'contractAddresses' parameter");if(r=b.PositionManager,i=b.LoanOperationsManagerModule,a=b.LiquidationManagerModule??b.LiquidationManager??"",o=b.BTCSpendAuthorizer||i,!r||!i||!a)throw new Error("Missing one or more required contract addresses (PositionManager, LoanOperationsManagerModule, LiquidationManager or LiquidationManagerModule)");let v;globalThis.rpcUrl&&"string"==typeof globalThis.rpcUrl?(!function(e,t){if(e.allowArbitraryEvmRpc)return;if(!t||"string"!=typeof t)throw new Error("validateEvmRpcForPolicy: rpcUrl must be a non-empty string");let r;try{r=new URL(t).hostname}catch(e){throw new Error(`Invalid rpcUrl: ${e?.message??String(e)}`)}if(!y.some((e=>r===e||r.endsWith(`.${e}`))))throw new Error(`Custom RPC domain "${r}" is not in the approved list: ${y.join(", ")}`)}(f,globalThis.rpcUrl),v=globalThis.rpcUrl):v=await Lit.Actions.getRpcUrl({chain:e}),t=new ethers.providers.JsonRpcProvider(v);const S=await Z({policy:f,ethersProvider:t,registryAddress:b.BitcoinProviderRegistry,devBitcoinProviderUrl:globalThis.devBitcoinProviderUrl});console.log(` \u2705 Bitcoin Provider: ${S.name} (min confs ${S.minConfirmations})`);!function(e,t){const r=V[e],i=[];for(const e of r)t[e]&&i.push(e);if(i.length>0)throw new Error(JSON.stringify({code:"PROTOCOL_PAUSED",operation:e,paused:i,status:t}))}("adminLiquidation",await J(t,r));const E=new k(globalThis.priceProviders),A=new C({providerUrl:S.url}),I=(l={contractAddress:o,chain:e,chainId:f.chainId,rpcUrl:globalThis.rpcUrl,bitcoinProvider:A,minConfirmations:S.minConfirmations},new R(l)),$=function(e){return new F(e)}({contractAddress:r,termManagerAddress:b.TermManagerModule,loanOpsManagerAddress:i,liquidationManagerAddress:a,chain:e,chainId:P(w),rpcUrl:globalThis.rpcUrl,vaultBalance:I,priceOracle:E});n="1",console.log("[Step 1] Getting vault snapshot...");const B=await $.getVaultSnapshot(d);if(B.btcPriceUsd<s||B.btcPriceUsd>c)throw new Error(`Bitcoin price ${B.btcPriceUsd} is outside acceptable range (${s} - ${c}). This may indicate oracle manipulation or stale price data. Please try again later.`);console.log(` \u2705 BTC price validation passed: $${(Number(B.btcPriceUsd)/1e8).toFixed(2)}`),n="2",console.log("[Step 2] Validating position state...");const M=["ACTIVE","EXPIRED","LIQUIDATABLE"],U=p(B.status);if(!M.includes(U))throw new Error(`Invalid position status for liquidation: ${U}. Must be ACTIVE, EXPIRED, or LIQUIDATABLE.`);if(console.log(` \u2705 Position status valid for liquidation: ${p(B.status)}`),n="3",console.log("[Step 3] Checking liquidation eligibility..."),!B.isLiquidatable)throw new Error(`Position is not liquidatable (${O(B.collateralRatioBps)}% >= ${O(B.currentLiquidationThreshold)}%) - liquidation rejected.`);console.log(" \u2705 Position is liquidatable"),console.log(` - Current ratio: ${O(B.collateralRatioBps)}%`),console.log(` - Liquidation threshold: ${O(B.currentLiquidationThreshold)}%`),n="4",console.log("[Step 4] Building admin liquidation EIP-712 digest...");const D=q()+u,x=P(w),L=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(m)),N={name:h,version:g,chainId:x,verifyingContract:ethers.utils.getAddress(a)},_={AdminLiquidationAuth:[{name:"actionTag",type:"bytes32"},{name:"positionId",type:"bytes32"},{name:"quantumTimestamp",type:"uint256"},{name:"btcPrice",type:"uint256"},{name:"btcVaultBalance",type:"uint256"}]},X={actionTag:L,positionId:d,quantumTimestamp:D,btcPrice:B.btcPriceUsd.toString(),btcVaultBalance:B.availableBTCSats.toString()};globalThis.publicKey;n="5",console.log("[Step 5] Signing authorization...");const z=globalThis.validatorWalletAddress;if(!z||!z.startsWith("0x"))throw new Error("validatorWalletAddress required \u2014 must be a createWallet-derived address");const H=await Lit.Actions.getPrivateKey({pkpId:z}),j="string"==typeof H?H.startsWith("0x")?H:`0x${H}`:String(H),G=new ethers.Wallet(j),K=await G._signTypedData(N,_,X);let Y=null;if(globalThis.callIsLiquidatable&&a){n="6",console.log("[Step 6] Calling isLiquidatable() on contract...");try{new ethers.Contract(a,te,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(e){console.warn(` \u26a0\ufe0f Failed to call isLiquidatable(): ${e.message}`)}}return console.log("[Admin Liquidation Validator] \u2705 Complete"),{approved:!0,positionId:d,btcPrice:B.btcPriceUsd.toString(),btcAmountSats:B.availableBTCSats.toString(),signature:K,timestamp:D,validatorPkp:G.address,isLiquidatable:B.isLiquidatable,isLiquidatableOnChain:Y}}catch(e){return console.error("[Admin Liquidation Validator] \u274c Failed:",e.message),console.error(`[Admin Liquidation Validator] Failed at step: ${n}`),{approved:!1,reason:e.message||e.toString(),positionId:globalThis.positionId,timestamp:Date.now()}}var l}return ee=a,((a,o,n,s)=>{if(o&&"object"==typeof o||"function"==typeof o)for(let c of r(o))i.call(a,c)||c===n||e(a,c,{get:()=>o[c],enumerable:!(s=t(o,c))||s.enumerable});return a})(e({},"__esModule",{value:!0}),ee)})();if("object"==typeof _LIT_ACTION_)if("function"==typeof _LIT_ACTION_.main)var main=_LIT_ACTION_.main;else"function"==typeof _LIT_ACTION_.go&&_LIT_ACTION_.go();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2e1db5737f817c322a11df3a1347723e021f2f6fc97728466d40cd2c1c9e8527
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "admin-liquidation-validator",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 117829,
|
|
4
|
+
"minifiedSize": 51420,
|
|
5
|
+
"compressionRatio": 0.5636048850452775,
|
|
6
|
+
"hash": "2e1db5737f817c322a11df3a1347723e021f2f6fc97728466d40cd2c1c9e8527",
|
|
7
|
+
"buildTime": 1778598000236,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var _LIT_ACTION_=(()=>{var t=Object.defineProperty,e=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,r=(e,n)=>{for(var o in n)t(e,o,{get:n[o],enumerable:!0})},i={};async function s(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 n=e.slice(2,66),o=e.slice(66,130),r=(BigInt("0x"+o)%2n===0n?"02":"03")+n,i=ethers.utils.sha256("0x"+r);let s;s="mainnet"===t?"bc":"testnet"===t?"tb":"bcrt";const a=function(t,e,n){const o="qpzry9x8gf2tvdw0s3jn54khce6mua7l",r=[];for(let t=0;t<n.length;t+=2)r.push(parseInt(n.substr(t,2),16));const i=c(r,8,5,!0),s=[e,...i],a=function(t,e){const n=function(t){const e=[];for(let n=0;n<t.length;n++)e.push(t.charCodeAt(n)>>5);e.push(0);for(let n=0;n<t.length;n++)e.push(31&t.charCodeAt(n));return e}(t).concat(e).concat([0,0,0,0,0,0]),o=1^function(t){const e=[996825010,642813549,513874426,1027748829,705979059];let n=1;for(let o=0;o<t.length;o++){const r=n>>25;n=(33554431&n)<<5^t[o];for(let t=0;t<5;t++)r>>t&1&&(n^=e[t])}return n}(n),r=[];for(let t=0;t<6;t++)r.push(o>>5*(5-t)&31);return r}(t,s),u=[...s,...a];let l=t+"1";for(let t=0;t<u.length;t++)l+=o[u[t]];return l}(s,0,ethers.utils.ripemd160(i).slice(2));return a}async function a(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 n=e.slice(2,66),o=e.slice(66,130),r=(BigInt("0x"+o)%2n===0n?"02":"03")+n,i=ethers.utils.sha256("0x"+r);let s;s="mainnet"===t?"00":"6f";const a=s+ethers.utils.ripemd160(i).slice(2),u=ethers.utils.sha256("0x"+a);return function(t){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";let n=BigInt("0x"+t),o="";for(;n>0n;){o=e[Number(n%58n)]+o,n/=58n}for(let e=0;e<t.length&&"00"===t.substr(e,2);e+=2)o="1"+o;return o}(a+ethers.utils.sha256(u).slice(2,10))}function u(t){if(/^(bc1|tb1|bcrt1)[0-9a-zA-Z]{11,87}$/.test(t)){const e=function(t){const e="qpzry9x8gf2tvdw0s3jn54khce6mua7l",n=t.lastIndexOf("1");if(n<1)throw new Error("No separator character for bech32");t.substring(0,n).toLowerCase();const o=t.substring(n+1).toLowerCase(),r=[];for(let t=0;t<o.length;t++){const n=e.indexOf(o[t]);if(-1===n)throw new Error(`Invalid bech32 character: ${o[t]}`);r.push(n)}const i=r.slice(0,-6),s=(i[0],c(i.slice(1),5,8,!1));return{witnessProgram:s.map((t=>t.toString(16).padStart(2,"0"))).join("")}}(t);return"0014"+e.witnessProgram}{const e=function(t){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";let n=0n;for(let o=0;o<t.length;o++){const r=e.indexOf(t[o]);if(r<0)throw new Error(`Invalid Base58 character: ${t[o]}`);n=58n*n+BigInt(r)}let o=n.toString(16);o.length%2&&(o="0"+o);for(let e=0;e<t.length&&"1"===t[e];e++)o="00"+o;return o}(t);return"76a914"+e.slice(2,e.length-8)+"88ac"}}function c(t,e,n,o){let r=0,i=0;const s=[],a=(1<<n)-1;for(let o=0;o<t.length;o++){const u=t[o];if(u<0||u>>e)throw new Error("Invalid data for bit conversion");for(r=r<<e|u,i+=e;i>=n;)i-=n,s.push(r>>i&a)}if(o)i>0&&s.push(r<<n-i&a);else if(i>=e||r<<n-i&a)throw new Error("Invalid padding in bit conversion");return s}r(i,{addressToScriptPubKey:()=>u,deriveBitcoinAddress:()=>s,deriveBitcoinAddressP2PKH:()=>a});var l,d,g=(l={"src/modules/bitcoin/address.ts"(){"use strict"}},function(){return l&&(d=(0,l[n(l)[0]])(l=0)),d}),p={};function h(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 f(t,e){const n=t.toString(16).padStart(2*e,"0").match(/../g);if(!n)throw new Error(`Invalid hex conversion: ${t}`);return n.reverse().join("")}function w(t){return t<253?t.toString(16).padStart(2,"0"):t<=65535?"fd"+f(t,2):t<=4294967295?"fe"+f(t,4):"ff"+f(t,8)}r(p,{main:()=>main}),g(),g();var m=100,y=1e5,P=60;function b(t){return Math.floor(t/P)*P}async function v(){const t=Math.floor(Date.now()/1e3),e=t%P;if(!function(t){const e=(t??Math.floor(Date.now()/1e3))%P;return e<8||e>=52}(t))return;let n;n=e>=52?P-e+8:8-e,console.log(`[Quantum] Waiting ${n}s for safe moment (current time in quantum: ${e}s)`),await new Promise((t=>setTimeout(t,1e3*n))),console.log(`[Quantum] Safe quantum ready: ${b(Math.floor(Date.now()/1e3))}`)}var T=class t{static verifyAmountPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.amount||null===t.amount)throw new Error("auth.amount is required");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");return t}static async recoverSigner(t,e){try{const n=ethers.utils.arrayify(t),o=ethers.utils.hashMessage(n);return ethers.utils.recoverAddress(o,e)}catch(t){throw new Error(`Failed to recover signer: ${t instanceof Error?t.message:String(t)}`)}}static async checkEIP1271(t,e,n,o){try{const r=new ethers.Contract(t,["function isValidSignature(bytes32,bytes) view returns (bytes4)"],o);return"0x1626ba7e"===await r.isValidSignature(e,n)}catch{return!1}}static async verifyActionAuthorization(t,e,n,o){const r=Math.floor(Date.now()/1e3);await v();const i=o?.strictCurrentQuantum?r:void 0;try{!function(t,e,n){const o=e??Math.floor(Date.now()/1e3),r=b(o),i=b(t);if(n?.strictCurrentQuantum){if(i!==r)throw new Error(`[Quantum] Signature must come from current quantum (strict mode). Signature quantum: ${i}, current quantum: ${r}`);return}const s=r-P,a=r+P;if(i!==s&&i!==r&&i!==a)throw new Error(`[Quantum] Signature outside 3-quantum window. Signature quantum: ${i}, Valid range: [${s} (past), ${r} (current), ${a} (next)]`);if(i===s){if(o<s+8)throw new Error(`[Quantum] Dead zone violation: Current time (${o}) is in first 8 seconds of PAST quantum (${s}). This is unsafe due to boundary instability.`)}else if(i===a&&o>=a+52)throw new Error(`[Quantum] Dead zone violation: Current time (${o}) is in last 8 seconds of NEXT quantum (${a}). This is unsafe due to boundary instability.`)}(t.timestamp,i,{strictCurrentQuantum:!0===o?.strictCurrentQuantum})}catch{return!1}const s=e(),a=await this.recoverSigner(s,t.signature);return await n(a,s,t.signature)}static async verifyAmountPositionActionAuthorization(e,n,o){return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),n="string"==typeof e.amount?BigInt(e.amount):e.amount,o=[e.positionId,e.timestamp,e.chainId,n.toString(),t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],o)}),(async(e,r,i)=>e.toLowerCase()===n.toLowerCase()||!!o&&t.checkEIP1271(n,r,i,o)))}static async verifyMintAuthorization(t,e,n){return"mint-ucd"===t.action&&this.verifyAmountPositionActionAuthorization(t,e,n)}static async verifyWithdrawAuthorization(e,n,o){if("withdraw-btc"!==e.action)return!1;return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),n="string"==typeof e.amount?BigInt(e.amount):e.amount,o=[e.positionId,e.timestamp,e.chainId,n.toString(),e.destinationAddress,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","string","bytes32"],o)}),(async(e,r,i)=>e.toLowerCase()===n.toLowerCase()||!!o&&t.checkEIP1271(n,r,i,o)))}static verifyTermPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.newTerm||null===t.newTerm)throw new Error("auth.newTerm is required");if("number"!=typeof t.newTerm)throw new Error("auth.newTerm must be a number");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");return t}static async verifyTermPositionActionAuthorization(e,n,o){return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),n=[e.positionId,e.timestamp,e.chainId,e.newTerm,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],n)}),(async(e,r,i)=>e.toLowerCase()===n.toLowerCase()||!!o&&t.checkEIP1271(n,r,i,o)))}static async verifyExtendAuthorization(t,e,n){return"extend-position"===t.action&&this.verifyTermPositionActionAuthorization(t,e,n)}static async verifyBtcExecutionAuthorization(e,n,o){if("execute-btc-withdrawal"!==e.action)return!1;return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),n=[e.positionId,e.timestamp,e.chainId,e.utxoIdentifier,e.networkFee,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","string","uint256","bytes32"],n)}),(async(e,r,i)=>e.toLowerCase()===n.toLowerCase()||!!o&&t.checkEIP1271(n,r,i,o)),{strictCurrentQuantum:!0})}};function A(){const t=globalThis,e=t.jsParams;return null==e||"object"!=typeof e||Array.isArray(e)?t:e}var I="function getProtocolPauseStatus() view returns (tuple(bool positionManagerPaused, bool positionManagerLiquidationsPaused, bool positionManagerCorePaused, bool loanOperationsPaused, bool collateralManagerPaused, bool liquidationManagerPaused, bool circuitBreakerPaused, bool ucdControllerPaused, bool ucdTokenPaused, bool simplePsmPaused))",k={mintUcd:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],withdrawBtc:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],processPayment:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused"],extendPosition:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],liquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],adminLiquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],btcTransactionSign:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused"]};function x(t){return{positionManagerPaused:t.positionManagerPaused,positionManagerLiquidationsPaused:t.positionManagerLiquidationsPaused,positionManagerCorePaused:t.positionManagerCorePaused,loanOperationsPaused:t.loanOperationsPaused,collateralManagerPaused:t.collateralManagerPaused,liquidationManagerPaused:t.liquidationManagerPaused,circuitBreakerPaused:t.circuitBreakerPaused,ucdControllerPaused:t.ucdControllerPaused,ucdTokenPaused:t.ucdTokenPaused,simplePsmPaused:t.simplePsmPaused}}var M,C=new ethers.utils.Interface(["function views() view returns (address)","function paused() view returns (bool)","function liquidationPaused() view returns (bool)","function simplePsm() view returns (address)"]),$=new ethers.utils.Interface(["function core() view returns (address)","function loanOps() view returns (address)","function collateralManager() view returns (address)","function liquidationManager() view returns (address)","function circuitBreaker() view returns (address)"]),E=new ethers.utils.Interface(["function getUcdController() view returns (address)","function ucdToken() view returns (address)"]),S=new ethers.utils.Interface(["function paused() view returns (bool)"]);async function q(t,e,n){return t.call({to:e,data:n})}async function O(t,e){const n=new ethers.utils.Interface([I]).encodeFunctionData("getProtocolPauseStatus",[]);let o;try{return function(t){const e=new ethers.utils.Interface([I]).decodeFunctionResult("getProtocolPauseStatus",t)[0];return Array.isArray(e)?x({positionManagerPaused:e[0],positionManagerLiquidationsPaused:e[1],positionManagerCorePaused:e[2],loanOperationsPaused:e[3],collateralManagerPaused:e[4],liquidationManagerPaused:e[5],circuitBreakerPaused:e[6],ucdControllerPaused:e[7],ucdTokenPaused:e[8],simplePsmPaused:e[9]}):x(e)}(await t.call({to:e,data:n}))}catch(t){o=t}try{return await async function(t,e){const n=await q(t,e,C.encodeFunctionData("views",[])),[o]=C.decodeFunctionResult("views",n),[r,i,s,a,u,c,l,d]=await Promise.all([q(t,e,C.encodeFunctionData("paused",[])),q(t,e,C.encodeFunctionData("liquidationPaused",[])),q(t,e,C.encodeFunctionData("simplePsm",[])),q(t,o,$.encodeFunctionData("core",[])),q(t,o,$.encodeFunctionData("loanOps",[])),q(t,o,$.encodeFunctionData("collateralManager",[])),q(t,o,$.encodeFunctionData("liquidationManager",[])),q(t,o,$.encodeFunctionData("circuitBreaker",[]))]),g=C.decodeFunctionResult("paused",r)[0],p=C.decodeFunctionResult("liquidationPaused",i)[0],h=C.decodeFunctionResult("simplePsm",s)[0],f=$.decodeFunctionResult("core",a)[0],w=$.decodeFunctionResult("loanOps",u)[0],m=$.decodeFunctionResult("collateralManager",c)[0],y=$.decodeFunctionResult("liquidationManager",l)[0],P=$.decodeFunctionResult("circuitBreaker",d)[0],b=await q(t,w,E.encodeFunctionData("getUcdController",[])),v=await q(t,w,E.encodeFunctionData("ucdToken",[])),T=E.decodeFunctionResult("getUcdController",b)[0],A=E.decodeFunctionResult("ucdToken",v)[0],I=async e=>{if(!e||e===ethers.constants.AddressZero)return!1;try{const n=await q(t,e,S.encodeFunctionData("paused",[]));return S.decodeFunctionResult("paused",n)[0]}catch{return!1}},[k,M,O,B,L,F,z,D]=await Promise.all([I(f),I(w),I(m),I(y),I(P),I(T),I(A),I(h)]);return x({positionManagerPaused:g,positionManagerLiquidationsPaused:p,positionManagerCorePaused:k,loanOperationsPaused:M,collateralManagerPaused:O,liquidationManagerPaused:B,circuitBreakerPaused:L,ucdControllerPaused:F,ucdTokenPaused:z,simplePsmPaused:D})}(t,e)}catch(t){const e=o instanceof Error?o.message:String(o),n=t instanceof Error?t.message:String(t);throw new Error(`getProtocolPauseStatus failed (aggregate: ${e}; decomposed: ${n})`)}}async function main(t){for(const[e,n]of Object.entries(t))globalThis[e]=n;const e=A();console.log("[Step 0] Validating configuration...");const n=e.chain,o=e.bitcoinNetwork;if(!n)throw new Error('Missing required parameter: "chain". Must be "sepolia" or "ethereum"');if(!o)throw new Error('Missing required parameter: "bitcoinNetwork". Must be "mainnet", "testnet", or "regtest"');if(!["mainnet","testnet","regtest"].includes(o))throw new Error(`Invalid bitcoinNetwork: "${o}". Must be "mainnet", "testnet", or "regtest"`);const r=e.auth;if(!r||"object"!=typeof r)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!r.positionId)throw new Error("auth.positionId is required");if(!r.utxo||"object"!=typeof r.utxo)throw new Error("auth.utxo is required and must be an object");if(void 0===r.networkFee||r.networkFee<0)throw new Error("auth.networkFee is required and must be non-negative");if(!r.utxo.txid||void 0===r.utxo.vout||!r.utxo.value||!r.utxo.scriptPubKey)throw new Error("Invalid UTXO: must have txid, vout, value, scriptPubKey");const a=r.positionId,c=r.utxo,l=r.utxos&&r.utxos.length>0?r.utxos:[c],d=r.networkFee;console.log(" \u2705 Parameters validated"),console.log(` Position: ${a}`),console.log(` UTXOs to spend: ${l.length}`),console.log(` Network fee: ${d} sats`),console.log(` Bitcoin network: ${o}`);const p=e.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 P=p;if(!e.contractAddresses||!e.contractAddresses.LoanOperationsManagerModule)throw new Error("contractAddresses.LoanOperationsManagerModule is required");if(!e.contractAddresses.PositionManager)throw new Error("contractAddresses.PositionManager is required");const b=e.contractAddresses.LoanOperationsManagerModule,v=e.contractAddresses.PositionManager,I=e.contractAddresses.BTCSpendAuthorizer??b;console.log(" \u2705 Contract addresses loaded from parameters");let x="0.5";try{console.log("[BTC Transaction Signer] Started"),console.log(` Position: ${a}`),console.log(` UTXO: ${c.txid}:${c.vout} (${c.value} sats)`),x="1",console.log("[Step 1] Setting up RPC connection...");const t=e.rpcUrl;let p;t&&"string"==typeof t?(console.log(` \u2705 Using RPC URL override: ${function(t){if(null==t||"string"!=typeof t)return"[missing]";let e=t.replace(/([?&])(dkey|apikey|api_key|key|token|secret|password|auth|authorization)=[^&]*/gi,"$1$2=[REDACTED]");try{const t=new URL(e);(t.username||t.password)&&(t.username="",t.password="",e=t.toString())}catch{}return e}(t)}`),p=t):(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:n}),console.log(` Using LIT-provided RPC URL for chain: ${n}`));const b=new ethers.providers.JsonRpcProvider(p);!function(t,e){const n=k[t],o=[];for(const t of n)e[t]&&o.push(t);if(o.length>0)throw new Error(JSON.stringify({code:"PROTOCOL_PAUSED",operation:t,paused:o,status:e}))}("btcTransactionSign",await O(b,v)),x="2",console.log("[Step 2] UTXO existence check..."),console.log(` UTXO: ${c.txid}:${c.vout}`),console.log(" \u26a0\ufe0f UTXO existence check deferred to Bitcoin consensus"),console.log(" \u2705 Proceeding (Bitcoin will reject if spent)"),x="3",console.log("[Step 3] Querying authorization from contract...");const A=[{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"}],M=new ethers.Contract(I,A,b),C=await M.getAuthorizedSpends(a);let $=null;for(const t of C){const e=t.txid.toLowerCase(),n=c.txid.toLowerCase(),o=Number(t.vout),r=Number(c.vout);if(e===n&&o===r){$=t;break}}if(!$)throw new Error(`No authorization found for UTXO ${c.txid}:${c.vout}`);const E=BigInt($.targetAmount.toString()),S=$.targetAddress,q=BigInt($.authorizedAt.toString());console.log(" \u2705 Authorization found on-chain"),console.log(` Target amount: ${E.toString()} sats`),console.log(` Destination: ${S}`),console.log(` Authorized at: ${q.toString()}`),x="3.5",console.log("[Step 3.5] Verifying owner signature for network fee...");const B=[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],L=new ethers.Contract(v,B,b),F=await L.core();console.log(` Core facet address: ${F}`);const z=[{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"}],D=new ethers.Contract(F,z,b),U=await D.getPositionDetails(a);let K;if(5===U.status){if(!e.contractAddresses?.LITActionValidator)throw new Error("LITActionValidator address required for liquidated position BTC signing");const t=new ethers.Contract(e.contractAddresses.LITActionValidator,["function pkpOwners(bytes32) view returns (address)"],b),n=await t.pkpOwners(U.pkpId);if(!n||n===ethers.constants.AddressZero)throw new Error("No PKP owner registered for liquidated position \u2014 cannot authorize BTC signing");K=n,console.log(` \u2139\ufe0f LIQUIDATED position \u2014 verifying against PKP owner: ${K}`)}else K=U.borrower;console.log(` Position owner: ${K}`);const j=`${c.txid}:${c.vout}`;if(!await T.verifyBtcExecutionAuthorization({positionId:r.positionId,timestamp:r.timestamp,chainId:r.chainId,utxoIdentifier:j,networkFee:r.networkFee,action:"execute-btc-withdrawal",signature:r.signature},K,b))throw new Error("Caller not authorized for execution");if(console.log(" \u2705 Owner signature verified"),x="4",console.log("[Step 4] Validating network fee..."),d>y)throw new Error(`Network fee ${d} sats exceeds maximum ${y} sats`);if(d<m)throw new Error(`Network fee ${d} sats below minimum ${m} sats`);if(BigInt(d)>=E)throw new Error(`Network fee ${d} sats cannot exceed target amount ${E.toString()} sats`);const R=Number(E)-d;console.log(" \u2705 Network fee validated"),console.log(` User receives: ${R} sats`),x="5",console.log("[Step 5] Deriving PKP Bitcoin address...");const N=await s(P,o);console.log(` \u2705 PKP BTC address: ${N}`),x="6",console.log("[Step 6] Validating UTXO ownership...");const{addressToScriptPubKey:_}=await Promise.resolve().then((()=>(g(),i))),X=_(N);for(const t of l){const e=t.scriptPubKey.startsWith("0x")?t.scriptPubKey.slice(2):t.scriptPubKey;if(e!==X)throw new Error(`UTXO ${t.txid}:${t.vout} does not belong to vault. Expected scriptPubKey ${X}, got ${e}`)}console.log(` \u2705 All ${l.length} UTXO(s) belong to vault (PKP address)`),x="7",console.log("[Step 7] Validating UTXO value...");const Q=Number(E),V=l.reduce(((t,e)=>t+e.value),0);if(V<Q)throw new Error(`Insufficient UTXO value: total ${V} sats across ${l.length} input(s), need ${Q} sats`);const W=V-Q;console.log(` \u2705 Total UTXO value sufficient (${l.length} input(s))`),console.log(` Total value: ${V} sats`),console.log(` Total required: ${Q} sats`),console.log(` Change: ${W} sats`),x="8",console.log("[Step 8] Building Bitcoin transaction...");const{txHex:H,sigHashes:Z}=function(t){const{utxos:e,destination:n,amount:o,changeAddress:r,changeAmount:i}=t;if(0===e.length)throw new Error("buildBitcoinTransactionMultiInput: at least one UTXO required");const s="02000000",a="ffffffff",c="00000000",l=w(e.length),d=e.map((t=>h(t.txid)+f(t.vout,4)+"00"+a)).join(""),g=[],p=u(n);if(g.push(f(o,8)+w(p.length/2)+p),i>0){const t=u(r);g.push(f(i,8)+w(t.length/2)+t)}const m=w(g.length),y=g.join(""),P=s+l+d+m+y+c,b=e.map((t=>h(t.txid)+f(t.vout,4))).join(""),v=ethers.utils.sha256(ethers.utils.sha256("0x"+b)),T=e.map((()=>a)).join(""),A=ethers.utils.sha256(ethers.utils.sha256("0x"+T)),I=ethers.utils.sha256(ethers.utils.sha256("0x"+y));return{txHex:P,sigHashes:e.map((t=>{const e=h(t.txid)+f(t.vout,4),n="1976a914"+(t.scriptPubKey.startsWith("0x")?t.scriptPubKey.slice(2):t.scriptPubKey).slice(4)+"88ac",o=s+v.slice(2)+A.slice(2)+e+n+f(t.value,8)+a+I.slice(2)+c+"01000000";return ethers.utils.sha256(ethers.utils.sha256("0x"+o))}))}}({utxos:l,destination:S,amount:R,changeAddress:N,changeAmount:W,network:o,pkpPublicKey:P});console.log(` \u2705 Transaction built (${l.length} input(s))`),console.log(` Signature hashes: ${Z.length}`),x="9",console.log(`[Step 9] Signing ${Z.length} input(s)...`);const J=globalThis.pkpId;if(!J)throw new Error("Missing required parameter: pkpId (user PKP ID for BTC signing)");const G=await Lit.Actions.getPrivateKey({pkpId:J}),Y="string"==typeof G?G.startsWith("0x")?G:`0x${G}`:String(G),tt=new ethers.utils.SigningKey(Y),et=[];for(let t=0;t<Z.length;t++){const e=tt.signDigest(Z[t]);et.push(ethers.utils.joinSignature(e)),console.log(` \u2705 Signed input ${t}: ${Z[t]}`)}return console.log("[BTC Transaction Signer] \u2705 Complete"),{success:!0,unsignedTxHex:H,sigHashes:Z,sigHash:Z[0],signatures:et,signature:et[0],pkpPublicKey:P,pkpBtcAddress:N,destination:S,userReceivesAmount:R,networkFee:d,changeAmount:W,utxos:l.map((t=>({txid:t.txid,vout:t.vout,value:t.value}))),utxo:{txid:c.txid,vout:c.vout,value:c.value},authorization:{targetAmount:E.toString(),authorizedAt:q.toString()},timestamp:Date.now(),action:"btc-transaction-signer"}}catch(t){return console.error("[BTC Transaction Signer] \u274c Failed:",t.message),console.error(`[BTC Transaction Signer] Failed at step: ${x}`),{success:!1,error:t.message||t.toString(),positionId:r?.positionId,timestamp:Date.now()}}}return M=p,((r,i,s,a)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let u of n(i))o.call(r,u)||u===s||t(r,u,{get:()=>i[u],enumerable:!(a=e(i,u))||a.enumerable});return r})(t({},"__esModule",{value:!0}),M)})();if("object"==typeof _LIT_ACTION_)if("function"==typeof _LIT_ACTION_.main)var main=_LIT_ACTION_.main;else"function"==typeof _LIT_ACTION_.go&&_LIT_ACTION_.go();
|
|
1
|
+
var _LIT_ACTION_=(()=>{var t=Object.defineProperty,e=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,r=Object.prototype.hasOwnProperty,o=(e,n)=>{for(var r in n)t(e,r,{get:n[r],enumerable:!0})},i={};async function s(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 n=e.slice(2,66),r=e.slice(66,130),o=(BigInt("0x"+r)%2n===0n?"02":"03")+n,i=ethers.utils.sha256("0x"+o);let s;s="mainnet"===t?"bc":"testnet"===t?"tb":"bcrt";const a=function(t,e,n){const r="qpzry9x8gf2tvdw0s3jn54khce6mua7l",o=[];for(let t=0;t<n.length;t+=2)o.push(parseInt(n.substr(t,2),16));const i=c(o,8,5,!0),s=[e,...i],a=function(t,e){const n=function(t){const e=[];for(let n=0;n<t.length;n++)e.push(t.charCodeAt(n)>>5);e.push(0);for(let n=0;n<t.length;n++)e.push(31&t.charCodeAt(n));return e}(t).concat(e).concat([0,0,0,0,0,0]),r=1^function(t){const e=[996825010,642813549,513874426,1027748829,705979059];let n=1;for(let r=0;r<t.length;r++){const o=n>>25;n=(33554431&n)<<5^t[r];for(let t=0;t<5;t++)o>>t&1&&(n^=e[t])}return n}(n),o=[];for(let t=0;t<6;t++)o.push(r>>5*(5-t)&31);return o}(t,s),u=[...s,...a];let d=t+"1";for(let t=0;t<u.length;t++)d+=r[u[t]];return d}(s,0,ethers.utils.ripemd160(i).slice(2));return a}async function a(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 n=e.slice(2,66),r=e.slice(66,130),o=(BigInt("0x"+r)%2n===0n?"02":"03")+n,i=ethers.utils.sha256("0x"+o);let s;s="mainnet"===t?"00":"6f";const a=s+ethers.utils.ripemd160(i).slice(2),u=ethers.utils.sha256("0x"+a);return function(t){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";let n=BigInt("0x"+t),r="";for(;n>0n;){r=e[Number(n%58n)]+r,n/=58n}for(let e=0;e<t.length&&"00"===t.substr(e,2);e+=2)r="1"+r;return r}(a+ethers.utils.sha256(u).slice(2,10))}function u(t){if(/^(bc1|tb1|bcrt1)[0-9a-zA-Z]{11,87}$/.test(t)){const e=function(t){const e="qpzry9x8gf2tvdw0s3jn54khce6mua7l",n=t.lastIndexOf("1");if(n<1)throw new Error("No separator character for bech32");t.substring(0,n).toLowerCase();const r=t.substring(n+1).toLowerCase(),o=[];for(let t=0;t<r.length;t++){const n=e.indexOf(r[t]);if(-1===n)throw new Error(`Invalid bech32 character: ${r[t]}`);o.push(n)}const i=o.slice(0,-6),s=(i[0],c(i.slice(1),5,8,!1));return{witnessProgram:s.map((t=>t.toString(16).padStart(2,"0"))).join("")}}(t);return"0014"+e.witnessProgram}{const e=function(t){const e="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";let n=0n;for(let r=0;r<t.length;r++){const o=e.indexOf(t[r]);if(o<0)throw new Error(`Invalid Base58 character: ${t[r]}`);n=58n*n+BigInt(o)}let r=n.toString(16);r.length%2&&(r="0"+r);for(let e=0;e<t.length&&"1"===t[e];e++)r="00"+r;return r}(t);return"76a914"+e.slice(2,e.length-8)+"88ac"}}function c(t,e,n,r){let o=0,i=0;const s=[],a=(1<<n)-1;for(let r=0;r<t.length;r++){const u=t[r];if(u<0||u>>e)throw new Error("Invalid data for bit conversion");for(o=o<<e|u,i+=e;i>=n;)i-=n,s.push(o>>i&a)}if(r)i>0&&s.push(o<<n-i&a);else if(i>=e||o<<n-i&a)throw new Error("Invalid padding in bit conversion");return s}o(i,{addressToScriptPubKey:()=>u,deriveBitcoinAddress:()=>s,deriveBitcoinAddressP2PKH:()=>a});var d,l,g=(d={"src/modules/bitcoin/address.ts"(){"use strict"}},function(){return d&&(l=(0,d[n(d)[0]])(d=0)),l}),h={};function p(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 f(t,e){const n=t.toString(16).padStart(2*e,"0").match(/../g);if(!n)throw new Error(`Invalid hex conversion: ${t}`);return n.reverse().join("")}function w(t){return t<253?t.toString(16).padStart(2,"0"):t<=65535?"fd"+f(t,2):t<=4294967295?"fe"+f(t,4):"ff"+f(t,8)}o(h,{main:()=>main}),g(),g();var m=100,y=1e5,P=60;function b(t){return Math.floor(t/P)*P}async function v(){const t=Math.floor(Date.now()/1e3),e=t%P;if(!function(t){const e=(t??Math.floor(Date.now()/1e3))%P;return e<8||e>=52}(t))return;let n;n=e>=52?P-e+8:8-e,console.log(`[Quantum] Waiting ${n}s for safe moment (current time in quantum: ${e}s)`),await new Promise((t=>setTimeout(t,1e3*n))),console.log(`[Quantum] Safe quantum ready: ${b(Math.floor(Date.now()/1e3))}`)}var A=class t{static verifyAmountPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.amount||null===t.amount)throw new Error("auth.amount is required");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");return t}static async recoverSigner(t,e){try{const n=ethers.utils.arrayify(t),r=ethers.utils.hashMessage(n);return ethers.utils.recoverAddress(r,e)}catch(t){throw new Error(`Failed to recover signer: ${t instanceof Error?t.message:String(t)}`)}}static async checkEIP1271(t,e,n,r){try{const o=new ethers.Contract(t,["function isValidSignature(bytes32,bytes) view returns (bytes4)"],r);return"0x1626ba7e"===await o.isValidSignature(e,n)}catch{return!1}}static async checkPositionDelegate(t,e,n,r){try{const o=new ethers.Contract(n,["function positionDelegate(bytes32) view returns (address)"],r),i=await o.positionDelegate(t);return!(!i||i===ethers.constants.AddressZero)&&i.toLowerCase()===e.toLowerCase()}catch{return!1}}static async verifyActionAuthorization(t,e,n,r){const o=Math.floor(Date.now()/1e3);await v();const i=r?.strictCurrentQuantum?o:void 0;try{!function(t,e,n){const r=e??Math.floor(Date.now()/1e3),o=b(r),i=b(t);if(n?.strictCurrentQuantum){if(i!==o)throw new Error(`[Quantum] Signature must come from current quantum (strict mode). Signature quantum: ${i}, current quantum: ${o}`);return}const s=o-P,a=o+P;if(i!==s&&i!==o&&i!==a)throw new Error(`[Quantum] Signature outside 3-quantum window. Signature quantum: ${i}, Valid range: [${s} (past), ${o} (current), ${a} (next)]`);if(i===s){if(r<s+8)throw new Error(`[Quantum] Dead zone violation: Current time (${r}) is in first 8 seconds of PAST quantum (${s}). This is unsafe due to boundary instability.`)}else if(i===a&&r>=a+52)throw new Error(`[Quantum] Dead zone violation: Current time (${r}) is in last 8 seconds of NEXT quantum (${a}). This is unsafe due to boundary instability.`)}(t.timestamp,i,{strictCurrentQuantum:!0===r?.strictCurrentQuantum})}catch{return!1}const s=e(),a=await this.recoverSigner(s,t.signature);return await n(a,s,t.signature)}static async verifyAmountPositionActionAuthorization(e,n,r,o){return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),n="string"==typeof e.amount?BigInt(e.amount):e.amount,r=[e.positionId,e.timestamp,e.chainId,n.toString(),t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],r)}),(async(i,s,a)=>{if(i.toLowerCase()===n.toLowerCase())return!0;if(r){if(await t.checkEIP1271(n,s,a,r))return!0;if(o)return t.checkPositionDelegate(e.positionId,i,o,r)}return!1}))}static async verifyMintAuthorization(t,e,n,r){return"mint-ucd"===t.action&&this.verifyAmountPositionActionAuthorization(t,e,n,r)}static async verifyPaymentAuthorization(t,e,n,r){return"make-payment"===t.action&&this.verifyAmountPositionActionAuthorization(t,e,n,r)}static async verifyWithdrawAuthorization(e,n,r,o){if("withdraw-btc"!==e.action)return!1;return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),n="string"==typeof e.amount?BigInt(e.amount):e.amount,r=[e.positionId,e.timestamp,e.chainId,n.toString(),e.destinationAddress,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","string","bytes32"],r)}),(async(i,s,a)=>{if(i.toLowerCase()===n.toLowerCase())return!0;if(r){if(await t.checkEIP1271(n,s,a,r))return!0;if(o)return t.checkPositionDelegate(e.positionId,i,o,r)}return!1}))}static verifyTermPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.newTerm||null===t.newTerm)throw new Error("auth.newTerm is required");if("number"!=typeof t.newTerm)throw new Error("auth.newTerm must be a number");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");return t}static async verifyTermPositionActionAuthorization(e,n,r,o){return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),n=[e.positionId,e.timestamp,e.chainId,e.newTerm,t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],n)}),(async(i,s,a)=>{if(i.toLowerCase()===n.toLowerCase())return!0;if(r){if(await t.checkEIP1271(n,s,a,r))return!0;if(o)return t.checkPositionDelegate(e.positionId,i,o,r)}return!1}))}static async verifyExtendAuthorization(t,e,n,r){return"extend-position"===t.action&&this.verifyTermPositionActionAuthorization(t,e,n,r)}static async verifyBtcExecutionAuthorization(e,n,r,o){if("execute-btc-withdrawal"!==e.action)return!1;return this.verifyActionAuthorization(e,(()=>{const t=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(e.action)),n="bigint"==typeof e.targetAmount?e.targetAmount:BigInt(e.targetAmount),r=[e.positionId,e.timestamp,e.chainId,e.utxoIdentifier,e.networkFee,e.targetAddress,n.toString(),t];return ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","string","uint256","string","uint256","bytes32"],r)}),(async(i,s,a)=>{if(i.toLowerCase()===n.toLowerCase())return!0;if(r){if(await t.checkEIP1271(n,s,a,r))return!0;if(o)return t.checkPositionDelegate(e.positionId,i,o,r)}return!1}),{strictCurrentQuantum:!0})}};function T(){const t=globalThis,e=t.jsParams;return null==e||"object"!=typeof e||Array.isArray(e)?t:e}var I="function getProtocolPauseStatus() view returns (tuple(bool positionManagerPaused, bool positionManagerLiquidationsPaused, bool positionManagerCorePaused, bool loanOperationsPaused, bool collateralManagerPaused, bool liquidationManagerPaused, bool circuitBreakerPaused, bool ucdControllerPaused, bool ucdTokenPaused, bool simplePsmPaused))",k={mintUcd:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],withdrawBtc:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],processPayment:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused"],extendPosition:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","circuitBreakerPaused","ucdControllerPaused","ucdTokenPaused","simplePsmPaused"],liquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],adminLiquidation:["positionManagerLiquidationsPaused","liquidationManagerPaused","circuitBreakerPaused"],btcTransactionSign:["positionManagerPaused","positionManagerCorePaused","loanOperationsPaused","collateralManagerPaused","circuitBreakerPaused"]};function x(t){return{positionManagerPaused:t.positionManagerPaused,positionManagerLiquidationsPaused:t.positionManagerLiquidationsPaused,positionManagerCorePaused:t.positionManagerCorePaused,loanOperationsPaused:t.loanOperationsPaused,collateralManagerPaused:t.collateralManagerPaused,liquidationManagerPaused:t.liquidationManagerPaused,circuitBreakerPaused:t.circuitBreakerPaused,ucdControllerPaused:t.ucdControllerPaused,ucdTokenPaused:t.ucdTokenPaused,simplePsmPaused:t.simplePsmPaused}}var C,M=new ethers.utils.Interface(["function views() view returns (address)","function paused() view returns (bool)","function liquidationPaused() view returns (bool)","function simplePsm() view returns (address)"]),$=new ethers.utils.Interface(["function core() view returns (address)","function loanOps() view returns (address)","function collateralManager() view returns (address)","function liquidationManager() view returns (address)","function circuitBreaker() view returns (address)"]),E=new ethers.utils.Interface(["function getUcdController() view returns (address)","function ucdToken() view returns (address)"]),S=new ethers.utils.Interface(["function paused() view returns (bool)"]);async function q(t,e,n){return t.call({to:e,data:n})}async function B(t,e){const n=new ethers.utils.Interface([I]).encodeFunctionData("getProtocolPauseStatus",[]);let r;try{return function(t){const e=new ethers.utils.Interface([I]).decodeFunctionResult("getProtocolPauseStatus",t)[0];return Array.isArray(e)?x({positionManagerPaused:e[0],positionManagerLiquidationsPaused:e[1],positionManagerCorePaused:e[2],loanOperationsPaused:e[3],collateralManagerPaused:e[4],liquidationManagerPaused:e[5],circuitBreakerPaused:e[6],ucdControllerPaused:e[7],ucdTokenPaused:e[8],simplePsmPaused:e[9]}):x(e)}(await t.call({to:e,data:n}))}catch(t){r=t}try{return await async function(t,e){const n=await q(t,e,M.encodeFunctionData("views",[])),[r]=M.decodeFunctionResult("views",n),[o,i,s,a,u,c,d,l]=await Promise.all([q(t,e,M.encodeFunctionData("paused",[])),q(t,e,M.encodeFunctionData("liquidationPaused",[])),q(t,e,M.encodeFunctionData("simplePsm",[])),q(t,r,$.encodeFunctionData("core",[])),q(t,r,$.encodeFunctionData("loanOps",[])),q(t,r,$.encodeFunctionData("collateralManager",[])),q(t,r,$.encodeFunctionData("liquidationManager",[])),q(t,r,$.encodeFunctionData("circuitBreaker",[]))]),g=M.decodeFunctionResult("paused",o)[0],h=M.decodeFunctionResult("liquidationPaused",i)[0],p=M.decodeFunctionResult("simplePsm",s)[0],f=$.decodeFunctionResult("core",a)[0],w=$.decodeFunctionResult("loanOps",u)[0],m=$.decodeFunctionResult("collateralManager",c)[0],y=$.decodeFunctionResult("liquidationManager",d)[0],P=$.decodeFunctionResult("circuitBreaker",l)[0],b=await q(t,w,E.encodeFunctionData("getUcdController",[])),v=await q(t,w,E.encodeFunctionData("ucdToken",[])),A=E.decodeFunctionResult("getUcdController",b)[0],T=E.decodeFunctionResult("ucdToken",v)[0],I=async e=>{if(!e||e===ethers.constants.AddressZero)return!1;try{const n=await q(t,e,S.encodeFunctionData("paused",[]));return S.decodeFunctionResult("paused",n)[0]}catch{return!1}},[k,C,B,O,L,D,z,F]=await Promise.all([I(f),I(w),I(m),I(y),I(P),I(A),I(T),I(p)]);return x({positionManagerPaused:g,positionManagerLiquidationsPaused:h,positionManagerCorePaused:k,loanOperationsPaused:C,collateralManagerPaused:B,liquidationManagerPaused:O,circuitBreakerPaused:L,ucdControllerPaused:D,ucdTokenPaused:z,simplePsmPaused:F})}(t,e)}catch(t){const e=r instanceof Error?r.message:String(r),n=t instanceof Error?t.message:String(t);throw new Error(`getProtocolPauseStatus failed (aggregate: ${e}; decomposed: ${n})`)}}async function main(t){for(const[e,n]of Object.entries(t))globalThis[e]=n;const e=T();console.log("[Step 0] Validating configuration...");const n=e.chain,r=e.bitcoinNetwork;if(!n)throw new Error('Missing required parameter: "chain". Must be "sepolia" or "ethereum"');if(!r)throw new Error('Missing required parameter: "bitcoinNetwork". Must be "mainnet", "testnet", or "regtest"');if(!["mainnet","testnet","regtest"].includes(r))throw new Error(`Invalid bitcoinNetwork: "${r}". Must be "mainnet", "testnet", or "regtest"`);const o=e.auth;if(!o||"object"!=typeof o)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!o.positionId)throw new Error("auth.positionId is required");if(!o.utxo||"object"!=typeof o.utxo)throw new Error("auth.utxo is required and must be an object");if(void 0===o.networkFee||o.networkFee<0)throw new Error("auth.networkFee is required and must be non-negative");if(!o.targetAddress||"string"!=typeof o.targetAddress)throw new Error("auth.targetAddress is required (string)");if(void 0===o.targetAmount||null===o.targetAmount)throw new Error("auth.targetAmount is required");if(!o.utxo.txid||void 0===o.utxo.vout||!o.utxo.value||!o.utxo.scriptPubKey)throw new Error("Invalid UTXO: must have txid, vout, value, scriptPubKey");const a=o.positionId,c=o.utxo,d=o.utxos&&o.utxos.length>0?o.utxos:[c],l=o.networkFee;console.log(" \u2705 Parameters validated"),console.log(` Position: ${a}`),console.log(` UTXOs to spend: ${d.length}`),console.log(` Network fee: ${l} sats`),console.log(` Bitcoin network: ${r}`);const h=e.publicKey;if(!h)throw new Error('Missing required parameter: "publicKey"');if("string"!=typeof h||0===h.length)throw new Error("Invalid publicKey: must be non-empty string");const P=h;if(!e.contractAddresses||!e.contractAddresses.LoanOperationsManagerModule)throw new Error("contractAddresses.LoanOperationsManagerModule is required");if(!e.contractAddresses.PositionManager)throw new Error("contractAddresses.PositionManager is required");const b=e.contractAddresses.LoanOperationsManagerModule,v=e.contractAddresses.PositionManager,I=e.contractAddresses.BTCSpendAuthorizer??b;console.log(" \u2705 Contract addresses loaded from parameters");let x="0.5";try{console.log("[BTC Transaction Signer] Started"),console.log(` Position: ${a}`),console.log(` UTXO: ${c.txid}:${c.vout} (${c.value} sats)`),x="1",console.log("[Step 1] Setting up RPC connection...");const t=e.rpcUrl;let h;t&&"string"==typeof t?(console.log(` \u2705 Using RPC URL override: ${function(t){if(null==t||"string"!=typeof t)return"[missing]";let e=t.replace(/([?&])(dkey|apikey|api_key|key|token|secret|password|auth|authorization)=[^&]*/gi,"$1$2=[REDACTED]");try{const t=new URL(e);(t.username||t.password)&&(t.username="",t.password="",e=t.toString())}catch{}return e}(t)}`),h=t):(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"),h=await Lit.Actions.getRpcUrl({chain:n}),console.log(` Using LIT-provided RPC URL for chain: ${n}`));const b=new ethers.providers.JsonRpcProvider(h);!function(t,e){const n=k[t],r=[];for(const t of n)e[t]&&r.push(t);if(r.length>0)throw new Error(JSON.stringify({code:"PROTOCOL_PAUSED",operation:t,paused:r,status:e}))}("btcTransactionSign",await B(b,v)),x="2",console.log("[Step 2] UTXO existence check..."),console.log(` UTXO: ${c.txid}:${c.vout}`),console.log(" \u26a0\ufe0f UTXO existence check deferred to Bitcoin consensus"),console.log(" \u2705 Proceeding (Bitcoin will reject if spent)"),x="3",console.log("[Step 3] Querying authorization from contract...");const T=[{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"}],C=new ethers.Contract(I,T,b),M=await C.getAuthorizedSpends(a);let $=null;for(const t of M){const e=t.txid.toLowerCase(),n=c.txid.toLowerCase(),r=Number(t.vout),o=Number(c.vout);if(e===n&&r===o){$=t;break}}if(!$)throw new Error(`No authorization found for UTXO ${c.txid}:${c.vout}`);const E=BigInt($.targetAmount.toString()),S=$.targetAddress,q=BigInt($.authorizedAt.toString());console.log(" \u2705 Authorization found on-chain"),console.log(` Target amount: ${E.toString()} sats`),console.log(` Destination: ${S}`),console.log(` Authorized at: ${q.toString()}`),x="3.5",console.log("[Step 3.5] Verifying owner signature for network fee...");const O=[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],L=new ethers.Contract(v,O,b),D=await L.core();console.log(` Core facet address: ${D}`);const z=[{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"}],F=new ethers.Contract(D,z,b),U=await F.getPositionDetails(a);let K;if(5===U.status){if(!e.contractAddresses?.LITActionValidator)throw new Error("LITActionValidator address required for liquidated position BTC signing");const t=new ethers.Contract(e.contractAddresses.LITActionValidator,["function pkpOwners(bytes32) view returns (address)"],b),n=await t.pkpOwners(U.pkpId);if(!n||n===ethers.constants.AddressZero)throw new Error("No PKP owner registered for liquidated position \u2014 cannot authorize BTC signing");K=n,console.log(` \u2139\ufe0f LIQUIDATED position \u2014 verifying against PKP owner: ${K}`)}else K=U.borrower;console.log(` Position owner: ${K}`);const R=`${c.txid}:${c.vout}`;if(!await A.verifyBtcExecutionAuthorization({positionId:o.positionId,timestamp:o.timestamp,chainId:o.chainId,utxoIdentifier:R,networkFee:o.networkFee,targetAddress:o.targetAddress,targetAmount:o.targetAmount,action:"execute-btc-withdrawal",signature:o.signature},K,b,globalThis.contractAddresses?.PositionDelegateRegistry))throw new Error("Caller not authorized for execution");console.log(" \u2705 Owner signature verified");const j=String(S);if(o.targetAddress.toLowerCase()!==j.toLowerCase())throw new Error(`targetAddress mismatch \u2014 user signed "${o.targetAddress}" but on-chain authorizer records "${j}"`);const N=BigInt(o.targetAmount.toString());if(N!==E)throw new Error(`targetAmount mismatch \u2014 user signed ${N.toString()} but on-chain authorizer records ${E.toString()}`);if(x="4",console.log("[Step 4] Validating network fee..."),l>y)throw new Error(`Network fee ${l} sats exceeds maximum ${y} sats`);if(l<m)throw new Error(`Network fee ${l} sats below minimum ${m} sats`);if(BigInt(l)>=E)throw new Error(`Network fee ${l} sats cannot exceed target amount ${E.toString()} sats`);const _=Number(E)-l;console.log(" \u2705 Network fee validated"),console.log(` User receives: ${_} sats`),x="5",console.log("[Step 5] Deriving PKP Bitcoin address...");const X=await s(P,r);console.log(` \u2705 PKP BTC address: ${X}`),x="6",console.log("[Step 6] Validating UTXO ownership...");const{addressToScriptPubKey:Q}=await Promise.resolve().then((()=>(g(),i))),V=Q(X);for(const t of d){const e=t.scriptPubKey.startsWith("0x")?t.scriptPubKey.slice(2):t.scriptPubKey;if(e!==V)throw new Error(`UTXO ${t.txid}:${t.vout} does not belong to vault. Expected scriptPubKey ${V}, got ${e}`)}console.log(` \u2705 All ${d.length} UTXO(s) belong to vault (PKP address)`),x="7",console.log("[Step 7] Validating UTXO value...");const W=Number(E),H=d.reduce(((t,e)=>t+e.value),0);if(H<W)throw new Error(`Insufficient UTXO value: total ${H} sats across ${d.length} input(s), need ${W} sats`);const Z=H-W;console.log(` \u2705 Total UTXO value sufficient (${d.length} input(s))`),console.log(` Total value: ${H} sats`),console.log(` Total required: ${W} sats`),console.log(` Change: ${Z} sats`),x="8",console.log("[Step 8] Building Bitcoin transaction...");const{txHex:J,sigHashes:G}=function(t){const{utxos:e,destination:n,amount:r,changeAddress:o,changeAmount:i}=t;if(0===e.length)throw new Error("buildBitcoinTransactionMultiInput: at least one UTXO required");const s="02000000",a="ffffffff",c="00000000",d=w(e.length),l=e.map((t=>p(t.txid)+f(t.vout,4)+"00"+a)).join(""),g=[],h=u(n);if(g.push(f(r,8)+w(h.length/2)+h),i>0){const t=u(o);g.push(f(i,8)+w(t.length/2)+t)}const m=w(g.length),y=g.join(""),P=s+d+l+m+y+c,b=e.map((t=>p(t.txid)+f(t.vout,4))).join(""),v=ethers.utils.sha256(ethers.utils.sha256("0x"+b)),A=e.map((()=>a)).join(""),T=ethers.utils.sha256(ethers.utils.sha256("0x"+A)),I=ethers.utils.sha256(ethers.utils.sha256("0x"+y));return{txHex:P,sigHashes:e.map((t=>{const e=p(t.txid)+f(t.vout,4),n="1976a914"+(t.scriptPubKey.startsWith("0x")?t.scriptPubKey.slice(2):t.scriptPubKey).slice(4)+"88ac",r=s+v.slice(2)+T.slice(2)+e+n+f(t.value,8)+a+I.slice(2)+c+"01000000";return ethers.utils.sha256(ethers.utils.sha256("0x"+r))}))}}({utxos:d,destination:S,amount:_,changeAddress:X,changeAmount:Z,network:r,pkpPublicKey:P});console.log(` \u2705 Transaction built (${d.length} input(s))`),console.log(` Signature hashes: ${G.length}`),x="9",console.log(`[Step 9] Signing ${G.length} input(s)...`);const Y=globalThis.pkpId;if(!Y)throw new Error("Missing required parameter: pkpId (user PKP ID for BTC signing)");const tt=await Lit.Actions.getPrivateKey({pkpId:Y}),et="string"==typeof tt?tt.startsWith("0x")?tt:`0x${tt}`:String(tt),nt=new ethers.utils.SigningKey(et),rt=[];for(let t=0;t<G.length;t++){const e=nt.signDigest(G[t]);rt.push(ethers.utils.joinSignature(e)),console.log(` \u2705 Signed input ${t}: ${G[t]}`)}return console.log("[BTC Transaction Signer] \u2705 Complete"),{success:!0,unsignedTxHex:J,sigHashes:G,sigHash:G[0],signatures:rt,signature:rt[0],pkpPublicKey:P,pkpBtcAddress:X,destination:S,userReceivesAmount:_,networkFee:l,changeAmount:Z,utxos:d.map((t=>({txid:t.txid,vout:t.vout,value:t.value}))),utxo:{txid:c.txid,vout:c.vout,value:c.value},authorization:{targetAmount:E.toString(),authorizedAt:q.toString()},timestamp:Date.now(),action:"btc-transaction-signer"}}catch(t){return console.error("[BTC Transaction Signer] \u274c Failed:",t.message),console.error(`[BTC Transaction Signer] Failed at step: ${x}`),{success:!1,error:t.message||t.toString(),positionId:o?.positionId,timestamp:Date.now()}}}return C=h,((o,i,s,a)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let u of n(i))r.call(o,u)||u===s||t(o,u,{get:()=>i[u],enumerable:!(a=e(i,u))||a.enumerable});return o})(t({},"__esModule",{value:!0}),C)})();if("object"==typeof _LIT_ACTION_)if("function"==typeof _LIT_ACTION_.main)var main=_LIT_ACTION_.main;else"function"==typeof _LIT_ACTION_.go&&_LIT_ACTION_.go();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
a16984eb42e6eb439565d6fad0fe7ad25390285c27824f2c9602e3ac9e7cbce9
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"actionName": "btc-transaction-signer",
|
|
3
|
-
"originalSize":
|
|
4
|
-
"minifiedSize":
|
|
5
|
-
"compressionRatio": 0.
|
|
6
|
-
"hash": "
|
|
7
|
-
"buildTime":
|
|
3
|
+
"originalSize": 63214,
|
|
4
|
+
"minifiedSize": 27284,
|
|
5
|
+
"compressionRatio": 0.5683867497706204,
|
|
6
|
+
"hash": "a16984eb42e6eb439565d6fad0fe7ad25390285c27824f2c9602e3ac9e7cbce9",
|
|
7
|
+
"buildTime": 1778598000464,
|
|
8
8
|
"version": "0.1.0"
|
|
9
9
|
}
|