@gvnrdao/dh-lit-actions 0.0.111 → 0.0.192

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/admin-liquidation-validator.action.js +1 -0
  2. package/dist/admin-liquidation-validator.hash +1 -0
  3. package/dist/admin-liquidation-validator.meta.json +9 -0
  4. package/dist/always-signer.action.js +1 -0
  5. package/dist/always-signer.hash +1 -0
  6. package/dist/always-signer.meta.json +9 -0
  7. package/dist/authorization-dummy-b.action.js +1 -0
  8. package/dist/authorization-dummy-b.hash +1 -0
  9. package/dist/authorization-dummy-b.meta.json +9 -0
  10. package/dist/authorization-dummy.action.js +1 -0
  11. package/dist/authorization-dummy.hash +1 -0
  12. package/dist/authorization-dummy.meta.json +9 -0
  13. package/dist/btc-transaction-signer.action.js +1 -0
  14. package/dist/btc-transaction-signer.hash +1 -0
  15. package/dist/btc-transaction-signer.meta.json +9 -0
  16. package/dist/btc-withdrawal.action.js +1 -0
  17. package/dist/btc-withdrawal.hash +1 -0
  18. package/dist/btc-withdrawal.meta.json +9 -0
  19. package/dist/extend-position-validator.action.js +1 -0
  20. package/dist/extend-position-validator.hash +1 -0
  21. package/dist/extend-position-validator.meta.json +9 -0
  22. package/dist/liquidation-validator.action.js +1 -0
  23. package/dist/liquidation-validator.hash +1 -0
  24. package/dist/liquidation-validator.meta.json +9 -0
  25. package/dist/loan-vault-btc-balance.action.js +1 -0
  26. package/dist/loan-vault-btc-balance.hash +1 -0
  27. package/dist/loan-vault-btc-balance.meta.json +9 -0
  28. package/dist/pkp-validator-datil.action.js +1 -0
  29. package/dist/pkp-validator-datil.hash +1 -0
  30. package/dist/pkp-validator-datil.meta.json +9 -0
  31. package/dist/price-oracle.action.js +1 -0
  32. package/dist/price-oracle.hash +1 -0
  33. package/dist/price-oracle.meta.json +9 -0
  34. package/dist/process-payment-sign-only.action.js +1 -0
  35. package/dist/process-payment-sign-only.hash +1 -0
  36. package/dist/process-payment-sign-only.meta.json +9 -0
  37. package/dist/process-payment-validator.action.js +1 -0
  38. package/dist/process-payment-validator.hash +1 -0
  39. package/dist/process-payment-validator.meta.json +9 -0
  40. package/dist/ucd-mint-validator.action.js +1 -0
  41. package/dist/ucd-mint-validator.hash +1 -0
  42. package/dist/ucd-mint-validator.meta.json +9 -0
  43. package/package.json +4 -3
  44. package/pkg-dist/pkg-src/constants/chunks/lit-actions-registry.d.ts.map +1 -1
  45. package/pkg-dist/pkg-src/constants/chunks/lit-actions-registry.js +127 -108
  46. package/pkg-dist/pkg-src/constants/chunks/lit-actions-registry.js.map +1 -1
  47. package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.d.ts +47 -0
  48. package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.d.ts.map +1 -0
  49. package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.js +77 -0
  50. package/pkg-dist/pkg-src/executors/chunks/price-oracle-executor.js.map +1 -0
  51. package/pkg-dist/pkg-src/executors/index.d.ts +39 -0
  52. package/pkg-dist/pkg-src/executors/index.d.ts.map +1 -1
  53. package/pkg-dist/pkg-src/executors/index.js +41 -1
  54. package/pkg-dist/pkg-src/executors/index.js.map +1 -1
  55. package/pkg-dist/pkg-src/utils/chunks/cid-utils.d.ts.map +1 -1
  56. package/pkg-dist/pkg-src/utils/chunks/cid-utils.js +0 -5
  57. package/pkg-dist/pkg-src/utils/chunks/cid-utils.js.map +1 -1
  58. package/pkg-dist/src/constants/chunks/quantum-time.d.ts +5 -5
  59. package/pkg-dist/src/constants/chunks/quantum-time.js +5 -5
  60. package/pkg-dist/src/interfaces/chunks/vault-balance.i.d.ts +0 -1
  61. package/pkg-dist/src/interfaces/chunks/vault-balance.i.d.ts.map +1 -1
  62. package/pkg-dist/src/modules/bitcoin-data-provider.module.d.ts +7 -6
  63. package/pkg-dist/src/modules/bitcoin-data-provider.module.d.ts.map +1 -1
  64. package/pkg-dist/src/modules/bitcoin-data-provider.module.js +93 -97
  65. package/pkg-dist/src/modules/bitcoin-data-provider.module.js.map +1 -1
  66. package/pkg-dist/src/modules/business-rules-math.module.d.ts +2 -0
  67. package/pkg-dist/src/modules/business-rules-math.module.d.ts.map +1 -1
  68. package/pkg-dist/src/modules/business-rules-math.module.js +9 -2
  69. package/pkg-dist/src/modules/business-rules-math.module.js.map +1 -1
  70. package/pkg-dist/src/modules/lit-action-logger.module.d.ts +55 -0
  71. package/pkg-dist/src/modules/lit-action-logger.module.d.ts.map +1 -0
  72. package/pkg-dist/src/modules/lit-action-logger.module.js +184 -0
  73. package/pkg-dist/src/modules/lit-action-logger.module.js.map +1 -0
  74. package/pkg-dist/src/modules/price-oracle.module.d.ts +41 -3
  75. package/pkg-dist/src/modules/price-oracle.module.d.ts.map +1 -1
  76. package/pkg-dist/src/modules/price-oracle.module.js +265 -122
  77. package/pkg-dist/src/modules/price-oracle.module.js.map +1 -1
  78. package/pkg-dist/src/modules/protocol-parameters.module.d.ts +10 -0
  79. package/pkg-dist/src/modules/protocol-parameters.module.d.ts.map +1 -1
  80. package/pkg-dist/src/modules/protocol-parameters.module.js +49 -2
  81. package/pkg-dist/src/modules/protocol-parameters.module.js.map +1 -1
  82. package/pkg-dist/src/modules/quantum-time.module.d.ts +1 -1
  83. package/pkg-dist/src/modules/quantum-time.module.d.ts.map +1 -1
  84. package/pkg-dist/src/modules/quantum-time.module.js +18 -9
  85. package/pkg-dist/src/modules/quantum-time.module.js.map +1 -1
  86. package/pkg-dist/src/modules/vault-balance.module.d.ts +37 -3
  87. package/pkg-dist/src/modules/vault-balance.module.d.ts.map +1 -1
  88. package/pkg-dist/src/modules/vault-balance.module.js +116 -63
  89. package/pkg-dist/src/modules/vault-balance.module.js.map +1 -1
  90. package/pkg-dist/src/modules/vault-snapshot.d.ts +5 -3
  91. package/pkg-dist/src/modules/vault-snapshot.d.ts.map +1 -1
  92. package/pkg-dist/src/modules/vault-snapshot.js +9 -6
  93. package/pkg-dist/src/modules/vault-snapshot.js.map +1 -1
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{var t=1000000000000000000n,e=100;function r(t){switch(t){case 0:return"PENDING_DEPOSIT";case 1:return"PENDING_MINT";case 2:return"ACTIVE";case 3:return"EXPIRED";case 4:return"LIQUIDATABLE";case 5:return"LIQUIDATED";case 6:return"REPAID";case 7:return"CLOSED";default:return`UNKNOWN(${t})`}}var i=(t=>(t.SEPOLIA="sepolia",t.ETHEREUM="ethereum",t.HARDHAT="hardhat",t))(i||{}),o={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},a={ethereum:1,sepolia:11155111,hardhat:31337};function s(t){const e=t.toLowerCase().trim();if("sepolia"===e)return"sepolia";if("ethereum"===e||"mainnet"===e)return"ethereum";if("hardhat"===e)return"hardhat";throw new Error(`Unsupported EVM chain: "${t}". Supported chains: ${Object.values(i).join(", ")}`)}function n(t){return a[t]}var c=class{stepStart(t,e){}stepEnd(t,e){}log(t,e){}warn(t,e){}error(t,e){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},l=class{constructor(t){this.stepStartTimes=new Map,this.stepDurations=new Map,this.TIMEOUT_MS=3e4,this.actionName=t,this.executionStartTime=Date.now();const e=Date.now(),r=Math.random().toString(36).substring(2,9);this.executionId=`exec_${e}_${r}`,this.logHeader()}logHeader(){console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution started`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`);void 0!==globalThis.litNodeContext?(console.log(`[${this.actionName}] Context: LIT Node`),console.log(`[${this.actionName}] Node: ${globalThis.litNodeContext?.nodeAddress||"unknown"}`)):console.log(`[${this.actionName}] Context: Browser/Test`),console.log(`[${this.actionName}] ========================================`)}stepStart(t,e){this.stepStartTimes.set(t,Date.now());const r=Date.now()-this.executionStartTime,i=this.TIMEOUT_MS-r;console.log(`[Step ${t}] ${e}...`),console.log(`[Step ${t}] Elapsed: ${r}ms | Remaining: ${i}ms`)}stepEnd(t,e=5e3){const r=this.stepStartTimes.get(t);if(!r)return console.warn(`[Step ${t}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const i=Date.now()-r;this.stepDurations.set(t,i),console.log(`[Step ${t}] Duration: ${i}ms`),i>e&&console.warn(`\u26a0\ufe0f [Step ${t}] Took ${i}ms (> ${e}ms threshold)`);const o=Date.now()-this.executionStartTime,a=this.TIMEOUT_MS-o;a<5e3&&console.warn(`\u26a0\ufe0f [Step ${t}] Time remaining: ${a}ms (approaching timeout)`)}log(t,e){console.log(`[Step ${t}] ${e}`)}warn(t,e){console.warn(`\u26a0\ufe0f [Step ${t}] ${e}`)}error(t,e){console.error(`\u274c [Step ${t}] ${e}`)}getElapsed(){return Date.now()-this.executionStartTime}getRemaining(){return this.TIMEOUT_MS-this.getElapsed()}summary(){const t=Date.now()-this.executionStartTime,e=this.TIMEOUT_MS-t;if(console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution Summary`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`),console.log(`[${this.actionName}] Total time: ${(t/1e3).toFixed(2)}s`),console.log(`[${this.actionName}] Time remaining: ${e}ms`),this.stepDurations.size>0){console.log(`[${this.actionName}] Step breakdown:`);for(const[e,r]of this.stepDurations.entries()){const i=(r/t*100).toFixed(1);console.log(`[${this.actionName}] Step ${e}: ${r}ms (${i}%)`)}}t>2e4&&console.warn(`\u26a0\ufe0f [${this.actionName}] Slow execution: ${(t/1e3).toFixed(2)}s (${e}ms remaining)`),e<5e3&&console.warn(`\u26a0\ufe0f [${this.actionName}] CRITICAL: Only ${e}ms remaining before timeout!`),console.log(`[${this.actionName}] ========================================`)}getExecutionId(){return this.executionId}isDebugEnabled(){return!0}},u=class{static create(t="LIT Action",e){return e??globalThis.debugAction??!1?new l(t):new c}},d=class{constructor(t){this.config=t,this.timeout=t.timeout||15e3,this.logger=u.create("BitcoinDataProvider")}async getCurrentBlockHeight(){if(this.logger.stepStart("0","getCurrentBlockHeight"),this.config.rpcHelper){const t=await this.config.rpcHelper.getBlockCount();return this.logger.stepEnd("0"),t}const t=new AbortController,e=setTimeout((()=>t.abort()),this.timeout);try{const r=await fetch(`${this.config.providerUrl}/blocks/tip/height`,{signal:t.signal,headers:{Accept:"text/plain","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(e),!r.ok)throw new Error(`Failed to fetch block height: ${r.status} ${r.statusText}`);const i=await r.text(),o=parseInt(i.trim(),10);if(isNaN(o))throw new Error(`Invalid block height response: ${i}`);return this.logger.stepEnd("0"),o}catch(t){if(clearTimeout(e),this.logger.stepEnd("0"),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOs(t){this.logger.log("0",`Original address: "${t}"`);const e=this.stripNetworkPrefix(t);if(this.logger.log("0",`Cleaned address: "${e}"`),this.config.rpcHelper)return await this.fetchUTXOsFromRPC(e);try{return await this.fetchUTXOsFromProvider(this.config.providerUrl,e)}catch(t){if(this.logger.warn("0",`Primary provider failed: ${t.message}`),this.config.fallbackProviders&&this.config.fallbackProviders.length>0){const t=[...this.config.fallbackProviders].sort(((t,e)=>t.priority-e.priority));for(const r of t)try{return this.logger.log("0",`Trying fallback: ${r.name} (${r.url})`),await this.fetchUTXOsFromProvider(r.url,e)}catch(t){this.logger.warn("0",`Fallback ${r.name} failed: ${t.message}`);continue}}throw new Error(`Failed to fetch UTXOs: ${t.message}`)}}stripNetworkPrefix(t){return t.replace(/^(REGTEST_|TESTNET_|MAINNET_)/,"")}async fetchUTXOsFromRPC(t){if(!this.config.rpcHelper)throw new Error("RPC helper not configured");const e=this.config.rpcWallet||"";return(await this.config.rpcHelper.listUnspent(e,t)).map((t=>({txid:t.txid,vout:t.vout,satoshis:BigInt(Math.round(1e8*t.amount)),confirmations:t.confirmations})))}parseBlockstreamUTXOs(t,e){if(!Array.isArray(t))throw new Error("Invalid Blockstream response format: expected an array");return t.map((t=>{let r=0;return t.status&&"object"==typeof t.status?t.status.confirmed&&(r=void 0!==t.status.block_height&&t.status.block_height>0?e-t.status.block_height+1:1):void 0!==t.confirmations&&(r=t.confirmations),{txid:t.txid,vout:void 0!==t.vout?t.vout:t.n,satoshis:BigInt(void 0!==t.value?t.value:t.satoshis),confirmations:r}}))}async fetchUTXOsFromProvider(t,e){this.logger.stepStart("1","fetchUTXOsFromProvider");const r=`${t}/address/${e}/utxos`;this.logger.log("1",`Fetching UTXOs from ${r}`);const i=await this.getCurrentBlockHeight();this.logger.log("1",`Current block height: ${i}`);const o=new AbortController,a=setTimeout((()=>o.abort()),this.timeout);try{const t=await fetch(r,{signal:o.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(a),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();this.logger.log("1",`Raw API Response: ${JSON.stringify(e,null,2)}`);const s=this.parseBlockstreamUTXOs(e,i);return this.logger.stepEnd("1"),s}catch(t){if(clearTimeout(a),this.logger.stepEnd("1"),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOSet(t,e){let r=[],i=null;for(let e=1;e<=3;e++)try{this.logger.log("2",`UTXO query attempt ${e}/3...`),r=await this.getUTXOs(t),this.logger.log("2",`UTXO query succeeded on attempt ${e}`),i=null;break}catch(t){i=t,this.logger.error("2",`UTXO query failed on attempt ${e}: ${t.message}`),e<3&&(this.logger.log("2","Waiting 500ms before retry..."),await new Promise((t=>setTimeout(t,500))))}if(i)throw new Error(`Failed to query UTXOs after 3 attempts: ${i.message}`);const o=r.reduce(((t,e)=>t+e.satoshis),0n),a=r.filter((t=>t.confirmations>=e)).reduce(((t,e)=>t+e.satoshis),0n),s=o-a;return{utxos:r,totalBalance:o,totalUTXOs:r.length,confirmedBalance:a,unconfirmedBalance:s}}async getBalance(t){const e=this.stripNetworkPrefix(t),r=`${this.config.providerUrl}/address/${e}`;this.logger.log("3",`Fetching balance from ${r}`);const i=new AbortController,o=setTimeout((()=>i.abort()),this.timeout);try{const t=await fetch(r,{signal:i.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(o),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();if(!e.chain_stats)throw new Error("Invalid response: missing chain_stats");const a=e.chain_stats.funded_txo_sum,s=e.chain_stats.spent_txo_sum;if(void 0===a||void 0===s)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${a}, spent_txo_sum: ${s}`);const n=BigInt(a-s);return this.logger.log("3",`Balance: ${n.toString()} sats`),this.logger.log("3",` - Funded: ${a} sats, Spent: ${s} sats`),n}catch(t){if(clearTimeout(o),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);const r=t.message||"Unknown error";throw this.logger.log("3",`Balance fetch failed: ${r}`),new Error(`Failed to fetch Bitcoin balance for address ${e}: ${r}`)}}async getTransaction(t){if(this.config.rpcHelper)try{const e=this.config.rpcWallet||"",r=await this.config.rpcHelper.getTransaction(e,t);return{txid:r.txid,confirmations:r.confirmations||0}}catch(t){if(t.message?.includes("Invalid or non-wallet transaction")||-5===t.code)return null;throw t}const e=new AbortController,r=setTimeout((()=>e.abort()),this.timeout);try{const i=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(r),!i.ok){if(404===i.status)return null;throw new Error(`Bitcoin provider error: ${i.status} ${i.statusText}`)}const o=await i.json();return o.status?{txid:o.txid,confirmations:o.status.confirmed&&o.status.block_height?1:0}:null}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);if(t.message?.includes("404"))return null;throw t}}};function h(t,e){if(0===t)return{termDurationDays:0,termLengthDays:30*e,isExpired:!1,daysUntilExpiry:30*e,daysIntoGracePeriod:0};const r=30*e,i=Math.floor(Date.now()/1e3)-t,o=Math.floor(i/86400);return{termDurationDays:o,termLengthDays:r,isExpired:o>r,daysUntilExpiry:Math.max(0,r-o),daysIntoGracePeriod:Math.max(0,o-r)}}function p(t,e){if(!t)return 13e3;const r=Math.min(e,30);return 11e3+9e3*(r*r)/900}function g(e,r,i){const o=e*r/10000000000000000n;if(0n===i)return{collateralValueUsd:o,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:o,collateralRatioBps:Number(o*t*10000n/i)}}function m(t){return t/100}var f=class{constructor(t,e,r){this.mode=r,e&&Array.isArray(e)?this.sources=e:t&&Array.isArray(t)?this.sources=this.buildSources(t):this.sources=this.buildSources(),this.sources.sort(((t,e)=>t.priority-e.priority)),this.validateProviderCount()}validateProviderCount(){if("prod"===this.mode&&this.sources.length<3)throw new Error(`Production mode requires at least 3 price providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported providers: CoinGecko, Binance, Coinbase, CryptoCompare (with API key)`);if(0===this.sources.length)throw new Error("No price providers configured. At least one provider is required.")}buildSources(t){const e=[];if(!t||0===t.length)return this.getDefaultSources();let r=1;for(const i of t){switch(i.name.toLowerCase()){case"coingecko":e.push(this.createCoinGeckoSource(i.apiKey,r++));break;case"binance":e.push(this.createBinanceSource(i.apiKey,i.apiSecret,r++));break;case"coinbase":e.push(this.createCoinbaseSource(i.apiKey,r++));break;case"cryptocompare":if(!i.apiKey)throw new Error("CryptoCompare requires an API key. Please provide apiKey in priceProviders config.");e.push(this.createCryptoCompareSource(i.apiKey,r++));break;default:console.warn(`[Price Oracle] Unknown provider: ${i.name} - skipping`)}}return e}createCryptoCompareSource(t,e){return{name:"CryptoCompare",fetchPrice:async()=>{const e=new URL("https://min-api.cryptocompare.com/data/price");e.searchParams.set("fsym","BTC"),e.searchParams.set("tsyms","USDT"),e.searchParams.set("api_key",t);const r=await(async t=>{const e=new AbortController,r=setTimeout((()=>e.abort()),5e3);try{const i=await fetch(t,{signal:e.signal,headers:{Accept:"application/json"}});if(clearTimeout(r),!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error("Request timeout after 5000ms");throw t}})(e.toString()),i=Number(r?.USDT);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CryptoCompare price payload");const o=(await Lit.Actions.broadcastAndCollect({name:"cryptoComparePrice",value:i.toString()})).map((t=>parseFloat(t)));return o.sort(((t,e)=>t-e)),o[Math.floor(o.length/2)]},priority:e}}createFetchJson(){return async(t,e)=>{const r=new AbortController,i=setTimeout((()=>r.abort()),5e3);try{const i=await fetch(t,{headers:{Accept:"application/json",...e||{}},signal:r.signal});if(!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}finally{clearTimeout(i)}}}getDefaultSources(){return[this.createCoinGeckoSource(void 0,1),this.createBinanceSource(void 0,void 0,2),this.createCoinbaseSource(void 0,3)]}createCoinGeckoSource(t,e=1){const r=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const t=new URL("https://api.coingecko.com/api/v3/simple/price");t.searchParams.set("ids","bitcoin"),t.searchParams.set("vs_currencies","usd");const e=await r(t.toString()),i=Number(e?.bitcoin?.usd);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CoinGecko price payload");const o=(await Lit.Actions.broadcastAndCollect({name:"coinGeckoPrice",value:i.toString()})).map((t=>parseFloat(t)));return o.sort(((t,e)=>t-e)),o[Math.floor(o.length/2)]},priority:e}}createBinanceSource(t,e,r=2){const i=this.createFetchJson();return{name:"Binance",fetchPrice:async()=>{const e=new URL("https://api.binance.com/api/v3/ticker/price");e.searchParams.set("symbol","BTCUSDT");const r=t?{"X-MBX-APIKEY":t}:void 0,o=await i(e.toString(),r),a=Number(o?.price);if(!Number.isFinite(a)||a<=0)throw new Error("Invalid Binance price payload");const s=(await Lit.Actions.broadcastAndCollect({name:"binancePrice",value:a.toString()})).map((t=>parseFloat(t)));return s.sort(((t,e)=>t-e)),s[Math.floor(s.length/2)]},priority:r}}createCoinbaseSource(t,e=3){const r=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const e=t?{"CB-ACCESS-KEY":t}:void 0,i=await r("https://api.coinbase.com/v2/prices/BTC-USD/spot",e),o=Number(i?.data?.amount);if(!Number.isFinite(o)||o<=0)throw new Error("Invalid Coinbase price payload");const a=(await Lit.Actions.broadcastAndCollect({name:"coinbasePrice",value:o.toString()})).map((t=>parseFloat(t)));return a.sort(((t,e)=>t-e)),a[Math.floor(a.length/2)]},priority:e}}async getBTCPrice(){const t=Date.now();console.log("[Price Oracle] Fetching BTC price from external sources (parallel)..."),console.log(`[Price Oracle] Start time: ${t}`);const e=new Promise(((e,r)=>{setTimeout((()=>{const e=Date.now()-t;console.error(`[Price Oracle] \u274c GLOBAL TIMEOUT after ${e}ms`),r(new Error("Price oracle global timeout after 15000ms"))}),15e3)})),r=this.sources.map((async t=>{const e=Date.now();try{console.log(`[Price Oracle] [${e}] Querying ${t.name}...`);const r=await t.fetchPrice(),i=Date.now()-e;return console.log(`[Price Oracle] \u2705 [+${i}ms] ${t.name}: $${r.toLocaleString()}`),{source:t.name,priceUSD:r}}catch(r){const i=Date.now()-e;throw console.warn(`[Price Oracle] \u26a0\ufe0f [+${i}ms] ${t.name} failed: ${r.message}`),r}}));try{console.log("[Price Oracle] Waiting for first success (race pattern)...");const i=await Promise.race([Promise.race(r.map((t=>t.catch((t=>({error:t})))))),e]);if("error"in i){console.log("[Price Oracle] First result was error, waiting for any success...");const e=Date.now(),i=new Promise(((t,r)=>{setTimeout((()=>{const t=Date.now()-e;console.error(`[Price Oracle] \u274c FALLBACK TIMEOUT after ${t}ms`),r(new Error("Price oracle fallback timeout after 10000ms"))}),1e4)})),o=await Promise.race([Promise.allSettled(r),i]),a=Date.now()-e;console.log(`[Price Oracle] Fallback completed in ${a}ms`);const s=o.find((t=>"fulfilled"===t.status));if(!s){const e=Date.now()-t;throw console.error(`[Price Oracle] All sources failed after ${e}ms`),new Error("All price sources failed")}const{source:n,priceUSD:c}=s.value,l=Date.now()-t;console.log(`[Price Oracle] Using ${n} (first successful after failures) - total time: ${l}ms`);const u=Math.round(100*c),d=1000000n*BigInt(u);return console.log(`[Price Oracle] Price with 8 decimals: ${d}`),d}const{source:o,priceUSD:a}=i,s=Date.now()-t;console.log(`[Price Oracle] Using ${o} (fastest response) - total time: ${s}ms`);const n=Math.round(100*a),c=1000000n*BigInt(n);return console.log(`[Price Oracle] Price with 8 decimals: ${c}`),c}catch(e){const r=Date.now()-t;throw console.error(`[Price Oracle] Failed after ${r}ms: ${e.message}`),new Error(`All price sources failed: ${e.message}`)}}async getBTCPriceConsensus(){console.log("[Price Oracle] Fetching BTC price with consensus...");const t=(await Promise.allSettled(this.sources.map((async t=>{const e=await t.fetchPrice();return{source:t.name,price:e}})))).filter((t=>"fulfilled"===t.status)).map((t=>t.value));if(0===t.length)throw new Error("No price sources returned data");console.log(`[Price Oracle] Got prices from ${t.length}/${this.sources.length} sources:`),t.forEach((t=>{console.log(` ${t.source}: $${t.price.toLocaleString()}`)}));const e=t.map((t=>t.price));e.sort(((t,e)=>t-e));const r=e[Math.floor(e.length/2)],i=e[0],o=e[e.length-1]/i,a=t.filter((t=>Math.abs(t.price-r)/r<=.02));if(o>1.05&&a.length===t.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(o-1)).toFixed(1)}% spread)`);if(a.length<t.length){const e=t.filter((t=>!a.find((e=>e.source===t.source))));if(console.log(`[Price Oracle] \u26a0\ufe0f Detected ${e.length} outlier(s):`),e.forEach((t=>{const e=Math.abs(t.price-r)/r;console.log(` ${t.source}: $${t.price.toLocaleString()} (${(100*e).toFixed(1)}% deviation)`)})),a.length<2)throw new Error("Price consensus failed: insufficient valid sources after outlier removal");console.log(`[Price Oracle] \u2705 Outliers filtered, continuing with ${a.length} valid sources`);const i=a.map((t=>t.price));i.sort(((t,e)=>t-e));const o=i[Math.floor(i.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${o.toLocaleString()}`);const s=Math.round(100*o);return 1000000n*BigInt(s)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${r.toLocaleString()}`);const s=Math.round(100*r);return 1000000n*BigInt(s)}};function w(){const t=Math.floor(Date.now()/1e3);return r=t,Math.floor(r/e)*e;var r}0,0;var y=class{constructor(t){this.config=t,this.bitcoinProvider=t.bitcoinProvider}async calculateBalance(t,e){const r=await this.bitcoinProvider.getBalance(e),i=await this.getAuthorizedSpendsFromContract(t),o=i.reduce(((t,e)=>t+e.satoshis),0n),a=r>o?r-o:0n;return{totalUTXOs:[],totalBalance:r,authorizedUTXOs:i,authorizedBalance:o,authorizedSpendsHash:this.computeAuthorizedSpendsHash(t,i),availableUTXOs:[],availableBalance:a,vaultAddress:e,positionId:t,timestamp:Date.now()}}async calculateTrustedBalance(t,e,r){if(r)return await this.calculateBalance(t,e);const i=this.config.minConfirmations||6,o=await this.bitcoinProvider.getUTXOSet(e,i),a=await this.getAuthorizedSpendsFromContract(t);for(const t of a){if(o.utxos.some((e=>e.txid===t.txid&&e.vout===t.vout)))throw new Error(`Authorized UTXO ${t.txid}:${t.vout} not yet spent. Transaction was authorized in smart contract but not signed and broadcasted to Bitcoin network. Complete the transaction by signing and broadcasting it.`)}const s=a.reduce(((t,e)=>t+e.satoshis),0n),n=o.utxos.filter((t=>!this.isUTXOAuthorized(t,a))),c=n.reduce(((t,e)=>t+e.satoshis),0n),l=this.computeAuthorizedSpendsHash(t,a);return{totalUTXOs:o.utxos,totalBalance:o.totalBalance,authorizedUTXOs:a,authorizedBalance:s,authorizedSpendsHash:l,availableUTXOs:n,availableBalance:c,vaultAddress:e,positionId:t,timestamp:Date.now()}}async getTrustedBalance(t,e){return(await this.calculateTrustedBalance(t,e)).availableBalance}async getAvailableUTXOs(t,e){return(await this.calculateTrustedBalance(t,e)).availableUTXOs}async isUTXOAvailable(t,e,r,i){return(await this.getAvailableUTXOs(t,e)).some((t=>t.txid===r&&t.vout===i))}isUTXOAuthorized(t,e){return e.some((e=>e.txid===t.txid&&e.vout===t.vout))}async getAuthorizedSpendsFromContract(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let r;r=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const i=new ethers.providers.StaticJsonRpcProvider(r,{name:"any",chainId:this.config.chainId}),o=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 o.getAuthorizedSpends(e)).map((e=>({txid:e.txid,vout:Number(e.vout),satoshis:BigInt(e.satoshis.toString()),positionId:t,targetAddress:e.targetAddress,targetAmount:BigInt(e.targetAmount.toString()),timestamp:Number(e.authorizedAt)})))}computeAuthorizedSpendsHash(t,e){const r=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;if(0===e.length)return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,[]]));const i=e.map((t=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.txid)),r=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.targetAddress));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","uint32","uint256","bytes32","uint256","uint256"],[e,t.vout,t.satoshis.toString(),r,t.targetAmount.toString(),t.timestamp]))}));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,i]))}};var T=class{constructor(t){this.termManagerAddress=t.termManagerAddress,this.loanOpsManagerAddress=t.loanOpsManagerAddress,this.chain=t.chain,this.chainId=t.chainId,this.rpcUrl=t.rpcUrl}async getLiquidationThreshold(){try{let t;t=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const e=new ethers.providers.StaticJsonRpcProvider(t,{name:"any",chainId:this.chainId}),r=[{inputs:[],name:"liquidationThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],i=new ethers.Contract(this.loanOpsManagerAddress,r,e),o=await i.liquidationThreshold();return Number(o.toString())}catch(t){throw console.error("[ProtocolParameters] Error fetching liquidation threshold:",t.message),new Error(`Failed to fetch liquidation threshold: ${t.message}`)}}async getTermFees(t){try{let e;e=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const r=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),i=[{inputs:[{internalType:"uint256",name:"_termMonths",type:"uint256"}],name:"getTermFees",outputs:[{internalType:"uint88",name:"originationFee",type:"uint88"},{internalType:"uint88",name:"extensionFee",type:"uint88"}],stateMutability:"view",type:"function"}],o=new ethers.Contract(this.termManagerAddress,i,r),a=await o.getTermFees(t);return{originationFeeBps:Number(a.originationFee?.toString?.()??a[0]?.toString?.()??a[0]),extensionFeeBps:Number(a.extensionFee?.toString?.()??a[1]?.toString?.()??a[1])}}catch(t){throw console.error("[ProtocolParameters] Error fetching term fees:",t.message),new Error(`Failed to fetch term fees: ${t.message}`)}}async getAuthorizedSpendsHash(t){try{const e=this.rpcUrl||await Lit.Actions.getRpcUrl({chain:this.chain}),r=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),i=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`,o=new ethers.Contract(this.loanOpsManagerAddress,[{inputs:[{name:"positionId",type:"bytes32"}],name:"getAuthorizedSpendsHash",outputs:[{name:"",type:"bytes32"}],stateMutability:"view",type:"function"}],r);return await o.getAuthorizedSpendsHash(i)}catch(t){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",t.message),new Error(`Failed to fetch authorized spends hash: ${t.message}`)}}};function b(t){return new T(t)}var S=class{constructor(t){this.config=t}async getVaultSnapshot(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const r=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress),i=await this.config.priceOracle.getBTCPrice(),o=b({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[a,s]=await Promise.all([o.getLiquidationThreshold(),o.getTermFees(e.selectedTerm)]),n=h(e.termStartTimestamp,e.selectedTerm),c=p(n.isExpired,n.daysIntoGracePeriod),l=g(r.availableBalance,i,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,totalBTCSats:r.totalBalance,totalUTXOs:r.totalUTXOs,authorizedSpendsSats:r.authorizedBalance,authorizedSpendsHash:r.authorizedSpendsHash,availableBTCSats:r.availableBalance,availableUTXOs:r.availableUTXOs,btcPriceUsd:i,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:n.termDurationDays,termLengthDays:n.termLengthDays,isExpired:n.isExpired,daysUntilExpiry:n.daysUntilExpiry,daysIntoGracePeriod:n.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:a,originationFeeBps:s.originationFeeBps,extensionFeeBps:s.extensionFeeBps,timestamp:Date.now()}}async getVaultSnapshotFast(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const r=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress,!0),i=await this.config.priceOracle.getBTCPrice(),o=b({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[a,s]=await Promise.all([o.getLiquidationThreshold(),o.getTermFees(e.selectedTerm)]),n=h(e.termStartTimestamp,e.selectedTerm),c=p(n.isExpired,n.daysIntoGracePeriod),l=g(r.availableBalance,i,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,totalBTCSats:r.totalBalance,totalUTXOs:r.totalUTXOs,authorizedSpendsSats:r.authorizedBalance,authorizedSpendsHash:r.authorizedSpendsHash,availableBTCSats:r.availableBalance,availableUTXOs:r.availableUTXOs,btcPriceUsd:i,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:n.termDurationDays,termLengthDays:n.termLengthDays,isExpired:n.isExpired,daysUntilExpiry:n.daysUntilExpiry,daysIntoGracePeriod:n.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:a,originationFeeBps:s.originationFeeBps,extensionFeeBps:s.extensionFeeBps,timestamp:Date.now()}}async queryPositionState(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let r;r=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const i=new ethers.providers.StaticJsonRpcProvider(r,{name:"any",chainId:this.config.chainId}),o=new ethers.Contract(this.config.contractAddress,[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],i),a=await o.core(),n=new ethers.Contract(a,[{inputs:[{internalType:"bytes32",name:"positionId",type:"bytes32"}],name:"getPositionDetails",outputs:[{components:[{internalType:"bytes32",name:"positionId",type:"bytes32"},{internalType:"bytes32",name:"pkpId",type:"bytes32"},{internalType:"uint256",name:"ucdDebt",type:"uint256"},{internalType:"string",name:"vaultAddress",type:"string"},{internalType:"address",name:"borrower",type:"address"},{internalType:"uint40",name:"createdAt",type:"uint40"},{internalType:"uint40",name:"lastUpdated",type:"uint40"},{internalType:"uint16",name:"selectedTerm",type:"uint16"},{internalType:"uint40",name:"expiryAt",type:"uint40"},{internalType:"enum LoanStatusLib.LoanStatus",name:"status",type:"uint8"}],internalType:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],i),c=await n.getPositionDetails(e),l=Number(c.status);if(l<0||l>7)throw console.error("[VaultSnapshot] INVALID STATUS - ABI decoding error detected"),console.error(" Position ID:",e),console.error(" Contract:",this.config.contractAddress),console.error(" Status decoded:",l,"(expected 0-7)"),console.error(" Full position:",JSON.stringify(c,null,2)),new Error(`Invalid position status decoded from contract: ${l} (expected 0-7). This indicates an ABI decoding issue. Position: ${e}`);const u=Number(c.expiryAt),d=Number(c.selectedTerm),h=(g=d,0===(p=u)?0:p-30*g*86400);var p,g;return{positionId:c.positionId,pkpId:c.pkpId,borrower:c.borrower,vaultAddress:this.parseVaultAddress(c.vaultAddress,s(this.config.chain)),ucdDebt:BigInt(c.ucdDebt.toString()),termStartTimestamp:h,selectedTerm:d,status:l,expiryAt:u}}parseVaultAddress(t,e){try{const r=JSON.parse(t);if("object"==typeof r&&null!==r){if("sepolia"===e.toLowerCase())return r.regtest||r.testnet||r.mainnet;const t=function(t){return o[t]}(e);return"testnet"===t&&(r.testnet||r.regtest)||r.mainnet}}catch(t){}return t}async isLiquidatable(t){return(await this.getVaultSnapshot(t)).isLiquidatable}async getCollateralRatio(t){return(await this.getVaultSnapshot(t)).collateralRatioBps}async getAvailableBalance(t){return(await this.getVaultSnapshot(t)).availableBTCSats}async hasSufficientCollateral(e,r,i){const o=await this.getVaultSnapshot(e);return function(e,r,i,o){const a=r+i;return 0n===a||Number(e*t*10000n/a)>=o}(o.collateralValueUsd,o.ucdDebt,r,i)}};var v=1000000000000n,A=100000000000000n;(async()=>{console.log("[Liquidation Validator] ========================================"),console.log("[Liquidation Validator] DEBUG: Received globalThis parameters:"),console.log("[Liquidation Validator] globalThis.contractAddresses:",globalThis.contractAddresses),console.log("[Liquidation Validator] globalThis.contractAddresses:",globalThis.contractAddresses),console.log("[Liquidation Validator] globalThis.positionId:",globalThis.positionId),console.log("[Liquidation Validator] ========================================");let t,e,i,o="0";try{o="0a",console.log("[Step 0a] Validating configuration...");const s=globalThis.chain,c=globalThis.bitcoinProviderUrl,l=globalThis.positionId;if(!s)throw new Error('Missing required parameter: "chain"');if(!c)throw new Error('Missing required parameter: "bitcoinProviderUrl"');if(!l)throw new Error('Missing required parameter: "positionId"');let u,h,p;o="0b",h=s,p="sepolia"===s?"testnet":"regtest",u={name:"Custom Provider",url:c,minConfirmations:1,network:p},o="0c";const g=globalThis.contractAddresses;if(!g||"object"!=typeof g)throw new Error("Missing or invalid 'contractAddresses' parameter");if(e=g.PositionManager,i=g.LoanOperationsManagerModule,!e||!i)throw new Error("Missing one or more required contract addresses (PositionManager, LoanOperationsManagerModule)");t=new ethers.providers.JsonRpcProvider(globalThis.rpcUrl||Lit.Actions.getRpcUrl());const T=new f({provider:u,priceProviders:globalThis.priceProviders}),b=new d({provider:u}),$=(a={contractAddress:i,chain:s,chainId:n(h),rpcUrl:globalThis.rpcUrl,bitcoinProvider:b,minConfirmations:1},new y(a)),P=function(t){return new S(t)}({contractAddress:e,termManagerAddress:g.TermManagerModule,loanOpsManagerAddress:i,chain:s,chainId:n(h),rpcUrl:globalThis.rpcUrl,vaultBalance:$,priceOracle:T});o="1",console.log("[Step 1] Getting vault snapshot...");const E=await P.getVaultSnapshot(l);if(E.btcPriceUsd<v||E.btcPriceUsd>A)throw new Error(`Bitcoin price ${E.btcPriceUsd} is outside acceptable range (1000000000000 - 100000000000000). This may indicate oracle manipulation or stale price data. Please try again later.`);console.log(` \u2705 BTC price validation passed: $${(Number(E.btcPriceUsd)/1e8).toFixed(2)}`),o="2",console.log("[Step 2] Validating position state...");if(!["ACTIVE","EXPIRED","LIQUIDATABLE"].includes(E.status))throw new Error(`Invalid position status for liquidation: ${r(E.status)}. Must be ACTIVE, EXPIRED, or LIQUIDATABLE.`);if(console.log(` \u2705 Position status valid for liquidation: ${r(E.status)}`),o="3",console.log("[Step 3] Checking liquidation eligibility..."),!E.isLiquidatable)throw new Error(`Position is not liquidatable (${m(E.collateralRatioBps)}% >= ${m(E.currentLiquidationThreshold)}%) - liquidation rejected.`);console.log(" \u2705 Position is liquidatable"),console.log(` - Current ratio: ${m(E.collateralRatioBps)}%`),console.log(` - Liquidation threshold: ${m(E.currentLiquidationThreshold)}%`),o="4",console.log("[Step 4] Building authorization message...");const U=w()+10,I=ethers.utils.defaultAbiCoder.encode(["bytes32","uint256","uint256"],[l,U,E.btcPriceUsd.toString()]),B=ethers.utils.defaultAbiCoder.encode(["bytes32","uint256","uint256","uint256"],[l,U,E.btcPriceUsd.toString(),E.availableBTCSats.toString()]),C=ethers.utils.keccak256(I),x=ethers.utils.keccak256(B),O=globalThis.publicKey;o="5",console.log("[Step 5] Signing authorization...");const D=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(C),publicKey:O,sigName:"liquidationAuth"}),k=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(x),publicKey:O,sigName:"liquidationBonusAuth"});console.log("[Liquidation Validator] \u2705 Complete"),Lit.Actions.setResponse({response:JSON.stringify({approved:!0,positionId:l,btcPrice:E.btcPriceUsd.toString(),btcAmountSats:E.availableBTCSats.toString(),signature:D,signatureBonus:k,timestamp:U,validatorPkp:O})})}catch(t){console.error("[Liquidation Validator] \u274c Failed:",t.message),console.error(`[Liquidation Validator] Failed at step: ${o}`),Lit.Actions.setResponse({response:JSON.stringify({approved:!1,reason:t.message||t.toString(),failedStep:o,positionId:globalThis.positionId,timestamp:Date.now()})})}var a})()})();
@@ -0,0 +1 @@
1
+ 1836dc3de51790e761756806e50686604df87d9c4edcb1c6f2d8ca01c649cead
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "liquidation-validator",
3
+ "originalSize": 84398,
4
+ "minifiedSize": 35221,
5
+ "compressionRatio": 0.5826796843527098,
6
+ "hash": "1836dc3de51790e761756806e50686604df87d9c4edcb1c6f2d8ca01c649cead",
7
+ "buildTime": 1768236994915,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{var t=class{stepStart(t,e){}stepEnd(t,e){}log(t,e){}warn(t,e){}error(t,e){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},e=class{constructor(t){this.stepStartTimes=new Map,this.stepDurations=new Map,this.TIMEOUT_MS=3e4,this.actionName=t,this.executionStartTime=Date.now();const e=Date.now(),r=Math.random().toString(36).substring(2,9);this.executionId=`exec_${e}_${r}`,this.logHeader()}logHeader(){console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution started`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`);void 0!==globalThis.litNodeContext?(console.log(`[${this.actionName}] Context: LIT Node`),console.log(`[${this.actionName}] Node: ${globalThis.litNodeContext?.nodeAddress||"unknown"}`)):console.log(`[${this.actionName}] Context: Browser/Test`),console.log(`[${this.actionName}] ========================================`)}stepStart(t,e){this.stepStartTimes.set(t,Date.now());const r=Date.now()-this.executionStartTime,a=this.TIMEOUT_MS-r;console.log(`[Step ${t}] ${e}...`),console.log(`[Step ${t}] Elapsed: ${r}ms | Remaining: ${a}ms`)}stepEnd(t,e=5e3){const r=this.stepStartTimes.get(t);if(!r)return console.warn(`[Step ${t}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const a=Date.now()-r;this.stepDurations.set(t,a),console.log(`[Step ${t}] Duration: ${a}ms`),a>e&&console.warn(`\u26a0\ufe0f [Step ${t}] Took ${a}ms (> ${e}ms threshold)`);const o=Date.now()-this.executionStartTime,i=this.TIMEOUT_MS-o;i<5e3&&console.warn(`\u26a0\ufe0f [Step ${t}] Time remaining: ${i}ms (approaching timeout)`)}log(t,e){console.log(`[Step ${t}] ${e}`)}warn(t,e){console.warn(`\u26a0\ufe0f [Step ${t}] ${e}`)}error(t,e){console.error(`\u274c [Step ${t}] ${e}`)}getElapsed(){return Date.now()-this.executionStartTime}getRemaining(){return this.TIMEOUT_MS-this.getElapsed()}summary(){const t=Date.now()-this.executionStartTime,e=this.TIMEOUT_MS-t;if(console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution Summary`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`),console.log(`[${this.actionName}] Total time: ${(t/1e3).toFixed(2)}s`),console.log(`[${this.actionName}] Time remaining: ${e}ms`),this.stepDurations.size>0){console.log(`[${this.actionName}] Step breakdown:`);for(const[e,r]of this.stepDurations.entries()){const a=(r/t*100).toFixed(1);console.log(`[${this.actionName}] Step ${e}: ${r}ms (${a}%)`)}}t>2e4&&console.warn(`\u26a0\ufe0f [${this.actionName}] Slow execution: ${(t/1e3).toFixed(2)}s (${e}ms remaining)`),e<5e3&&console.warn(`\u26a0\ufe0f [${this.actionName}] CRITICAL: Only ${e}ms remaining before timeout!`),console.log(`[${this.actionName}] ========================================`)}getExecutionId(){return this.executionId}isDebugEnabled(){return!0}},r=class{static create(r="LIT Action",a){return a??globalThis.debugAction??!1?new e(r):new t}},a=class{constructor(t){this.config=t,this.timeout=t.timeout||15e3,this.logger=r.create("BitcoinDataProvider")}async getCurrentBlockHeight(){if(this.logger.stepStart("0","getCurrentBlockHeight"),this.config.rpcHelper){const t=await this.config.rpcHelper.getBlockCount();return this.logger.stepEnd("0"),t}const t=new AbortController,e=setTimeout((()=>t.abort()),this.timeout);try{const r=await fetch(`${this.config.providerUrl}/blocks/tip/height`,{signal:t.signal,headers:{Accept:"text/plain","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(e),!r.ok)throw new Error(`Failed to fetch block height: ${r.status} ${r.statusText}`);const a=await r.text(),o=parseInt(a.trim(),10);if(isNaN(o))throw new Error(`Invalid block height response: ${a}`);return this.logger.stepEnd("0"),o}catch(t){if(clearTimeout(e),this.logger.stepEnd("0"),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOs(t){this.logger.log("0",`Original address: "${t}"`);const e=this.stripNetworkPrefix(t);if(this.logger.log("0",`Cleaned address: "${e}"`),this.config.rpcHelper)return await this.fetchUTXOsFromRPC(e);try{return await this.fetchUTXOsFromProvider(this.config.providerUrl,e)}catch(t){if(this.logger.warn("0",`Primary provider failed: ${t.message}`),this.config.fallbackProviders&&this.config.fallbackProviders.length>0){const t=[...this.config.fallbackProviders].sort(((t,e)=>t.priority-e.priority));for(const r of t)try{return this.logger.log("0",`Trying fallback: ${r.name} (${r.url})`),await this.fetchUTXOsFromProvider(r.url,e)}catch(t){this.logger.warn("0",`Fallback ${r.name} failed: ${t.message}`);continue}}throw new Error(`Failed to fetch UTXOs: ${t.message}`)}}stripNetworkPrefix(t){return t.replace(/^(REGTEST_|TESTNET_|MAINNET_)/,"")}async fetchUTXOsFromRPC(t){if(!this.config.rpcHelper)throw new Error("RPC helper not configured");const e=this.config.rpcWallet||"";return(await this.config.rpcHelper.listUnspent(e,t)).map((t=>({txid:t.txid,vout:t.vout,satoshis:BigInt(Math.round(1e8*t.amount)),confirmations:t.confirmations})))}parseBlockstreamUTXOs(t,e){if(!Array.isArray(t))throw new Error("Invalid Blockstream response format: expected an array");return t.map((t=>{let r=0;return t.status&&"object"==typeof t.status?t.status.confirmed&&(r=void 0!==t.status.block_height&&t.status.block_height>0?e-t.status.block_height+1:1):void 0!==t.confirmations&&(r=t.confirmations),{txid:t.txid,vout:void 0!==t.vout?t.vout:t.n,satoshis:BigInt(void 0!==t.value?t.value:t.satoshis),confirmations:r}}))}async fetchUTXOsFromProvider(t,e){this.logger.stepStart("1","fetchUTXOsFromProvider");const r=`${t}/address/${e}/utxos`;this.logger.log("1",`Fetching UTXOs from ${r}`);const a=await this.getCurrentBlockHeight();this.logger.log("1",`Current block height: ${a}`);const o=new AbortController,i=setTimeout((()=>o.abort()),this.timeout);try{const t=await fetch(r,{signal:o.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(i),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();this.logger.log("1",`Raw API Response: ${JSON.stringify(e,null,2)}`);const s=this.parseBlockstreamUTXOs(e,a);return this.logger.stepEnd("1"),s}catch(t){if(clearTimeout(i),this.logger.stepEnd("1"),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOSet(t,e){let r=[],a=null;for(let e=1;e<=3;e++)try{this.logger.log("2",`UTXO query attempt ${e}/3...`),r=await this.getUTXOs(t),this.logger.log("2",`UTXO query succeeded on attempt ${e}`),a=null;break}catch(t){a=t,this.logger.error("2",`UTXO query failed on attempt ${e}: ${t.message}`),e<3&&(this.logger.log("2","Waiting 500ms before retry..."),await new Promise((t=>setTimeout(t,500))))}if(a)throw new Error(`Failed to query UTXOs after 3 attempts: ${a.message}`);const o=r.reduce(((t,e)=>t+e.satoshis),0n),i=r.filter((t=>t.confirmations>=e)).reduce(((t,e)=>t+e.satoshis),0n),s=o-i;return{utxos:r,totalBalance:o,totalUTXOs:r.length,confirmedBalance:i,unconfirmedBalance:s}}async getBalance(t){const e=this.stripNetworkPrefix(t),r=`${this.config.providerUrl}/address/${e}`;this.logger.log("3",`Fetching balance from ${r}`);const a=new AbortController,o=setTimeout((()=>a.abort()),this.timeout);try{const t=await fetch(r,{signal:a.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(o),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();if(!e.chain_stats)throw new Error("Invalid response: missing chain_stats");const i=e.chain_stats.funded_txo_sum,s=e.chain_stats.spent_txo_sum;if(void 0===i||void 0===s)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${i}, spent_txo_sum: ${s}`);const n=BigInt(i-s);return this.logger.log("3",`Balance: ${n.toString()} sats`),this.logger.log("3",` - Funded: ${i} sats, Spent: ${s} sats`),n}catch(t){if(clearTimeout(o),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);const r=t.message||"Unknown error";throw this.logger.log("3",`Balance fetch failed: ${r}`),new Error(`Failed to fetch Bitcoin balance for address ${e}: ${r}`)}}async getTransaction(t){if(this.config.rpcHelper)try{const e=this.config.rpcWallet||"",r=await this.config.rpcHelper.getTransaction(e,t);return{txid:r.txid,confirmations:r.confirmations||0}}catch(t){if(t.message?.includes("Invalid or non-wallet transaction")||-5===t.code)return null;throw t}const e=new AbortController,r=setTimeout((()=>e.abort()),this.timeout);try{const a=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(r),!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,confirmations:o.status.confirmed&&o.status.block_height?1:0}:null}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);if(t.message?.includes("404"))return null;throw t}}},o=1000000000000000000n,i=(t=>(t.SEPOLIA="sepolia",t.ETHEREUM="ethereum",t.HARDHAT="hardhat",t))(i||{}),s=(t=>(t.TESTNET="testnet",t.MAINNET="mainnet",t.REGTEST="regtest",t))(s||{}),n={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},c={ethereum:1,sepolia:11155111,hardhat:31337},l={regtest:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://127.0.0.1:18443",network:"regtest",minConfirmations:1,name:"Local Regtest"},{url:"https://313561dc30c5.ngrok-free.app",network:"regtest",minConfirmations:1,name:"Ngrok Bitcoin Tunnel"}],testnet:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"}],mainnet:[{url:"https://blockstream.info/api",network:"mainnet",minConfirmations:6,name:"Blockstream Mainnet"},{url:"https://mempool.space/api",network:"mainnet",minConfirmations:6,name:"Mempool.space Mainnet"}]};function u(t){const e=t.toLowerCase().trim();if("sepolia"===e)return"sepolia";if("ethereum"===e||"mainnet"===e)return"ethereum";if("hardhat"===e)return"hardhat";throw new Error(`Unsupported EVM chain: "${t}". Supported chains: ${Object.values(i).join(", ")}`)}function d(t){return n[t]}function h(t){return c[t]}var p=class{constructor(t,e,r){this.mode=r,e&&Array.isArray(e)?this.sources=e:t&&Array.isArray(t)?this.sources=this.buildSources(t):this.sources=this.buildSources(),this.sources.sort(((t,e)=>t.priority-e.priority)),this.validateProviderCount()}validateProviderCount(){if("prod"===this.mode&&this.sources.length<3)throw new Error(`Production mode requires at least 3 price providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported providers: CoinGecko, Binance, Coinbase, CryptoCompare (with API key)`);if(0===this.sources.length)throw new Error("No price providers configured. At least one provider is required.")}buildSources(t){const e=[];if(!t||0===t.length)return this.getDefaultSources();let r=1;for(const a of t){switch(a.name.toLowerCase()){case"coingecko":e.push(this.createCoinGeckoSource(a.apiKey,r++));break;case"binance":e.push(this.createBinanceSource(a.apiKey,a.apiSecret,r++));break;case"coinbase":e.push(this.createCoinbaseSource(a.apiKey,r++));break;case"cryptocompare":if(!a.apiKey)throw new Error("CryptoCompare requires an API key. Please provide apiKey in priceProviders config.");e.push(this.createCryptoCompareSource(a.apiKey,r++));break;default:console.warn(`[Price Oracle] Unknown provider: ${a.name} - skipping`)}}return e}createCryptoCompareSource(t,e){return{name:"CryptoCompare",fetchPrice:async()=>{const e=new URL("https://min-api.cryptocompare.com/data/price");e.searchParams.set("fsym","BTC"),e.searchParams.set("tsyms","USDT"),e.searchParams.set("api_key",t);const r=await(async t=>{const e=new AbortController,r=setTimeout((()=>e.abort()),5e3);try{const a=await fetch(t,{signal:e.signal,headers:{Accept:"application/json"}});if(clearTimeout(r),!a.ok)throw new Error(`HTTP ${a.status} ${a.statusText}`);return await a.json()}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error("Request timeout after 5000ms");throw t}})(e.toString()),a=Number(r?.USDT);if(!Number.isFinite(a)||a<=0)throw new Error("Invalid CryptoCompare price payload");const o=(await Lit.Actions.broadcastAndCollect({name:"cryptoComparePrice",value:a.toString()})).map((t=>parseFloat(t)));return o.sort(((t,e)=>t-e)),o[Math.floor(o.length/2)]},priority:e}}createFetchJson(){return async(t,e)=>{const r=new AbortController,a=setTimeout((()=>r.abort()),5e3);try{const a=await fetch(t,{headers:{Accept:"application/json",...e||{}},signal:r.signal});if(!a.ok)throw new Error(`HTTP ${a.status} ${a.statusText}`);return await a.json()}finally{clearTimeout(a)}}}getDefaultSources(){return[this.createCoinGeckoSource(void 0,1),this.createBinanceSource(void 0,void 0,2),this.createCoinbaseSource(void 0,3)]}createCoinGeckoSource(t,e=1){const r=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const t=new URL("https://api.coingecko.com/api/v3/simple/price");t.searchParams.set("ids","bitcoin"),t.searchParams.set("vs_currencies","usd");const e=await r(t.toString()),a=Number(e?.bitcoin?.usd);if(!Number.isFinite(a)||a<=0)throw new Error("Invalid CoinGecko price payload");const o=(await Lit.Actions.broadcastAndCollect({name:"coinGeckoPrice",value:a.toString()})).map((t=>parseFloat(t)));return o.sort(((t,e)=>t-e)),o[Math.floor(o.length/2)]},priority:e}}createBinanceSource(t,e,r=2){const a=this.createFetchJson();return{name:"Binance",fetchPrice:async()=>{const e=new URL("https://api.binance.com/api/v3/ticker/price");e.searchParams.set("symbol","BTCUSDT");const r=t?{"X-MBX-APIKEY":t}:void 0,o=await a(e.toString(),r),i=Number(o?.price);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid Binance price payload");const s=(await Lit.Actions.broadcastAndCollect({name:"binancePrice",value:i.toString()})).map((t=>parseFloat(t)));return s.sort(((t,e)=>t-e)),s[Math.floor(s.length/2)]},priority:r}}createCoinbaseSource(t,e=3){const r=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const e=t?{"CB-ACCESS-KEY":t}:void 0,a=await r("https://api.coinbase.com/v2/prices/BTC-USD/spot",e),o=Number(a?.data?.amount);if(!Number.isFinite(o)||o<=0)throw new Error("Invalid Coinbase price payload");const i=(await Lit.Actions.broadcastAndCollect({name:"coinbasePrice",value:o.toString()})).map((t=>parseFloat(t)));return i.sort(((t,e)=>t-e)),i[Math.floor(i.length/2)]},priority:e}}async getBTCPrice(){const t=Date.now();console.log("[Price Oracle] Fetching BTC price from external sources (parallel)..."),console.log(`[Price Oracle] Start time: ${t}`);const e=new Promise(((e,r)=>{setTimeout((()=>{const e=Date.now()-t;console.error(`[Price Oracle] \u274c GLOBAL TIMEOUT after ${e}ms`),r(new Error("Price oracle global timeout after 15000ms"))}),15e3)})),r=this.sources.map((async t=>{const e=Date.now();try{console.log(`[Price Oracle] [${e}] Querying ${t.name}...`);const r=await t.fetchPrice(),a=Date.now()-e;return console.log(`[Price Oracle] \u2705 [+${a}ms] ${t.name}: $${r.toLocaleString()}`),{source:t.name,priceUSD:r}}catch(r){const a=Date.now()-e;throw console.warn(`[Price Oracle] \u26a0\ufe0f [+${a}ms] ${t.name} failed: ${r.message}`),r}}));try{console.log("[Price Oracle] Waiting for first success (race pattern)...");const a=await Promise.race([Promise.race(r.map((t=>t.catch((t=>({error:t})))))),e]);if("error"in a){console.log("[Price Oracle] First result was error, waiting for any success...");const e=Date.now(),a=new Promise(((t,r)=>{setTimeout((()=>{const t=Date.now()-e;console.error(`[Price Oracle] \u274c FALLBACK TIMEOUT after ${t}ms`),r(new Error("Price oracle fallback timeout after 10000ms"))}),1e4)})),o=await Promise.race([Promise.allSettled(r),a]),i=Date.now()-e;console.log(`[Price Oracle] Fallback completed in ${i}ms`);const s=o.find((t=>"fulfilled"===t.status));if(!s){const e=Date.now()-t;throw console.error(`[Price Oracle] All sources failed after ${e}ms`),new Error("All price sources failed")}const{source:n,priceUSD:c}=s.value,l=Date.now()-t;console.log(`[Price Oracle] Using ${n} (first successful after failures) - total time: ${l}ms`);const u=Math.round(100*c),d=1000000n*BigInt(u);return console.log(`[Price Oracle] Price with 8 decimals: ${d}`),d}const{source:o,priceUSD:i}=a,s=Date.now()-t;console.log(`[Price Oracle] Using ${o} (fastest response) - total time: ${s}ms`);const n=Math.round(100*i),c=1000000n*BigInt(n);return console.log(`[Price Oracle] Price with 8 decimals: ${c}`),c}catch(e){const r=Date.now()-t;throw console.error(`[Price Oracle] Failed after ${r}ms: ${e.message}`),new Error(`All price sources failed: ${e.message}`)}}async getBTCPriceConsensus(){console.log("[Price Oracle] Fetching BTC price with consensus...");const t=(await Promise.allSettled(this.sources.map((async t=>{const e=await t.fetchPrice();return{source:t.name,price:e}})))).filter((t=>"fulfilled"===t.status)).map((t=>t.value));if(0===t.length)throw new Error("No price sources returned data");console.log(`[Price Oracle] Got prices from ${t.length}/${this.sources.length} sources:`),t.forEach((t=>{console.log(` ${t.source}: $${t.price.toLocaleString()}`)}));const e=t.map((t=>t.price));e.sort(((t,e)=>t-e));const r=e[Math.floor(e.length/2)],a=e[0],o=e[e.length-1]/a,i=t.filter((t=>Math.abs(t.price-r)/r<=.02));if(o>1.05&&i.length===t.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(o-1)).toFixed(1)}% spread)`);if(i.length<t.length){const e=t.filter((t=>!i.find((e=>e.source===t.source))));if(console.log(`[Price Oracle] \u26a0\ufe0f Detected ${e.length} outlier(s):`),e.forEach((t=>{const e=Math.abs(t.price-r)/r;console.log(` ${t.source}: $${t.price.toLocaleString()} (${(100*e).toFixed(1)}% deviation)`)})),i.length<2)throw new Error("Price consensus failed: insufficient valid sources after outlier removal");console.log(`[Price Oracle] \u2705 Outliers filtered, continuing with ${i.length} valid sources`);const a=i.map((t=>t.price));a.sort(((t,e)=>t-e));const o=a[Math.floor(a.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${o.toLocaleString()}`);const s=Math.round(100*o);return 1000000n*BigInt(s)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${r.toLocaleString()}`);const s=Math.round(100*r);return 1000000n*BigInt(s)}},m=class{constructor(t){this.config=t,this.bitcoinProvider=t.bitcoinProvider}async calculateBalance(t,e){const r=await this.bitcoinProvider.getBalance(e),a=await this.getAuthorizedSpendsFromContract(t),o=a.reduce(((t,e)=>t+e.satoshis),0n),i=r>o?r-o:0n;return{totalUTXOs:[],totalBalance:r,authorizedUTXOs:a,authorizedBalance:o,authorizedSpendsHash:this.computeAuthorizedSpendsHash(t,a),availableUTXOs:[],availableBalance:i,vaultAddress:e,positionId:t,timestamp:Date.now()}}async calculateTrustedBalance(t,e,r){if(r)return await this.calculateBalance(t,e);const a=this.config.minConfirmations||6,o=await this.bitcoinProvider.getUTXOSet(e,a),i=await this.getAuthorizedSpendsFromContract(t);for(const t of i){if(o.utxos.some((e=>e.txid===t.txid&&e.vout===t.vout)))throw new Error(`Authorized UTXO ${t.txid}:${t.vout} not yet spent. Transaction was authorized in smart contract but not signed and broadcasted to Bitcoin network. Complete the transaction by signing and broadcasting it.`)}const s=i.reduce(((t,e)=>t+e.satoshis),0n),n=o.utxos.filter((t=>!this.isUTXOAuthorized(t,i))),c=n.reduce(((t,e)=>t+e.satoshis),0n),l=this.computeAuthorizedSpendsHash(t,i);return{totalUTXOs:o.utxos,totalBalance:o.totalBalance,authorizedUTXOs:i,authorizedBalance:s,authorizedSpendsHash:l,availableUTXOs:n,availableBalance:c,vaultAddress:e,positionId:t,timestamp:Date.now()}}async getTrustedBalance(t,e){return(await this.calculateTrustedBalance(t,e)).availableBalance}async getAvailableUTXOs(t,e){return(await this.calculateTrustedBalance(t,e)).availableUTXOs}async isUTXOAvailable(t,e,r,a){return(await this.getAvailableUTXOs(t,e)).some((t=>t.txid===r&&t.vout===a))}isUTXOAuthorized(t,e){return e.some((e=>e.txid===t.txid&&e.vout===t.vout))}async getAuthorizedSpendsFromContract(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let r;r=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const a=new ethers.providers.StaticJsonRpcProvider(r,{name:"any",chainId:this.config.chainId}),o=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"}],a);return(await o.getAuthorizedSpends(e)).map((e=>({txid:e.txid,vout:Number(e.vout),satoshis:BigInt(e.satoshis.toString()),positionId:t,targetAddress:e.targetAddress,targetAmount:BigInt(e.targetAmount.toString()),timestamp:Number(e.authorizedAt)})))}computeAuthorizedSpendsHash(t,e){const r=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;if(0===e.length)return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,[]]));const a=e.map((t=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.txid)),r=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.targetAddress));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","uint32","uint256","bytes32","uint256","uint256"],[e,t.vout,t.satoshis.toString(),r,t.targetAmount.toString(),t.timestamp]))}));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[r,a]))}};function g(t,e){if(0===t)return{termDurationDays:0,termLengthDays:30*e,isExpired:!1,daysUntilExpiry:30*e,daysIntoGracePeriod:0};const r=30*e,a=Math.floor(Date.now()/1e3)-t,o=Math.floor(a/86400);return{termDurationDays:o,termLengthDays:r,isExpired:o>r,daysUntilExpiry:Math.max(0,r-o),daysIntoGracePeriod:Math.max(0,o-r)}}function f(t,e){if(!t)return 13e3;const r=Math.min(e,30);return 11e3+9e3*(r*r)/900}function w(t,e,r){const a=t*e/10000000000000000n;if(0n===r)return{collateralValueUsd:a,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:a,collateralRatioBps:Number(a*o*10000n/r)}}var y=class{constructor(t){this.termManagerAddress=t.termManagerAddress,this.loanOpsManagerAddress=t.loanOpsManagerAddress,this.chain=t.chain,this.chainId=t.chainId,this.rpcUrl=t.rpcUrl}async getLiquidationThreshold(){try{let t;t=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const e=new ethers.providers.StaticJsonRpcProvider(t,{name:"any",chainId:this.chainId}),r=[{inputs:[],name:"liquidationThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],a=new ethers.Contract(this.loanOpsManagerAddress,r,e),o=await a.liquidationThreshold();return Number(o.toString())}catch(t){throw console.error("[ProtocolParameters] Error fetching liquidation threshold:",t.message),new Error(`Failed to fetch liquidation threshold: ${t.message}`)}}async getTermFees(t){try{let e;e=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const r=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),a=[{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"}],o=new ethers.Contract(this.termManagerAddress,a,r),i=await o.getTermFees(t);return{originationFeeBps:Number(i.originationFee?.toString?.()??i[0]?.toString?.()??i[0]),extensionFeeBps:Number(i.extensionFee?.toString?.()??i[1]?.toString?.()??i[1])}}catch(t){throw console.error("[ProtocolParameters] Error fetching term fees:",t.message),new Error(`Failed to fetch term fees: ${t.message}`)}}async getAuthorizedSpendsHash(t){try{const e=this.rpcUrl||await Lit.Actions.getRpcUrl({chain:this.chain}),r=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),a=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`,o=new ethers.Contract(this.loanOpsManagerAddress,[{inputs:[{name:"positionId",type:"bytes32"}],name:"getAuthorizedSpendsHash",outputs:[{name:"",type:"bytes32"}],stateMutability:"view",type:"function"}],r);return await o.getAuthorizedSpendsHash(a)}catch(t){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",t.message),new Error(`Failed to fetch authorized spends hash: ${t.message}`)}}};function T(t){return new y(t)}var b=class{constructor(t){this.config=t}async getVaultSnapshot(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const r=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress),a=await this.config.priceOracle.getBTCPrice(),o=T({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[i,s]=await Promise.all([o.getLiquidationThreshold(),o.getTermFees(e.selectedTerm)]),n=g(e.termStartTimestamp,e.selectedTerm),c=f(n.isExpired,n.daysIntoGracePeriod),l=w(r.availableBalance,a,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,totalBTCSats:r.totalBalance,totalUTXOs:r.totalUTXOs,authorizedSpendsSats:r.authorizedBalance,authorizedSpendsHash:r.authorizedSpendsHash,availableBTCSats:r.availableBalance,availableUTXOs:r.availableUTXOs,btcPriceUsd:a,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:n.termDurationDays,termLengthDays:n.termLengthDays,isExpired:n.isExpired,daysUntilExpiry:n.daysUntilExpiry,daysIntoGracePeriod:n.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:i,originationFeeBps:s.originationFeeBps,extensionFeeBps:s.extensionFeeBps,timestamp:Date.now()}}async getVaultSnapshotFast(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const r=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress,!0),a=await this.config.priceOracle.getBTCPrice(),o=T({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[i,s]=await Promise.all([o.getLiquidationThreshold(),o.getTermFees(e.selectedTerm)]),n=g(e.termStartTimestamp,e.selectedTerm),c=f(n.isExpired,n.daysIntoGracePeriod),l=w(r.availableBalance,a,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,totalBTCSats:r.totalBalance,totalUTXOs:r.totalUTXOs,authorizedSpendsSats:r.authorizedBalance,authorizedSpendsHash:r.authorizedSpendsHash,availableBTCSats:r.availableBalance,availableUTXOs:r.availableUTXOs,btcPriceUsd:a,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:n.termDurationDays,termLengthDays:n.termLengthDays,isExpired:n.isExpired,daysUntilExpiry:n.daysUntilExpiry,daysIntoGracePeriod:n.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:i,originationFeeBps:s.originationFeeBps,extensionFeeBps:s.extensionFeeBps,timestamp:Date.now()}}async queryPositionState(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let r;r=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const a=new ethers.providers.StaticJsonRpcProvider(r,{name:"any",chainId:this.config.chainId}),o=new ethers.Contract(this.config.contractAddress,[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],a),i=await o.core(),s=new ethers.Contract(i,[{inputs:[{internalType:"bytes32",name:"positionId",type:"bytes32"}],name:"getPositionDetails",outputs:[{components:[{internalType:"bytes32",name:"positionId",type:"bytes32"},{internalType:"bytes32",name:"pkpId",type:"bytes32"},{internalType:"uint256",name:"ucdDebt",type:"uint256"},{internalType:"string",name:"vaultAddress",type:"string"},{internalType:"address",name:"borrower",type:"address"},{internalType:"uint40",name:"createdAt",type:"uint40"},{internalType:"uint40",name:"lastUpdated",type:"uint40"},{internalType:"uint16",name:"selectedTerm",type:"uint16"},{internalType:"uint40",name:"expiryAt",type:"uint40"},{internalType:"enum LoanStatusLib.LoanStatus",name:"status",type:"uint8"}],internalType:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],a),n=await s.getPositionDetails(e),c=Number(n.status);if(c<0||c>7)throw console.error("[VaultSnapshot] INVALID STATUS - ABI decoding error detected"),console.error(" Position ID:",e),console.error(" Contract:",this.config.contractAddress),console.error(" Status decoded:",c,"(expected 0-7)"),console.error(" Full position:",JSON.stringify(n,null,2)),new Error(`Invalid position status decoded from contract: ${c} (expected 0-7). This indicates an ABI decoding issue. Position: ${e}`);const l=Number(n.expiryAt),d=Number(n.selectedTerm),h=(m=d,0===(p=l)?0:p-30*m*86400);var p,m;return{positionId:n.positionId,pkpId:n.pkpId,borrower:n.borrower,vaultAddress:this.parseVaultAddress(n.vaultAddress,u(this.config.chain)),ucdDebt:BigInt(n.ucdDebt.toString()),termStartTimestamp:h,selectedTerm:d,status:c,expiryAt:l}}parseVaultAddress(t,e){try{const r=JSON.parse(t);if("object"==typeof r&&null!==r){if("sepolia"===e.toLowerCase())return r.regtest||r.testnet||r.mainnet;return"testnet"===d(e)&&(r.testnet||r.regtest)||r.mainnet}}catch(t){}return t}async isLiquidatable(t){return(await this.getVaultSnapshot(t)).isLiquidatable}async getCollateralRatio(t){return(await this.getVaultSnapshot(t)).collateralRatioBps}async getAvailableBalance(t){return(await this.getVaultSnapshot(t)).availableBTCSats}async hasSufficientCollateral(t,e,r){const a=await this.getVaultSnapshot(t);return function(t,e,r,a){const i=e+r;return 0n===i||Number(t*o*10000n/i)>=a}(a.collateralValueUsd,a.ucdDebt,e,r)}};(async()=>{console.log("[BTC Deposit Validator] Starting...");const t=globalThis.chain,e=globalThis.bitcoinProviderUrl,r=globalThis.positionId,o=globalThis.publicKey,i=globalThis.timestamp||Math.floor(Date.now()/1e3);if(!t)throw new Error('Missing required parameter: "chain"');if(!e)throw new Error('Missing required parameter: "bitcoinProviderUrl"');if(!r)throw new Error('Missing required parameter: "positionId"');if(!o)throw new Error('Missing required parameter: "publicKey"');const n=u(t),c=d(n),g=function(t){for(const e of Object.values(s)){const r=l[e].find((e=>e.url===t));if(r)return r}const e=Object.entries(l).flatMap((([t,e])=>e.map((e=>`${t}: ${e.name} (${e.url})`)))).join(", ");throw new Error(`Bitcoin provider not approved. Provided URL: ${t}. Approved providers: ${e}`)}(e);if(console.log(` \u2705 Chain: ${t}`),console.log(` \u2705 Bitcoin Network: ${c}`),console.log(` \u2705 Position ID: ${r}`),!globalThis.contractAddresses)throw new Error('Missing required parameter: "contractAddresses" (PositionManager, LoanOperationsManagerModule, TermManagerModule)');const f=globalThis.contractAddresses.PositionManager,w=globalThis.contractAddresses.LoanOperationsManagerModule,y=globalThis.contractAddresses.TermManagerModule;try{console.log("[Step 1] Getting vault snapshot...");const e=new a({providerUrl:g.url}),s=new p,c=function(t){return new b(t)}({contractAddress:f,termManagerAddress:y,loanOpsManagerAddress:w,chain:t,vaultBalance:(T={contractAddress:w,chain:t,chainId:h(n),bitcoinProvider:e,minConfirmations:g.minConfirmations,rpcUrl:globalThis.customRpcUrl},new m(T)),priceOracle:s,rpcUrl:globalThis.customRpcUrl}),l=await c.getVaultSnapshot(r);if(l.btcPriceUsd<1000000000000n||l.btcPriceUsd>100000000000000n)throw new Error(`Bitcoin price ${l.btcPriceUsd} is outside acceptable range`);console.log(` \u2705 Vault balance: ${l.availableBTCSats} sats`),console.log(` \u2705 BTC price: $${(Number(l.btcPriceUsd)/1e8).toFixed(2)}`);const u=l.vaultAddress;console.log("[Step 2] Signing vault data...");const d=ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","uint256","uint256","uint256"],[r,l.availableBTCSats,l.btcPriceUsd,i])),S=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(d),publicKey:"0x"+o,sigName:"btcDepositAuth"});console.log("[BTC Deposit Validator] \u2705 Complete"),Lit.Actions.setResponse({response:JSON.stringify({vaultAddress:u,vaultBalance:l.availableBTCSats.toString(),positionId:r,btcPrice:l.btcPriceUsd.toString(),signature:S,timestamp:i,validatorPkp:o})})}catch(t){console.error("[BTC Deposit Validator] \u274c Failed:",t.message),Lit.Actions.setResponse({response:JSON.stringify({error:t.message||t.toString(),positionId:r||null,timestamp:Date.now()})})}var T})()})();
@@ -0,0 +1 @@
1
+ b9ffd06fc8d8c1154e3ee205c96f2edf09055b3b2fe224812e99f32b8301867a
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "loan-vault-btc-balance",
3
+ "originalSize": 81948,
4
+ "minifiedSize": 34377,
5
+ "compressionRatio": 0.5805022697320252,
6
+ "hash": "b9ffd06fc8d8c1154e3ee205c96f2edf09055b3b2fe224812e99f32b8301867a",
7
+ "buildTime": 1768236995076,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=void(async()=>{const e=globalThis.targetPkpTokenId,t=globalThis.expectedCid,o=globalThis.network;console.log("\ud83d\udd0d LIT ACTION TRACE: pkp-validator-datil.ts ENTRY"),console.log(` - Received targetPkpTokenId: "${e}"`),console.log(` - Received expectedCid: "${t}"`),console.log(` - Received network: "${o}"`);try{let s=function(e,t){const o=ethers.utils.hexlify(e).startsWith("0x")?ethers.utils.hexlify(e).slice(2):ethers.utils.hexlify(e);if(130!==o.length)throw new Error(`Invalid public key length: ${o.length}`);const s=o.slice(2,66),l=o.slice(66,130),r=(BigInt("0x"+l)%2n===0n?"02":"03")+s,i=ethers.utils.sha256("0x"+r),c=ethers.utils.ripemd160(i).slice(2);let a;return a="mainnet"===t?"bc":"testnet"===t?"tb":"bcrt",n(a,0,c)},n=function(e,t,o){const s=[];for(let e=0;e<o.length;e+=2)s.push(parseInt(o.substr(e,2),16));const n=[t].concat(c(s,8,5,!0)),r=l(e,n),i=n.concat(r);let a=e+"1";for(const e of i)a+="qpzry9x8gf2tvdw0s3jn54khce6mua7l"[e];return a},l=function(e,t){const o=i(e).concat(t).concat([0,0,0,0,0,0]),s=1^r(o),n=[];for(let e=0;e<6;e++)n.push(s>>5*(5-e)&31);return n},r=function(e){const t=[996825010,642813549,513874426,1027748829,705979059];let o=1;for(const s of e){const e=o>>25;o=(33554431&o)<<5^s;for(let s=0;s<5;s++)e>>s&1&&(o^=t[s])}return o},i=function(e){const t=[];for(let o=0;o<e.length;o++)t.push(e.charCodeAt(o)>>5);t.push(0);for(let o=0;o<e.length;o++)t.push(31&e.charCodeAt(o));return t},c=function(e,t,o,s){let n=0,l=0;const r=[],i=(1<<o)-1;for(const s of e){if(s<0||s>>t)throw new Error("Invalid data for bit conversion");for(n=n<<t|s,l+=t;l>=o;)l-=o,r.push(n>>l&i)}if(s)l>0&&r.push(n<<o-l&i);else if(l>=t||n<<o-l&i)throw new Error("Invalid padding in bit conversion");return r};if(!e)throw new Error("targetPkpTokenId is required in parameters");if(!t)throw new Error("expectedCid is required in parameters");console.log("\n\ud83d\udd11 Step 0: Getting PKP public key from token ID..."),console.log(` \ud83c\udfaf Target PKP Token ID: ${e}`),console.log(" \ud83c\udfaf Target PKP Token ID type: "+typeof e),console.log(` \ud83c\udfaf Target PKP Token ID length: ${String(e).length}`);const a="0x487A9D096BB4B7Ac1520Cb12370e31e677B175EA";console.log(` \ud83d\udccd PKPNFT address: ${a}`);const d=ethers.utils.concat([ethers.utils.id("tokenURI(uint256)").substring(0,10),ethers.utils.defaultAbiCoder.encode(["uint256"],[e])]);console.log(` \ud83d\udce4 Calldata: ${ethers.utils.hexlify(d)}`);const g={method:"eth_call",params:[{to:a,data:ethers.utils.hexlify(d)},"latest"],id:0,jsonrpc:"2.0"};console.log(" \ud83c\udf10 Calling Yellowstone RPC for tokenURI...");const h=await fetch("https://yellowstone-rpc.litprotocol.com",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(g)});console.log(` \ud83d\udce1 RPC Response status: ${h.status}`);const u=await h.json();if(u.error)throw new Error(`RPC Error getting tokenURI: ${u.error.message}`);if(console.log(` \ud83d\udce6 tokenURI result length: ${u.result?.length||0}`),!u.result||"0x"===u.result||u.result.length<=2)throw new Error(`PKP ${e} does not exist on Yellowstone/Datil network`);const p=ethers.utils.defaultAbiCoder.decode(["string"],u.result)[0];console.log(` \ud83d\udce6 tokenURI string length: ${p.length}`);const f="data:application/json;base64,";if(!p.startsWith(f))throw new Error("Invalid tokenURI format: expected base64 data URI");const P=p.substring(f.length),b=atob(P),m=JSON.parse(b);console.log(" \ud83d\udce6 Metadata decoded successfully");const C=m.attributes.find((e=>"Public Key"===e.trait_type));if(!C||!C.value)throw new Error("Public key not found in PKP metadata");const w=C.value;console.log(` \u2705 Retrieved PKP public key: ${w.substring(0,20)}...`),console.log("\n\ud83d\udd12 PKP SECURITY VALIDATION"),console.log(" Using EXACT same logic as validatePKPSecurity test function"),console.log(" Making direct smart contract calls to verify security"),console.log(` Target PKP: ${e}`),console.log(` Expected CID: ${t}`),console.log(` Network: ${o}`);const y={PKPNFT:"0x487A9D096BB4B7Ac1520Cb12370e31e677B175EA",PKPPermissions:"0x213Db6E1446928E19588269bEF7dFc9187c4829A"};console.log("\n\ud83d\udd0d Step 1: PKP Existence Check...");const E=ethers.utils.concat([ethers.utils.id("exists(uint256)").substring(0,10),ethers.utils.defaultAbiCoder.encode(["uint256"],[e])]),I={method:"eth_call",params:[{to:y.PKPNFT,data:ethers.utils.hexlify(E)},"latest"],id:1,jsonrpc:"2.0"},T=await fetch("https://yellowstone-rpc.litprotocol.com",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(I)}),$=await T.json();if($.error)throw new Error(`RPC Error checking exists: ${$.error.message}`);const k=ethers.utils.defaultAbiCoder.decode(["bool"],$.result)[0];console.log(` PKP exists: ${k}`),console.log("\n\ud83d\udd0d Step 2: PKP Immutability Check...");const x=ethers.utils.concat([ethers.utils.id("ownerOf(uint256)").substring(0,10),ethers.utils.defaultAbiCoder.encode(["uint256"],[e])]),A={method:"eth_call",params:[{to:y.PKPNFT,data:ethers.utils.hexlify(x)},"latest"],id:2,jsonrpc:"2.0"};let R="0x0000000000000000000000000000000000000000",S=!1;if(k){const e=await fetch("https://yellowstone-rpc.litprotocol.com",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(A)}),t=await e.json();if(t.error)throw new Error(`RPC Error checking owner: ${t.error.message}`);R=ethers.utils.defaultAbiCoder.decode(["address"],t.result)[0];const o="0x000000000000000000000000000000000000dEaD";S=R.toLowerCase()===o.toLowerCase()}console.log(` PKP owner: ${R}`),console.log(` Is immutable (burned to dead address): ${S}`),console.log("\n\ud83d\udd10 Step 3: Authorization Check...");const K=ethers.utils.concat([ethers.utils.id("getPermittedActions(uint256)").substring(0,10),ethers.utils.defaultAbiCoder.encode(["uint256"],[e])]),v={method:"eth_call",params:[{to:y.PKPPermissions,data:ethers.utils.hexlify(K)},"latest"],id:3,jsonrpc:"2.0"};let D=[],N=!1,O=!1,L=null;if(k){const e=await fetch("https://yellowstone-rpc.litprotocol.com",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(v)}),o=await e.json();if(o.error)throw new Error(`RPC Error getting permitted actions: ${o.error.message}`);D=ethers.utils.defaultAbiCoder.decode(["bytes[]"],o.result)[0],N=1===D.length,L=D[0]||null,O=L?.toLowerCase()===t.toLowerCase()}console.log(` Permitted actions count: ${D.length}`),console.log(" Actions bytes:",D),console.log(` Has exactly one action: ${N}`),console.log(` Matches expected CID: ${O}`),console.log(` Expected CID: ${t}`),console.log(` Found CID: ${L||"NONE"}`);const U=k&&S&&N&&O,j={pkpExists:`${k} ${k?"\u2705":"\u274c"}`,pkpImmutable:`${S} ${S?"\u2705":"\u274c"}`,singleAction:`${N} ${N?"\u2705":"\u274c"}`,correctCID:`${O} ${O?"\u2705":"\u274c"}`,overallSecurity:`${U} ${U?"\ud83d\udd12 SECURE":"\u26a0\ufe0f INSECURE"}`};if(console.log("\n\ud83d\udd12 COMPLETE SECURITY VALIDATION RESULT:"),console.log(` PKP exists: ${j.pkpExists}`),console.log(` PKP immutable: ${j.pkpImmutable}`),console.log(` Single action: ${j.singleAction}`),console.log(` Correct CID: ${j.correctCID}`),console.log(` OVERALL SECURITY: ${j.overallSecurity}`),!U)throw new Error("PKP security validation failed - not all conditions met");console.log("\n\ud83c\udf89 PKP IS FULLY SECURE!"),console.log(" \u2705 Exists on-chain"),console.log(" \u2705 Immutable (cannot be modified)"),console.log(" \u2705 Only authorized for expected LIT Action"),console.log(" \ud83d\udd12 READY FOR PRODUCTION USE"),console.log("\n\u20bf Step 4: Deriving Bitcoin addresses from target PKP..."),console.log(` Target PKP public key: ${ethers.utils.hexlify(w).substring(0,20)}...`);const B=s(w,"mainnet"),M=s(w,"regtest");console.log(` \u2705 Mainnet address: ${B}`),console.log(` \u2705 Regtest address: ${M}`),console.log("\n\u270d\ufe0f Step 5: Signing certification with BTC addresses...");const F=`PKP::${e}::CID::${t.toLowerCase()}::MAINNET::${B}::REGTEST::${M}::SECURE`;console.log(" Certification message:",F),console.log(" Message length:",F.length);const Y=ethers.utils.arrayify(ethers.utils.hashMessage(F));console.log(` Message hash (with Ethereum prefix): ${ethers.utils.hexlify(Y)}`);const _=await Lit.Actions.signEcdsa({toSign:Y,publicKey:"0x"+globalThis.publicKey,sigName:"pkpValidation"});console.log("\u2705 PKP-CID relationship certification signature generated"),Lit.Actions.setResponse({response:JSON.stringify({success:!0,validated:{pkpTokenId:e,exists:k,immutable:S,singleAction:N,correctCid:O,expectedCid:t,foundCidHex:D[0]},bitcoinAddresses:{mainnet:B,regtest:M},validatorPkp:globalThis.publicKey,signedMessage:F,signature:_,timestamp:Date.now(),network:o})})}catch(e){console.error("\u274c PKP Validation failed:",e.message),Lit.Actions.setResponse({response:JSON.stringify({success:!1,error:e.message||e.toString(),targetPkpTokenId:globalThis.targetPkpTokenId||"unknown",validatorPkp:globalThis.publicKey||"unknown",timestamp:Date.now(),network:o})})}})();
@@ -0,0 +1 @@
1
+ acd53f3f9d1ced662461bd5d461b83826bdf6fde8275da81069acd8ade2cd76e
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "pkp-validator-datil",
3
+ "originalSize": 17290,
4
+ "minifiedSize": 9061,
5
+ "compressionRatio": 0.4759398496240601,
6
+ "hash": "acd53f3f9d1ced662461bd5d461b83826bdf6fde8275da81069acd8ade2cd76e",
7
+ "buildTime": 1768236995154,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{var e=Object.defineProperty,r=Object.getOwnPropertyDescriptor,t=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,s={};((r,t)=>{for(var o in t)e(r,o,{get:t[o],enumerable:!0})})(s,{main:()=>main});var i,c=class{constructor(e,r,t){this.mode=t,r&&Array.isArray(r)?this.sources=r:e&&Array.isArray(e)?this.sources=this.buildSources(e):this.sources=this.buildSources(),this.sources.sort(((e,r)=>e.priority-r.priority)),this.validateProviderCount()}validateProviderCount(){if("prod"===this.mode&&this.sources.length<3)throw new Error(`Production mode requires at least 3 price providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported providers: CoinGecko, Binance, Coinbase, CryptoCompare (with API key)`);if(0===this.sources.length)throw new Error("No price providers configured. At least one provider is required.")}buildSources(e){const r=[];if(!e||0===e.length)return this.getDefaultSources();let t=1;for(const o of e){switch(o.name.toLowerCase()){case"coingecko":r.push(this.createCoinGeckoSource(o.apiKey,t++));break;case"binance":r.push(this.createBinanceSource(o.apiKey,o.apiSecret,t++));break;case"coinbase":r.push(this.createCoinbaseSource(o.apiKey,t++));break;case"cryptocompare":if(!o.apiKey)throw new Error("CryptoCompare requires an API key. Please provide apiKey in priceProviders config.");r.push(this.createCryptoCompareSource(o.apiKey,t++));break;default:console.warn(`[Price Oracle] Unknown provider: ${o.name} - skipping`)}}return r}createCryptoCompareSource(e,r){return{name:"CryptoCompare",fetchPrice:async()=>{const r=new URL("https://min-api.cryptocompare.com/data/price");r.searchParams.set("fsym","BTC"),r.searchParams.set("tsyms","USDT"),r.searchParams.set("api_key",e);const t=await(async e=>{const r=new AbortController,t=setTimeout((()=>r.abort()),5e3);try{const o=await fetch(e,{signal:r.signal,headers:{Accept:"application/json"}});if(clearTimeout(t),!o.ok)throw new Error(`HTTP ${o.status} ${o.statusText}`);return await o.json()}catch(e){if(clearTimeout(t),"AbortError"===e.name)throw new Error("Request timeout after 5000ms");throw e}})(r.toString()),o=Number(t?.USDT);if(!Number.isFinite(o)||o<=0)throw new Error("Invalid CryptoCompare price payload");const s=(await Lit.Actions.broadcastAndCollect({name:"cryptoComparePrice",value:o.toString()})).map((e=>parseFloat(e)));return s.sort(((e,r)=>e-r)),s[Math.floor(s.length/2)]},priority:r}}createFetchJson(){return async(e,r)=>{const t=new AbortController,o=setTimeout((()=>t.abort()),5e3);try{const o=await fetch(e,{headers:{Accept:"application/json",...r||{}},signal:t.signal});if(!o.ok)throw new Error(`HTTP ${o.status} ${o.statusText}`);return await o.json()}finally{clearTimeout(o)}}}getDefaultSources(){return[this.createCoinGeckoSource(void 0,1),this.createBinanceSource(void 0,void 0,2),this.createCoinbaseSource(void 0,3)]}createCoinGeckoSource(e,r=1){const t=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const e=new URL("https://api.coingecko.com/api/v3/simple/price");e.searchParams.set("ids","bitcoin"),e.searchParams.set("vs_currencies","usd");const r=await t(e.toString()),o=Number(r?.bitcoin?.usd);if(!Number.isFinite(o)||o<=0)throw new Error("Invalid CoinGecko price payload");const s=(await Lit.Actions.broadcastAndCollect({name:"coinGeckoPrice",value:o.toString()})).map((e=>parseFloat(e)));return s.sort(((e,r)=>e-r)),s[Math.floor(s.length/2)]},priority:r}}createBinanceSource(e,r,t=2){const o=this.createFetchJson();return{name:"Binance",fetchPrice:async()=>{const r=new URL("https://api.binance.com/api/v3/ticker/price");r.searchParams.set("symbol","BTCUSDT");const t=e?{"X-MBX-APIKEY":e}:void 0,s=await o(r.toString(),t),i=Number(s?.price);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid Binance price payload");const c=(await Lit.Actions.broadcastAndCollect({name:"binancePrice",value:i.toString()})).map((e=>parseFloat(e)));return c.sort(((e,r)=>e-r)),c[Math.floor(c.length/2)]},priority:t}}createCoinbaseSource(e,r=3){const t=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const r=e?{"CB-ACCESS-KEY":e}:void 0,o=await t("https://api.coinbase.com/v2/prices/BTC-USD/spot",r),s=Number(o?.data?.amount);if(!Number.isFinite(s)||s<=0)throw new Error("Invalid Coinbase price payload");const i=(await Lit.Actions.broadcastAndCollect({name:"coinbasePrice",value:s.toString()})).map((e=>parseFloat(e)));return i.sort(((e,r)=>e-r)),i[Math.floor(i.length/2)]},priority:r}}async getBTCPrice(){const e=Date.now();console.log("[Price Oracle] Fetching BTC price from external sources (parallel)..."),console.log(`[Price Oracle] Start time: ${e}`);const r=new Promise(((r,t)=>{setTimeout((()=>{const r=Date.now()-e;console.error(`[Price Oracle] \u274c GLOBAL TIMEOUT after ${r}ms`),t(new Error("Price oracle global timeout after 15000ms"))}),15e3)})),t=this.sources.map((async e=>{const r=Date.now();try{console.log(`[Price Oracle] [${r}] Querying ${e.name}...`);const t=await e.fetchPrice(),o=Date.now()-r;return console.log(`[Price Oracle] \u2705 [+${o}ms] ${e.name}: $${t.toLocaleString()}`),{source:e.name,priceUSD:t}}catch(t){const o=Date.now()-r;throw console.warn(`[Price Oracle] \u26a0\ufe0f [+${o}ms] ${e.name} failed: ${t.message}`),t}}));try{console.log("[Price Oracle] Waiting for first success (race pattern)...");const o=await Promise.race([Promise.race(t.map((e=>e.catch((e=>({error:e})))))),r]);if("error"in o){console.log("[Price Oracle] First result was error, waiting for any success...");const r=Date.now(),o=new Promise(((e,t)=>{setTimeout((()=>{const e=Date.now()-r;console.error(`[Price Oracle] \u274c FALLBACK TIMEOUT after ${e}ms`),t(new Error("Price oracle fallback timeout after 10000ms"))}),1e4)})),s=await Promise.race([Promise.allSettled(t),o]),i=Date.now()-r;console.log(`[Price Oracle] Fallback completed in ${i}ms`);const c=s.find((e=>"fulfilled"===e.status));if(!c){const r=Date.now()-e;throw console.error(`[Price Oracle] All sources failed after ${r}ms`),new Error("All price sources failed")}const{source:a,priceUSD:n}=c.value,l=Date.now()-e;console.log(`[Price Oracle] Using ${a} (first successful after failures) - total time: ${l}ms`);const u=Math.round(100*n),p=1000000n*BigInt(u);return console.log(`[Price Oracle] Price with 8 decimals: ${p}`),p}const{source:s,priceUSD:i}=o,c=Date.now()-e;console.log(`[Price Oracle] Using ${s} (fastest response) - total time: ${c}ms`);const a=Math.round(100*i),n=1000000n*BigInt(a);return console.log(`[Price Oracle] Price with 8 decimals: ${n}`),n}catch(r){const t=Date.now()-e;throw console.error(`[Price Oracle] Failed after ${t}ms: ${r.message}`),new Error(`All price sources failed: ${r.message}`)}}async getBTCPriceConsensus(){console.log("[Price Oracle] Fetching BTC price with consensus...");const e=(await Promise.allSettled(this.sources.map((async e=>{const r=await e.fetchPrice();return{source:e.name,price:r}})))).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 r=e.map((e=>e.price));r.sort(((e,r)=>e-r));const t=r[Math.floor(r.length/2)],o=r[0],s=r[r.length-1]/o,i=e.filter((e=>Math.abs(e.price-t)/t<=.02));if(s>1.05&&i.length===e.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(s-1)).toFixed(1)}% spread)`);if(i.length<e.length){const r=e.filter((e=>!i.find((r=>r.source===e.source))));if(console.log(`[Price Oracle] \u26a0\ufe0f Detected ${r.length} outlier(s):`),r.forEach((e=>{const r=Math.abs(e.price-t)/t;console.log(` ${e.source}: $${e.price.toLocaleString()} (${(100*r).toFixed(1)}% deviation)`)})),i.length<2)throw new Error("Price consensus failed: insufficient valid sources after outlier removal");console.log(`[Price Oracle] \u2705 Outliers filtered, continuing with ${i.length} valid sources`);const o=i.map((e=>e.price));o.sort(((e,r)=>e-r));const s=o[Math.floor(o.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${s.toLocaleString()}`);const c=Math.round(100*s);return 1000000n*BigInt(c)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${t.toLocaleString()}`);const c=Math.round(100*t);return 1000000n*BigInt(c)}};async function main(){const e="fast"===globalThis.mode?"fast":"full",r=!0===globalThis.sign,t=globalThis.priceProviders,o=new c(t);let s,i="fast"===e?"fast":"consensus";try{s="fast"===e?await o.getBTCPrice():await o.getBTCPriceConsensus()}catch(e){s=await o.getBTCPrice(),i="fast-fallback"}const a=Math.floor(Date.now()/1e3),n={priceE8:s.toString(),timestamp:a,source:i};let l,u;if(r)try{const e=JSON.stringify(n),r=Lit.Auth?.sig?.derivedViaPublicKey,t=r?r.startsWith("0x")?r:"0x"+r:void 0,o=await Lit.Actions.signEcdsa({toSign:(new TextEncoder).encode(e),publicKey:t,sigName:"oracleSig"});l=o?.signature||void 0,u=o?.publicKey||void 0}catch{}const p={priceE8:n.priceE8,price:Number(n.priceE8)/1e8,timestamp:n.timestamp,source:n.source,signature:l,signer:u};Lit.Actions.setResponse({response:JSON.stringify(p)})}return main(),i=s,((s,i,c,a)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let n of t(i))o.call(s,n)||n===c||e(s,n,{get:()=>i[n],enumerable:!(a=r(i,n))||a.enumerable});return s})(e({},"__esModule",{value:!0}),i)})();
@@ -0,0 +1 @@
1
+ 37c45d3fa858b995d84caff1225a95b3e592d67e4de521ca376e615115fb3340
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "price-oracle",
3
+ "originalSize": 20610,
4
+ "minifiedSize": 9382,
5
+ "compressionRatio": 0.5447840853954391,
6
+ "hash": "37c45d3fa858b995d84caff1225a95b3e592d67e4de521ca376e615115fb3340",
7
+ "buildTime": 1768236995234,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{function e(e){const s=e.replace(/^0x/,"");if(64!==s.length)throw new Error(`Expected 32-byte hex string, got length ${s.length}`);const n=new Uint8Array(32);for(let e=0;e<32;e++)n[e]=parseInt(s.slice(2*e,2*e+2),16);return n}(async()=>{let s="init";try{s="0";const n=await Lit.Actions.getParams(),t=n.chain||"sepolia",i=n.publicKey||globalThis.publicKey;if(!i)throw new Error("publicKey is required for sign-only test");s="1";const o="0x1111111111111111111111111111111111111111111111111111111111111111",l=n.messageHash||o,a=l.replace(/^0x/,"");if(!/^[0-9a-fA-F]{64}$/.test(a))throw new Error(`Invalid messageHash: expected 32-byte hex string, got ${l}`);const g=i;console.log("[Sign-Only] Chain:",t),console.log("[Sign-Only] Message hash:",l),console.log("[Sign-Only] Message hash bytes length:",e(l).length),console.log("[Sign-Only] PKP publicKey:",i),console.log("[Sign-Only] PKP Ethereum address:",g),s="2",console.log("[Sign-Only] Signing message hash with PKP...");const r=await Lit.Actions.signEcdsa({toSign:e(l),publicKey:i,sigName:"paymentSignOnly"});console.log("[Sign-Only] \u2705 Signature obtained"),Lit.Actions.setResponse({response:JSON.stringify({success:!0,chain:t,messageHash:l,pkpPublicKey:i,pkpEthAddress:g,signature:r,timestamp:Date.now()})})}catch(e){console.error("[Sign-Only] \u274c Failed:",e?.message||String(e)),console.error(`[Sign-Only] Failed at step: ${s}`),Lit.Actions.setResponse({response:JSON.stringify({success:!1,error:e?.message||String(e),failedStep:s,timestamp:Date.now()})})}})()})();
@@ -0,0 +1 @@
1
+ 705db74e17e8043e50e01267381b5d7e71dcf1ab11d8bd2558d0a23b374acf5c
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "process-payment-sign-only",
3
+ "originalSize": 2706,
4
+ "minifiedSize": 1546,
5
+ "compressionRatio": 0.42867701404286773,
6
+ "hash": "705db74e17e8043e50e01267381b5d7e71dcf1ab11d8bd2558d0a23b374acf5c",
7
+ "buildTime": 1768236995252,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{var e=100;function t(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 o=(e=>(e.SEPOLIA="sepolia",e.ETHEREUM="ethereum",e.HARDHAT="hardhat",e))(o||{}),r=(e=>(e.TESTNET="testnet",e.MAINNET="mainnet",e.REGTEST="regtest",e))(r||{}),i={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},n={ethereum:1,sepolia:11155111,hardhat:31337},s={regtest:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://127.0.0.1:18443",network:"regtest",minConfirmations:1,name:"Local Regtest"},{url:"https://313561dc30c5.ngrok-free.app",network:"regtest",minConfirmations:1,name:"Ngrok Bitcoin Tunnel"}],testnet:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"}],mainnet:[{url:"https://blockstream.info/api",network:"mainnet",minConfirmations:6,name:"Blockstream Mainnet"},{url:"https://mempool.space/api",network:"mainnet",minConfirmations:6,name:"Mempool.space Mainnet"}]};var a=class{constructor(e,t,o){this.mode=o,t&&Array.isArray(t)?this.sources=t:e&&Array.isArray(e)?this.sources=this.buildSources(e):this.sources=this.buildSources(),this.sources.sort(((e,t)=>e.priority-t.priority)),this.validateProviderCount()}validateProviderCount(){if("prod"===this.mode&&this.sources.length<3)throw new Error(`Production mode requires at least 3 price providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported providers: CoinGecko, Binance, Coinbase, CryptoCompare (with API key)`);if(0===this.sources.length)throw new Error("No price providers configured. At least one provider is required.")}buildSources(e){const t=[];if(!e||0===e.length)return this.getDefaultSources();let o=1;for(const r of e){switch(r.name.toLowerCase()){case"coingecko":t.push(this.createCoinGeckoSource(r.apiKey,o++));break;case"binance":t.push(this.createBinanceSource(r.apiKey,r.apiSecret,o++));break;case"coinbase":t.push(this.createCoinbaseSource(r.apiKey,o++));break;case"cryptocompare":if(!r.apiKey)throw new Error("CryptoCompare requires an API key. Please provide apiKey in priceProviders config.");t.push(this.createCryptoCompareSource(r.apiKey,o++));break;default:console.warn(`[Price Oracle] Unknown provider: ${r.name} - skipping`)}}return t}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 o=await(async e=>{const t=new AbortController,o=setTimeout((()=>t.abort()),5e3);try{const r=await fetch(e,{signal:t.signal,headers:{Accept:"application/json"}});if(clearTimeout(o),!r.ok)throw new Error(`HTTP ${r.status} ${r.statusText}`);return await r.json()}catch(e){if(clearTimeout(o),"AbortError"===e.name)throw new Error("Request timeout after 5000ms");throw e}})(t.toString()),r=Number(o?.USDT);if(!Number.isFinite(r)||r<=0)throw new Error("Invalid CryptoCompare price payload");const i=(await Lit.Actions.broadcastAndCollect({name:"cryptoComparePrice",value:r.toString()})).map((e=>parseFloat(e)));return i.sort(((e,t)=>e-t)),i[Math.floor(i.length/2)]},priority:t}}createFetchJson(){return async(e,t)=>{const o=new AbortController,r=setTimeout((()=>o.abort()),5e3);try{const r=await fetch(e,{headers:{Accept:"application/json",...t||{}},signal:o.signal});if(!r.ok)throw new Error(`HTTP ${r.status} ${r.statusText}`);return await r.json()}finally{clearTimeout(r)}}}getDefaultSources(){return[this.createCoinGeckoSource(void 0,1),this.createBinanceSource(void 0,void 0,2),this.createCoinbaseSource(void 0,3)]}createCoinGeckoSource(e,t=1){const o=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const e=new URL("https://api.coingecko.com/api/v3/simple/price");e.searchParams.set("ids","bitcoin"),e.searchParams.set("vs_currencies","usd");const t=await o(e.toString()),r=Number(t?.bitcoin?.usd);if(!Number.isFinite(r)||r<=0)throw new Error("Invalid CoinGecko price payload");const i=(await Lit.Actions.broadcastAndCollect({name:"coinGeckoPrice",value:r.toString()})).map((e=>parseFloat(e)));return i.sort(((e,t)=>e-t)),i[Math.floor(i.length/2)]},priority:t}}createBinanceSource(e,t,o=2){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 o=e?{"X-MBX-APIKEY":e}:void 0,i=await r(t.toString(),o),n=Number(i?.price);if(!Number.isFinite(n)||n<=0)throw new Error("Invalid Binance price payload");const s=(await Lit.Actions.broadcastAndCollect({name:"binancePrice",value:n.toString()})).map((e=>parseFloat(e)));return s.sort(((e,t)=>e-t)),s[Math.floor(s.length/2)]},priority:o}}createCoinbaseSource(e,t=3){const o=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const t=e?{"CB-ACCESS-KEY":e}:void 0,r=await o("https://api.coinbase.com/v2/prices/BTC-USD/spot",t),i=Number(r?.data?.amount);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid Coinbase price payload");const n=(await Lit.Actions.broadcastAndCollect({name:"coinbasePrice",value:i.toString()})).map((e=>parseFloat(e)));return n.sort(((e,t)=>e-t)),n[Math.floor(n.length/2)]},priority:t}}async getBTCPrice(){const e=Date.now();console.log("[Price Oracle] Fetching BTC price from external sources (parallel)..."),console.log(`[Price Oracle] Start time: ${e}`);const t=new Promise(((t,o)=>{setTimeout((()=>{const t=Date.now()-e;console.error(`[Price Oracle] \u274c GLOBAL TIMEOUT after ${t}ms`),o(new Error("Price oracle global timeout after 15000ms"))}),15e3)})),o=this.sources.map((async e=>{const t=Date.now();try{console.log(`[Price Oracle] [${t}] Querying ${e.name}...`);const o=await e.fetchPrice(),r=Date.now()-t;return console.log(`[Price Oracle] \u2705 [+${r}ms] ${e.name}: $${o.toLocaleString()}`),{source:e.name,priceUSD:o}}catch(o){const r=Date.now()-t;throw console.warn(`[Price Oracle] \u26a0\ufe0f [+${r}ms] ${e.name} failed: ${o.message}`),o}}));try{console.log("[Price Oracle] Waiting for first success (race pattern)...");const r=await Promise.race([Promise.race(o.map((e=>e.catch((e=>({error:e})))))),t]);if("error"in r){console.log("[Price Oracle] First result was error, waiting for any success...");const t=Date.now(),r=new Promise(((e,o)=>{setTimeout((()=>{const e=Date.now()-t;console.error(`[Price Oracle] \u274c FALLBACK TIMEOUT after ${e}ms`),o(new Error("Price oracle fallback timeout after 10000ms"))}),1e4)})),i=await Promise.race([Promise.allSettled(o),r]),n=Date.now()-t;console.log(`[Price Oracle] Fallback completed in ${n}ms`);const s=i.find((e=>"fulfilled"===e.status));if(!s){const t=Date.now()-e;throw console.error(`[Price Oracle] All sources failed after ${t}ms`),new Error("All price sources failed")}const{source:a,priceUSD:c}=s.value,l=Date.now()-e;console.log(`[Price Oracle] Using ${a} (first successful after failures) - total time: ${l}ms`);const p=Math.round(100*c),u=1000000n*BigInt(p);return console.log(`[Price Oracle] Price with 8 decimals: ${u}`),u}const{source:i,priceUSD:n}=r,s=Date.now()-e;console.log(`[Price Oracle] Using ${i} (fastest response) - total time: ${s}ms`);const a=Math.round(100*n),c=1000000n*BigInt(a);return console.log(`[Price Oracle] Price with 8 decimals: ${c}`),c}catch(t){const o=Date.now()-e;throw console.error(`[Price Oracle] Failed after ${o}ms: ${t.message}`),new Error(`All price sources failed: ${t.message}`)}}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 o=t[Math.floor(t.length/2)],r=t[0],i=t[t.length-1]/r,n=e.filter((e=>Math.abs(e.price-o)/o<=.02));if(i>1.05&&n.length===e.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(i-1)).toFixed(1)}% spread)`);if(n.length<e.length){const t=e.filter((e=>!n.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-o)/o;console.log(` ${e.source}: $${e.price.toLocaleString()} (${(100*t).toFixed(1)}% deviation)`)})),n.length<2)throw new Error("Price consensus failed: insufficient valid sources after outlier removal");console.log(`[Price Oracle] \u2705 Outliers filtered, continuing with ${n.length} valid sources`);const r=n.map((e=>e.price));r.sort(((e,t)=>e-t));const i=r[Math.floor(r.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${i.toLocaleString()}`);const s=Math.round(100*i);return 1000000n*BigInt(s)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${o.toLocaleString()}`);const s=Math.round(100*o);return 1000000n*BigInt(s)}};function c(){const t=Math.floor(Date.now()/1e3);return o=t,Math.floor(o/e)*e;var o}0,0;var l=class{stepStart(e,t){}stepEnd(e,t){}log(e,t){}warn(e,t){}error(e,t){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},p=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(),o=Math.random().toString(36).substring(2,9);this.executionId=`exec_${t}_${o}`,this.logHeader()}logHeader(){console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution started`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`);void 0!==globalThis.litNodeContext?(console.log(`[${this.actionName}] Context: LIT Node`),console.log(`[${this.actionName}] Node: ${globalThis.litNodeContext?.nodeAddress||"unknown"}`)):console.log(`[${this.actionName}] Context: Browser/Test`),console.log(`[${this.actionName}] ========================================`)}stepStart(e,t){this.stepStartTimes.set(e,Date.now());const o=Date.now()-this.executionStartTime,r=this.TIMEOUT_MS-o;console.log(`[Step ${e}] ${t}...`),console.log(`[Step ${e}] Elapsed: ${o}ms | Remaining: ${r}ms`)}stepEnd(e,t=5e3){const o=this.stepStartTimes.get(e);if(!o)return console.warn(`[Step ${e}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const r=Date.now()-o;this.stepDurations.set(e,r),console.log(`[Step ${e}] Duration: ${r}ms`),r>t&&console.warn(`\u26a0\ufe0f [Step ${e}] Took ${r}ms (> ${t}ms threshold)`);const i=Date.now()-this.executionStartTime,n=this.TIMEOUT_MS-i;n<5e3&&console.warn(`\u26a0\ufe0f [Step ${e}] Time remaining: ${n}ms (approaching timeout)`)}log(e,t){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,o]of this.stepDurations.entries()){const r=(o/e*100).toFixed(1);console.log(`[${this.actionName}] Step ${t}: ${o}ms (${r}%)`)}}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}},u=class{static create(e="LIT Action",t){return t??globalThis.debugAction??!1?new p(e):new l}},d=1000000000000n,g=100000000000000n;(async()=>{try{fetch("http://127.0.0.1:7242/ingest/2c6a6938-b207-41ef-a45b-0fff11f4ff23",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"process-payment-validator.ts:87",message:"PAYMENT LIT Action START",data:{hasPublicKey:!!globalThis.publicKey,publicKeyPrefix:globalThis.publicKey?.substring(0,10),hasPkpTokenId:!!globalThis.pkpTokenId,positionId:globalThis.positionId?.substring(0,20),paymentAmount:globalThis.paymentAmount},timestamp:Date.now(),sessionId:"debug-session",runId:"run1",hypothesisId:"PAYMENT_START"})}).catch((()=>{}))}catch(e){}const e=u.create("Process Payment Validator");e.log("0","========================================"),e.log("0","DEBUG: Received globalThis parameters:"),e.log("0","globalThis.contractAddresses:",JSON.stringify(globalThis.contractAddresses)),e.log("0","globalThis.positionId:",globalThis.positionId),e.log("0","globalThis.paymentAmount:",globalThis.paymentAmount),e.log("0","globalThis.priceProviders:",JSON.stringify(globalThis.priceProviders)),e.log("0","globalThis.priceProviders type:",typeof globalThis.priceProviders),e.log("0","globalThis.priceProviders isArray:",Array.isArray(globalThis.priceProviders)),e.log("0","All globalThis keys:",Object.keys(globalThis).filter((e=>!e.startsWith("_")&&"console"!==e&&"require"!==e&&"module"!==e&&"exports"!==e&&"process"!==e&&"Buffer"!==e&&"setTimeout"!==e&&"clearTimeout"!==e&&"setInterval"!==e&&"clearInterval"!==e))),e.log("0","========================================");let l,p,h,m="0";try{m="0a",e.log("0a","Validating configuration...");const u=globalThis.chain,f=globalThis.bitcoinProviderUrl,y=globalThis.positionId,b=globalThis.paymentAmount;if(!u)throw new Error('Missing required parameter: "chain"');if(!f)throw new Error('Missing required parameter: "bitcoinProviderUrl"');if(!y)throw new Error('Missing required parameter: "positionId"');if(!b)throw new Error('Missing required parameter: "paymentAmount"');const w=BigInt(b);if(w<=0n)throw new Error(`Payment amount must be positive, got: ${w.toString()} wei`);let T,P,S;m="0b","sepolia"===u||"ethereum"===u?(P=function(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(o).join(", ")}`)}(u),S=function(e){return i[e]}(P),T=function(e){for(const t of Object.values(r)){const o=s[t].find((t=>t.url===e));if(o)return o}const t=Object.entries(s).flatMap((([e,t])=>t.map((t=>`${e}: ${t.name} (${t.url})`)))).join(", ");throw new Error(`Bitcoin provider not approved. Provided URL: ${e}. Approved providers: ${t}`)}(f)):(P=u,S="sepolia"===u?"testnet":"regtest",T={name:"Custom Provider",url:f,minConfirmations:1,network:S}),e.log("0",` \u2705 Chain: ${P}`),e.log("0",` \u2705 Bitcoin Network: ${S}`),e.log("0",` \u2705 Payment Amount: ${w.toString()} wei`),m="0c",e.log("0c","Loading contract addresses...");const $=globalThis.contractAddresses;if($&&"object"==typeof $){if(p=$.PositionManager,h=$.LoanOperationsManagerModule,!p||!h)throw new Error("Missing required contract addresses in contractAddresses object");e.log("0"," \u2705 Contract addresses loaded from parameters (dev mode)")}else{if(p="0x0000000000000000000000000000000000000000",h="0x0000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000"===p||"0x0000000000000000000000000000000000"===h)throw new Error('Production mode not ready: Contract addresses have not been set. For testing, use mode="dev" and pass contract addresses via contracts parameter.');e.log("0"," \u2705 Contract addresses loaded from production constants")}m="0d",e.log("0d","Initializing modules...");const I=globalThis.customRpcUrl||globalThis.rpcUrl,E=globalThis.customRpcUrl,v=function(e){return n[e]}(P);if(E||"hardhat"===u||"sepolia"===u){const t="hardhat"===u?{chainId:1337,name:"hardhat"}:"sepolia"===u?{chainId:11155111,name:"sepolia"}:{chainId:v,name:u};e.log("0",` Using StaticJsonRpcProvider for ${u} to skip network detection`),e.log("0"," Network config:",t),l=new ethers.providers.StaticJsonRpcProvider(I,t)}else l=new ethers.providers.JsonRpcProvider(I);e.log("0","[DEBUG] Before PriceOracleModule init:"),e.log("0"," globalThis.priceProviders:",JSON.stringify(globalThis.priceProviders)),e.log("0"," typeof globalThis.priceProviders:",typeof globalThis.priceProviders),e.log("0"," Array.isArray(globalThis.priceProviders):",Array.isArray(globalThis.priceProviders));const C=new a(globalThis.priceProviders,void 0);e.log("0","[DEBUG] After PriceOracleModule init:"),e.log("0"," PriceOracleModule sources count:",C.sources?.length||0),e.log("0"," PriceOracleModule first source:",C.sources?.[0]?.name||"none"),m="1",e.log("1","Fetching BTC price via PriceOracleModule...");const A=await C.getBTCPrice();if(A<d||A>g)throw new Error(`Bitcoin price ${A} is outside acceptable range (1000000000000 - 100000000000000). This may indicate oracle manipulation or stale price data. Please try again later.`);e.log("0",` \u2705 BTC price validation passed: $${(Number(A)/1e8).toFixed(2)}`),m="2",e.log("2","Validating position state...");const k=y.startsWith("0x")&&66===y.length?y:"0x"+y.padStart(64,"0"),D=l,N=new ethers.Contract(p,[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],D),M=await N.core(),O="0x"+ethers.utils.id("getPositionDetails(bytes32)").substring(2,10)+ethers.utils.defaultAbiCoder.encode(["bytes32"],[k]).substring(2),B=await D.call({to:M,data:O}),x=ethers.utils.defaultAbiCoder.decode([{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"}],B)[0],U=t(x.status);if(!["PENDING_MINT","ACTIVE","EXPIRED","LIQUIDATABLE"].includes(U))throw new Error(`Invalid position status for payment: ${U}. Must be PENDING_MINT, ACTIVE, EXPIRED, or LIQUIDATABLE.`);e.log("0",` \u2705 Position status valid for payment: ${t(x.status)}`),m="3",e.log("3","Validating payment amount...");const L=BigInt(x.ucdDebt.toString());if(w>L)throw new Error(`Payment amount ${w.toString()} wei exceeds total debt ${L.toString()} wei. Payment amount must be less than or equal to total debt.`);e.log("0",` \u2705 Payment amount valid: ${w.toString()} wei`),e.log("0",` - Total debt: ${L.toString()} wei`),m="3.25",e.log("0","[Step 3.25] Circuit breaker check..."),e.log("0"," \u26a0\ufe0f Circuit breaker validation deferred to contract (state-changing function)"),e.log("0"," \u2705 Payment amount within bounds for circuit breaker check"),m="3.5",e.log("0","[Step 3.5] Validating UCD balance and allowance...");const R=[{inputs:[],name:"ucdToken",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],F=new ethers.Contract(h,R,l),K=await F.ucdToken();e.log("0",` UCD Token Address: ${K}`);const G=x.borrower;if(!G)throw new Error("Borrower address not found in position snapshot");e.log("0",` Borrower Address: ${G}`);const _=[{inputs:[{internalType:"address",name:"account",type:"address"}],name:"balanceOf",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"},{inputs:[{internalType:"address",name:"owner",type:"address"},{internalType:"address",name:"spender",type:"address"}],name:"allowance",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],j=new ethers.Contract(K,_,l),[H,J]=await Promise.all([j.balanceOf(G),j.allowance(G,p)]),q=BigInt(H.toString()),V=BigInt(J.toString());if(e.log("0",` Borrower UCD Balance: ${q.toString()} wei`),e.log("0",` Borrower UCD Allowance: ${V.toString()} wei`),e.log("0",` Required Payment Amount: ${w.toString()} wei`),q<w)throw new Error(`Insufficient UCD balance: borrower has ${q.toString()} wei but needs ${w.toString()} wei to make payment. Please ensure sufficient UCD tokens are available before attempting payment.`);if(e.log("0"," \u2705 Borrower has sufficient UCD balance"),V<w)throw new Error(`Insufficient UCD allowance: borrower has approved ${V.toString()} wei but needs ${w.toString()} wei to make payment. Please approve PositionManager (${p}) to spend UCD tokens before attempting payment.`);e.log("0"," \u2705 Borrower has sufficient UCD allowance"),m="4",e.log("4","Collateral ratio validation skipped for repayment (handled off-chain / by business rules)"),m="5",e.log("5","Building authorization message...");const Y=c()+10,W=ethers.utils.defaultAbiCoder.encode(["bytes32","uint256","uint256","uint256"],[k,Y,A.toString(),w.toString()]),Q=ethers.utils.keccak256(W),X=globalThis.publicKey;if(!X)throw new Error('Missing required parameter: "publicKey"');if("string"!=typeof X||0===X.length)throw new Error("Invalid publicKey: expected non-empty string, got "+typeof X);const z=X;e.log("0",`[Step 6] PublicKey length: ${z.length}, starts with: ${z.substring(0,10)}...`),m="6",e.log("6","Signing authorization..."),e.log("0",` Message hash: ${Q}`),e.log("0",` Message hash bytes length: ${ethers.utils.arrayify(Q).length}`),e.log("0",` Public key: ${z.substring(0,20)}...`),e.log("0",` Public key length: ${z.length}`),e.log("0","======= PKP AUTHORIZATION DIAGNOSTICS ======="),e.log("0","globalThis.publicKey:",z?`${z.substring(0,20)}...`:"MISSING"),e.log("0","globalThis.publicKey length:",z?.length||0),e.log("0","globalThis.publicKey has 0x prefix:",z?.startsWith("0x")||!1);const Z=globalThis.pkpTokenId;e.log("0","globalThis.pkpTokenId:",Z||"NOT SET"),Z&&(e.log("0","globalThis.pkpTokenId type:",typeof Z),e.log("0","globalThis.pkpTokenId value:",Z),e.log("0","NOTE: pkpTokenId is used for session signature scoping in LitOps, not as a signEcdsa parameter."));const ee=globalThis.positionId;e.log("0","globalThis.positionId:",ee||"NOT SET");const te=globalThis.ipfsId;e.log("0","globalThis.ipfsId:",te||"NOT SET");try{const t=ethers.utils.arrayify("0x"+z),o=ethers.utils.computeAddress(t);e.log("0","Computed ETH Address from publicKey:",o)}catch(t){e.log("0","Could not compute ETH address from publicKey:",t instanceof Error?t.message:String(t))}e.log("0","Message hash to sign:",Q),e.log("0","Signature name: paymentAuth"),e.log("0","============================================="),fetch("http://127.0.0.1:7242/ingest/2c6a6938-b207-41ef-a45b-0fff11f4ff23",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"process-payment-validator.ts:510",message:"PAYMENT signEcdsa BEFORE",data:{publicKeyLength:z.length,publicKeyPrefix:z.substring(0,10),has0xPrefix:z.startsWith("0x"),messageHashLength:ethers.utils.arrayify(Q).length,sigName:"paymentAuth",hasPkpTokenId:!!Z,pkpTokenId:Z||"NOT_SET"},timestamp:Date.now(),sessionId:"debug-session",runId:"run1",hypothesisId:"PAYMENT_SIGN"})}).catch((()=>{}));const oe=globalThis.pkpTokenId;let re;try{"pkpTokenId"in globalThis&&delete globalThis.pkpTokenId,re=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(Q),publicKey:z,sigName:"paymentAuth"})}finally{void 0!==oe&&(globalThis.pkpTokenId=oe)}fetch("http://127.0.0.1:7242/ingest/2c6a6938-b207-41ef-a45b-0fff11f4ff23",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"process-payment-validator.ts:517",message:"PAYMENT signEcdsa AFTER",data:{hasSignature:!!re,signatureLength:re?.length},timestamp:Date.now(),sessionId:"debug-session",runId:"run1",hypothesisId:"PAYMENT_SIGN"})}).catch((()=>{})),e.log("0"," \u2705 Signature obtained: "+(re?"present":"missing")),e.log("0","\u2705 Complete"),Lit.Actions.setResponse({response:JSON.stringify({approved:!0,positionId:k,paymentAmount:w.toString(),btcPrice:A.toString(),signature:re,timestamp:Y,validatorPkp:z})})}catch(e){console.error("[Process Payment Validator] \u274c Failed:",e.message),console.error(`[Process Payment Validator] Failed at step: ${m}`),Lit.Actions.setResponse({response:JSON.stringify({approved:!1,reason:e.message||e.toString(),failedStep:m,positionId:globalThis.positionId,timestamp:Date.now()})})}})()})();
@@ -0,0 +1 @@
1
+ 302a55329e8829c9d96740b3b081a079f297bec99af3431bb99fd06815877f79
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "process-payment-validator",
3
+ "originalSize": 49087,
4
+ "minifiedSize": 25405,
5
+ "compressionRatio": 0.48244952838837163,
6
+ "hash": "302a55329e8829c9d96740b3b081a079f297bec99af3431bb99fd06815877f79",
7
+ "buildTime": 1768236995410,
8
+ "version": "0.1.0"
9
+ }
@@ -0,0 +1 @@
1
+ var _LIT_ACTION_=(()=>{var t=100000000n,e=1000000000000000000n,o=100;function i(t){switch(t){case 0:return"PENDING_DEPOSIT";case 1:return"PENDING_MINT";case 2:return"ACTIVE";case 3:return"EXPIRED";case 4:return"LIQUIDATABLE";case 5:return"LIQUIDATED";case 6:return"REPAID";case 7:return"CLOSED";default:return`UNKNOWN(${t})`}}var r=(t=>(t.SEPOLIA="sepolia",t.ETHEREUM="ethereum",t.HARDHAT="hardhat",t))(r||{}),n=(t=>(t.TESTNET="testnet",t.MAINNET="mainnet",t.REGTEST="regtest",t))(n||{}),a={sepolia:"testnet",ethereum:"mainnet",hardhat:"regtest"},s={ethereum:1,sepolia:11155111,hardhat:31337},c={regtest:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"regtest",minConfirmations:1,name:"Diamond Hands"},{url:"http://127.0.0.1:18443",network:"regtest",minConfirmations:1,name:"Local Regtest"},{url:"https://313561dc30c5.ngrok-free.app",network:"regtest",minConfirmations:1,name:"Ngrok Bitcoin Tunnel"}],testnet:[{url:"https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"},{url:"http://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora",network:"testnet",minConfirmations:1,name:"Diamond Hands"}],mainnet:[{url:"https://blockstream.info/api",network:"mainnet",minConfirmations:6,name:"Blockstream Mainnet"},{url:"https://mempool.space/api",network:"mainnet",minConfirmations:6,name:"Mempool.space Mainnet"}]};function l(t){const e=t.toLowerCase().trim();if("sepolia"===e)return"sepolia";if("ethereum"===e||"mainnet"===e)return"ethereum";if("hardhat"===e)return"hardhat";throw new Error(`Unsupported EVM chain: "${t}". Supported chains: ${Object.values(r).join(", ")}`)}function u(t){return a[t]}function d(t){return s[t]}function h(t){return Math.floor(t/o)*o}async function p(){const t=Math.floor(Date.now()/1e3),e=t%o;if(!function(t){const e=(t??Math.floor(Date.now()/1e3))%o;return e>=92||e<8}(t))return;let i;i=e>=92?100-e+8:8-e,console.log(`[Quantum] Waiting ${i}s for safe moment (current time in quantum: ${e}s)`),await new Promise((t=>setTimeout(t,1e3*i))),console.log(`[Quantum] Safe quantum ready: ${h(Math.floor(Date.now()/1e3))}`)}0,0;var g=class{static verifyAmountPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.amount||null===t.amount)throw new Error("auth.amount is required");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");if(!t.mode)throw new Error("auth.mode is required");if("dev"!==t.mode&&"prod"!==t.mode)throw new Error(`auth.mode must be "dev" or "prod", got: ${t.mode}`);return t}static async recoverSigner(t,e){try{const o=ethers.utils.arrayify(t),i=ethers.utils.hashMessage(o);return ethers.utils.recoverAddress(i,e)}catch(t){throw console.error("[Authorization] Signature recovery failed:",t),new Error(`Failed to recover signer: ${t instanceof Error?t.message:String(t)}`)}}static async verifyActionAuthorization(t,e,o){await p();try{!function(t,e){const o=h(e??Math.floor(Date.now()/1e3)),i=h(t);if(i!==o)throw new Error(`[Quantum] Invalid timestamp: signature from quantum ${i}, current quantum is ${o}`)}(t.timestamp)}catch(t){return console.error("[Authorization] Quantum timestamp validation failed:",t),!1}if(!t.mode)return console.error("[Authorization] Missing mode field"),!1;if("dev"!==t.mode&&"prod"!==t.mode)return console.error(`[Authorization] Invalid mode: ${t.mode}, must be "dev" or "prod"`),!1;if("prod"===t.mode)return console.error('[Authorization] Production mode not yet supported - use mode="dev"'),!1;const i=e();console.log("[Authorization] Message hash:",i),console.log("[Authorization] Recovering signer from signature..."),console.log(" signature:",t.signature);const r=await this.recoverSigner(i,t.signature);console.log(" recoveredAddress:",r),console.log("[Authorization] Checking authorization:"),console.log(" recoveredAddress:",r),console.log(" recoveredAddress (lowercase):",r.toLowerCase());const n=o(r);return console.log(" match:",n?"\u2705 YES":"\u274c NO"),n||console.error("[Authorization] Not authorized:",`\n Recovered: ${r}`),n}static async verifyAmountPositionActionAuthorization(t,e){return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action)),o="string"==typeof t.amount?BigInt(t.amount):t.amount;console.log("[Authorization] Building message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" amount (string):",t.amount),console.log(" amount (BigInt):",o.toString()),console.log(" action:",t.action),console.log(" actionHash:",e);const i=[t.positionId,t.timestamp,t.chainId,o.toString(),e],r=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],i);return console.log(" messageHash (solidityKeccak256):",r),r}),(t=>t.toLowerCase()===e.toLowerCase()))}static async verifyMintAuthorization(t,e){return"mint-ucd"!==t.action?(console.error(`[Authorization] Invalid action for mint: expected "mint-ucd", got "${t.action}"`),!1):this.verifyAmountPositionActionAuthorization(t,e)}static async verifyWithdrawAuthorization(t,e){if("withdraw-btc"!==t.action)return console.error(`[Authorization] Invalid action for withdrawal: expected "withdraw-btc", got "${t.action}"`),!1;return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action)),o="string"==typeof t.amount?BigInt(t.amount):t.amount;console.log("[Authorization] Building withdrawal message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" amount (string):",t.amount),console.log(" amount (BigInt):",o.toString()),console.log(" destinationAddress:",t.destinationAddress),console.log(" action:",t.action),console.log(" actionHash:",e);const i=[t.positionId,t.timestamp,t.chainId,o.toString(),t.destinationAddress,e],r=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","string","bytes32"],i);return console.log(" messageHash (solidityKeccak256):",r),r}),(t=>t.toLowerCase()===e.toLowerCase()))}static verifyTermPositionActionAuthorizationStructure(t){if(!t||"object"!=typeof t)throw new Error('Missing or invalid "auth" parameter - must be an object');if(!t.positionId)throw new Error("auth.positionId is required");if("number"!=typeof t.chainId)throw new Error("auth.chainId must be a number");if("number"!=typeof t.timestamp)throw new Error("auth.timestamp must be a number");if(void 0===t.newTerm||null===t.newTerm)throw new Error("auth.newTerm is required");if("number"!=typeof t.newTerm)throw new Error("auth.newTerm must be a number");if(!t.action)throw new Error("auth.action is required");if(!t.signature)throw new Error("auth.signature is required");if(!t.mode)throw new Error("auth.mode is required");if("dev"!==t.mode&&"prod"!==t.mode)throw new Error(`auth.mode must be "dev" or "prod", got: ${t.mode}`);return t}static async verifyTermPositionActionAuthorization(t,e){return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action));console.log("[Authorization] Building term message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" newTerm:",t.newTerm),console.log(" action:",t.action),console.log(" actionHash:",e);const o=[t.positionId,t.timestamp,t.chainId,t.newTerm,e],i=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","uint256","bytes32"],o);return console.log(" messageHash (solidityKeccak256):",i),i}),(t=>t.toLowerCase()===e.toLowerCase()))}static async verifyExtendAuthorization(t,e){return"extend-position"!==t.action?(console.error(`[Authorization] Invalid action for extension: expected "extend-position", got "${t.action}"`),!1):this.verifyTermPositionActionAuthorization(t,e)}static async verifyBtcExecutionAuthorization(t,e){if("execute-btc-withdrawal"!==t.action)return console.error(`[Authorization] Invalid action for BTC execution: expected "execute-btc-withdrawal", got "${t.action}"`),!1;return this.verifyActionAuthorization(t,(()=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.action));console.log("[Authorization] Building BTC execution message hash:"),console.log(" positionId:",t.positionId),console.log(" timestamp:",t.timestamp),console.log(" chainId:",t.chainId),console.log(" utxoIdentifier:",t.utxoIdentifier),console.log(" networkFee:",t.networkFee),console.log(" action:",t.action),console.log(" actionHash:",e);const o=[t.positionId,t.timestamp,t.chainId,t.utxoIdentifier,t.networkFee,e],i=ethers.utils.solidityKeccak256(["bytes32","uint256","uint256","string","uint256","bytes32"],o);return console.log(" messageHash (solidityKeccak256):",i),i}),(t=>t.toLowerCase()===e.toLowerCase()))}},m=class{stepStart(t,e){}stepEnd(t,e){}log(t,e){}warn(t,e){}error(t,e){}getElapsed(){return 0}getRemaining(){return 3e4}summary(){}getExecutionId(){return""}isDebugEnabled(){return!1}},w=class{constructor(t){this.stepStartTimes=new Map,this.stepDurations=new Map,this.TIMEOUT_MS=3e4,this.actionName=t,this.executionStartTime=Date.now();const e=Date.now(),o=Math.random().toString(36).substring(2,9);this.executionId=`exec_${e}_${o}`,this.logHeader()}logHeader(){console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution started`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`);void 0!==globalThis.litNodeContext?(console.log(`[${this.actionName}] Context: LIT Node`),console.log(`[${this.actionName}] Node: ${globalThis.litNodeContext?.nodeAddress||"unknown"}`)):console.log(`[${this.actionName}] Context: Browser/Test`),console.log(`[${this.actionName}] ========================================`)}stepStart(t,e){this.stepStartTimes.set(t,Date.now());const o=Date.now()-this.executionStartTime,i=this.TIMEOUT_MS-o;console.log(`[Step ${t}] ${e}...`),console.log(`[Step ${t}] Elapsed: ${o}ms | Remaining: ${i}ms`)}stepEnd(t,e=5e3){const o=this.stepStartTimes.get(t);if(!o)return console.warn(`[Step ${t}] \u26a0\ufe0f stepEnd called without matching stepStart`),void 0;const i=Date.now()-o;this.stepDurations.set(t,i),console.log(`[Step ${t}] Duration: ${i}ms`),i>e&&console.warn(`\u26a0\ufe0f [Step ${t}] Took ${i}ms (> ${e}ms threshold)`);const r=Date.now()-this.executionStartTime,n=this.TIMEOUT_MS-r;n<5e3&&console.warn(`\u26a0\ufe0f [Step ${t}] Time remaining: ${n}ms (approaching timeout)`)}log(t,e){console.log(`[Step ${t}] ${e}`)}warn(t,e){console.warn(`\u26a0\ufe0f [Step ${t}] ${e}`)}error(t,e){console.error(`\u274c [Step ${t}] ${e}`)}getElapsed(){return Date.now()-this.executionStartTime}getRemaining(){return this.TIMEOUT_MS-this.getElapsed()}summary(){const t=Date.now()-this.executionStartTime,e=this.TIMEOUT_MS-t;if(console.log(`[${this.actionName}] ========================================`),console.log(`[${this.actionName}] Execution Summary`),console.log(`[${this.actionName}] Execution ID: ${this.executionId}`),console.log(`[${this.actionName}] Total time: ${(t/1e3).toFixed(2)}s`),console.log(`[${this.actionName}] Time remaining: ${e}ms`),this.stepDurations.size>0){console.log(`[${this.actionName}] Step breakdown:`);for(const[e,o]of this.stepDurations.entries()){const i=(o/t*100).toFixed(1);console.log(`[${this.actionName}] Step ${e}: ${o}ms (${i}%)`)}}t>2e4&&console.warn(`\u26a0\ufe0f [${this.actionName}] Slow execution: ${(t/1e3).toFixed(2)}s (${e}ms remaining)`),e<5e3&&console.warn(`\u26a0\ufe0f [${this.actionName}] CRITICAL: Only ${e}ms remaining before timeout!`),console.log(`[${this.actionName}] ========================================`)}getExecutionId(){return this.executionId}isDebugEnabled(){return!0}},f=class{static create(t="LIT Action",e){return e??globalThis.debugAction??!1?new w(t):new m}},y=class{constructor(t){this.config=t,this.timeout=t.timeout||15e3,this.logger=f.create("BitcoinDataProvider")}async getCurrentBlockHeight(){if(this.logger.stepStart("0","getCurrentBlockHeight"),this.config.rpcHelper){const t=await this.config.rpcHelper.getBlockCount();return this.logger.stepEnd("0"),t}const t=new AbortController,e=setTimeout((()=>t.abort()),this.timeout);try{const o=await fetch(`${this.config.providerUrl}/blocks/tip/height`,{signal:t.signal,headers:{Accept:"text/plain","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(e),!o.ok)throw new Error(`Failed to fetch block height: ${o.status} ${o.statusText}`);const i=await o.text(),r=parseInt(i.trim(),10);if(isNaN(r))throw new Error(`Invalid block height response: ${i}`);return this.logger.stepEnd("0"),r}catch(t){if(clearTimeout(e),this.logger.stepEnd("0"),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOs(t){this.logger.log("0",`Original address: "${t}"`);const e=this.stripNetworkPrefix(t);if(this.logger.log("0",`Cleaned address: "${e}"`),this.config.rpcHelper)return await this.fetchUTXOsFromRPC(e);try{return await this.fetchUTXOsFromProvider(this.config.providerUrl,e)}catch(t){if(this.logger.warn("0",`Primary provider failed: ${t.message}`),this.config.fallbackProviders&&this.config.fallbackProviders.length>0){const t=[...this.config.fallbackProviders].sort(((t,e)=>t.priority-e.priority));for(const o of t)try{return this.logger.log("0",`Trying fallback: ${o.name} (${o.url})`),await this.fetchUTXOsFromProvider(o.url,e)}catch(t){this.logger.warn("0",`Fallback ${o.name} failed: ${t.message}`);continue}}throw new Error(`Failed to fetch UTXOs: ${t.message}`)}}stripNetworkPrefix(t){return t.replace(/^(REGTEST_|TESTNET_|MAINNET_)/,"")}async fetchUTXOsFromRPC(t){if(!this.config.rpcHelper)throw new Error("RPC helper not configured");const e=this.config.rpcWallet||"";return(await this.config.rpcHelper.listUnspent(e,t)).map((t=>({txid:t.txid,vout:t.vout,satoshis:BigInt(Math.round(1e8*t.amount)),confirmations:t.confirmations})))}parseBlockstreamUTXOs(t,e){if(!Array.isArray(t))throw new Error("Invalid Blockstream response format: expected an array");return t.map((t=>{let o=0;return t.status&&"object"==typeof t.status?t.status.confirmed&&(o=void 0!==t.status.block_height&&t.status.block_height>0?e-t.status.block_height+1:1):void 0!==t.confirmations&&(o=t.confirmations),{txid:t.txid,vout:void 0!==t.vout?t.vout:t.n,satoshis:BigInt(void 0!==t.value?t.value:t.satoshis),confirmations:o}}))}async fetchUTXOsFromProvider(t,e){this.logger.stepStart("1","fetchUTXOsFromProvider");const o=`${t}/address/${e}/utxos`;this.logger.log("1",`Fetching UTXOs from ${o}`);const i=await this.getCurrentBlockHeight();this.logger.log("1",`Current block height: ${i}`);const r=new AbortController,n=setTimeout((()=>r.abort()),this.timeout);try{const t=await fetch(o,{signal:r.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(n),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();this.logger.log("1",`Raw API Response: ${JSON.stringify(e,null,2)}`);const a=this.parseBlockstreamUTXOs(e,i);return this.logger.stepEnd("1"),a}catch(t){if(clearTimeout(n),this.logger.stepEnd("1"),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);throw t}}async getUTXOSet(t,e){let o=[],i=null;for(let e=1;e<=3;e++)try{this.logger.log("2",`UTXO query attempt ${e}/3...`),o=await this.getUTXOs(t),this.logger.log("2",`UTXO query succeeded on attempt ${e}`),i=null;break}catch(t){i=t,this.logger.error("2",`UTXO query failed on attempt ${e}: ${t.message}`),e<3&&(this.logger.log("2","Waiting 500ms before retry..."),await new Promise((t=>setTimeout(t,500))))}if(i)throw new Error(`Failed to query UTXOs after 3 attempts: ${i.message}`);const r=o.reduce(((t,e)=>t+e.satoshis),0n),n=o.filter((t=>t.confirmations>=e)).reduce(((t,e)=>t+e.satoshis),0n),a=r-n;return{utxos:o,totalBalance:r,totalUTXOs:o.length,confirmedBalance:n,unconfirmedBalance:a}}async getBalance(t){const e=this.stripNetworkPrefix(t),o=`${this.config.providerUrl}/address/${e}`;this.logger.log("3",`Fetching balance from ${o}`);const i=new AbortController,r=setTimeout((()=>i.abort()),this.timeout);try{const t=await fetch(o,{signal:i.signal,headers:{Accept:"application/json","Content-Type":"application/json","User-Agent":"Mozilla/5.0 (compatible; DiamondHandsValidator/1.0)","ngrok-skip-browser-warning":"true"}});if(clearTimeout(r),!t.ok)throw new Error(`Bitcoin provider error: ${t.status} ${t.statusText}`);const e=await t.json();if(!e.chain_stats)throw new Error("Invalid response: missing chain_stats");const n=e.chain_stats.funded_txo_sum,a=e.chain_stats.spent_txo_sum;if(void 0===n||void 0===a)throw new Error(`Invalid response: missing required fields. funded_txo_sum: ${n}, spent_txo_sum: ${a}`);const s=BigInt(n-a);return this.logger.log("3",`Balance: ${s.toString()} sats`),this.logger.log("3",` - Funded: ${n} sats, Spent: ${a} sats`),s}catch(t){if(clearTimeout(r),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);const o=t.message||"Unknown error";throw this.logger.log("3",`Balance fetch failed: ${o}`),new Error(`Failed to fetch Bitcoin balance for address ${e}: ${o}`)}}async getTransaction(t){if(this.config.rpcHelper)try{const e=this.config.rpcWallet||"",o=await this.config.rpcHelper.getTransaction(e,t);return{txid:o.txid,confirmations:o.confirmations||0}}catch(t){if(t.message?.includes("Invalid or non-wallet transaction")||-5===t.code)return null;throw t}const e=new AbortController,o=setTimeout((()=>e.abort()),this.timeout);try{const i=await fetch(`${this.config.providerUrl}/tx/${t}`,{signal:e.signal});if(clearTimeout(o),!i.ok){if(404===i.status)return null;throw new Error(`Bitcoin provider error: ${i.status} ${i.statusText}`)}const r=await i.json();return r.status?{txid:r.txid,confirmations:r.status.confirmed&&r.status.block_height?1:0}:null}catch(t){if(clearTimeout(o),"AbortError"===t.name)throw new Error(`Request timeout after ${this.timeout}ms`);if(t.message?.includes("404"))return null;throw t}}};function b(t,e){if(0===t)return{termDurationDays:0,termLengthDays:30*e,isExpired:!1,daysUntilExpiry:30*e,daysIntoGracePeriod:0};const o=30*e,i=Math.floor(Date.now()/1e3)-t,r=Math.floor(i/86400);return{termDurationDays:r,termLengthDays:o,isExpired:r>o,daysUntilExpiry:Math.max(0,o-r),daysIntoGracePeriod:Math.max(0,r-o)}}function S(t,e){if(!t)return 13e3;const o=Math.min(e,30);return 11e3+9e3*(o*o)/900}function T(t,o,i){const r=t*o/10000000000000000n;if(0n===i)return{collateralValueUsd:r,collateralRatioBps:Number.MAX_SAFE_INTEGER};return{collateralValueUsd:r,collateralRatioBps:Number(r*e*10000n/i)}}function A(t,e){if(t<0n)throw new Error("Collateral value cannot be negative");if(e<=0n)throw new Error("Debt must be positive to calculate ratio");const o=Number(10000n*(10000000000n*t)/e);if(o<0)throw new Error(`Collateral ratio out of bounds: ${o} bps (${o/100}%)`);return!isFinite(o)||o>Number.MAX_SAFE_INTEGER?Number.MAX_SAFE_INTEGER:o}function v(t){return t/100}var $=class{constructor(t,e,o){this.mode=o,e&&Array.isArray(e)?this.sources=e:t&&Array.isArray(t)?this.sources=this.buildSources(t):this.sources=this.buildSources(),this.sources.sort(((t,e)=>t.priority-e.priority)),this.validateProviderCount()}validateProviderCount(){if("prod"===this.mode&&this.sources.length<3)throw new Error(`Production mode requires at least 3 price providers for consensus. Currently configured: ${this.sources.length} provider(s). Supported providers: CoinGecko, Binance, Coinbase, CryptoCompare (with API key)`);if(0===this.sources.length)throw new Error("No price providers configured. At least one provider is required.")}buildSources(t){const e=[];if(!t||0===t.length)return this.getDefaultSources();let o=1;for(const i of t){switch(i.name.toLowerCase()){case"coingecko":e.push(this.createCoinGeckoSource(i.apiKey,o++));break;case"binance":e.push(this.createBinanceSource(i.apiKey,i.apiSecret,o++));break;case"coinbase":e.push(this.createCoinbaseSource(i.apiKey,o++));break;case"cryptocompare":if(!i.apiKey)throw new Error("CryptoCompare requires an API key. Please provide apiKey in priceProviders config.");e.push(this.createCryptoCompareSource(i.apiKey,o++));break;default:console.warn(`[Price Oracle] Unknown provider: ${i.name} - skipping`)}}return e}createCryptoCompareSource(t,e){return{name:"CryptoCompare",fetchPrice:async()=>{const e=new URL("https://min-api.cryptocompare.com/data/price");e.searchParams.set("fsym","BTC"),e.searchParams.set("tsyms","USDT"),e.searchParams.set("api_key",t);const o=await(async t=>{const e=new AbortController,o=setTimeout((()=>e.abort()),5e3);try{const i=await fetch(t,{signal:e.signal,headers:{Accept:"application/json"}});if(clearTimeout(o),!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}catch(t){if(clearTimeout(o),"AbortError"===t.name)throw new Error("Request timeout after 5000ms");throw t}})(e.toString()),i=Number(o?.USDT);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CryptoCompare price payload");const r=(await Lit.Actions.broadcastAndCollect({name:"cryptoComparePrice",value:i.toString()})).map((t=>parseFloat(t)));return r.sort(((t,e)=>t-e)),r[Math.floor(r.length/2)]},priority:e}}createFetchJson(){return async(t,e)=>{const o=new AbortController,i=setTimeout((()=>o.abort()),5e3);try{const i=await fetch(t,{headers:{Accept:"application/json",...e||{}},signal:o.signal});if(!i.ok)throw new Error(`HTTP ${i.status} ${i.statusText}`);return await i.json()}finally{clearTimeout(i)}}}getDefaultSources(){return[this.createCoinGeckoSource(void 0,1),this.createBinanceSource(void 0,void 0,2),this.createCoinbaseSource(void 0,3)]}createCoinGeckoSource(t,e=1){const o=this.createFetchJson();return{name:"CoinGecko",fetchPrice:async()=>{const t=new URL("https://api.coingecko.com/api/v3/simple/price");t.searchParams.set("ids","bitcoin"),t.searchParams.set("vs_currencies","usd");const e=await o(t.toString()),i=Number(e?.bitcoin?.usd);if(!Number.isFinite(i)||i<=0)throw new Error("Invalid CoinGecko price payload");const r=(await Lit.Actions.broadcastAndCollect({name:"coinGeckoPrice",value:i.toString()})).map((t=>parseFloat(t)));return r.sort(((t,e)=>t-e)),r[Math.floor(r.length/2)]},priority:e}}createBinanceSource(t,e,o=2){const i=this.createFetchJson();return{name:"Binance",fetchPrice:async()=>{const e=new URL("https://api.binance.com/api/v3/ticker/price");e.searchParams.set("symbol","BTCUSDT");const o=t?{"X-MBX-APIKEY":t}:void 0,r=await i(e.toString(),o),n=Number(r?.price);if(!Number.isFinite(n)||n<=0)throw new Error("Invalid Binance price payload");const a=(await Lit.Actions.broadcastAndCollect({name:"binancePrice",value:n.toString()})).map((t=>parseFloat(t)));return a.sort(((t,e)=>t-e)),a[Math.floor(a.length/2)]},priority:o}}createCoinbaseSource(t,e=3){const o=this.createFetchJson();return{name:"Coinbase",fetchPrice:async()=>{const e=t?{"CB-ACCESS-KEY":t}:void 0,i=await o("https://api.coinbase.com/v2/prices/BTC-USD/spot",e),r=Number(i?.data?.amount);if(!Number.isFinite(r)||r<=0)throw new Error("Invalid Coinbase price payload");const n=(await Lit.Actions.broadcastAndCollect({name:"coinbasePrice",value:r.toString()})).map((t=>parseFloat(t)));return n.sort(((t,e)=>t-e)),n[Math.floor(n.length/2)]},priority:e}}async getBTCPrice(){const t=Date.now();console.log("[Price Oracle] Fetching BTC price from external sources (parallel)..."),console.log(`[Price Oracle] Start time: ${t}`);const e=new Promise(((e,o)=>{setTimeout((()=>{const e=Date.now()-t;console.error(`[Price Oracle] \u274c GLOBAL TIMEOUT after ${e}ms`),o(new Error("Price oracle global timeout after 15000ms"))}),15e3)})),o=this.sources.map((async t=>{const e=Date.now();try{console.log(`[Price Oracle] [${e}] Querying ${t.name}...`);const o=await t.fetchPrice(),i=Date.now()-e;return console.log(`[Price Oracle] \u2705 [+${i}ms] ${t.name}: $${o.toLocaleString()}`),{source:t.name,priceUSD:o}}catch(o){const i=Date.now()-e;throw console.warn(`[Price Oracle] \u26a0\ufe0f [+${i}ms] ${t.name} failed: ${o.message}`),o}}));try{console.log("[Price Oracle] Waiting for first success (race pattern)...");const i=await Promise.race([Promise.race(o.map((t=>t.catch((t=>({error:t})))))),e]);if("error"in i){console.log("[Price Oracle] First result was error, waiting for any success...");const e=Date.now(),i=new Promise(((t,o)=>{setTimeout((()=>{const t=Date.now()-e;console.error(`[Price Oracle] \u274c FALLBACK TIMEOUT after ${t}ms`),o(new Error("Price oracle fallback timeout after 10000ms"))}),1e4)})),r=await Promise.race([Promise.allSettled(o),i]),n=Date.now()-e;console.log(`[Price Oracle] Fallback completed in ${n}ms`);const a=r.find((t=>"fulfilled"===t.status));if(!a){const e=Date.now()-t;throw console.error(`[Price Oracle] All sources failed after ${e}ms`),new Error("All price sources failed")}const{source:s,priceUSD:c}=a.value,l=Date.now()-t;console.log(`[Price Oracle] Using ${s} (first successful after failures) - total time: ${l}ms`);const u=Math.round(100*c),d=1000000n*BigInt(u);return console.log(`[Price Oracle] Price with 8 decimals: ${d}`),d}const{source:r,priceUSD:n}=i,a=Date.now()-t;console.log(`[Price Oracle] Using ${r} (fastest response) - total time: ${a}ms`);const s=Math.round(100*n),c=1000000n*BigInt(s);return console.log(`[Price Oracle] Price with 8 decimals: ${c}`),c}catch(e){const o=Date.now()-t;throw console.error(`[Price Oracle] Failed after ${o}ms: ${e.message}`),new Error(`All price sources failed: ${e.message}`)}}async getBTCPriceConsensus(){console.log("[Price Oracle] Fetching BTC price with consensus...");const t=(await Promise.allSettled(this.sources.map((async t=>{const e=await t.fetchPrice();return{source:t.name,price:e}})))).filter((t=>"fulfilled"===t.status)).map((t=>t.value));if(0===t.length)throw new Error("No price sources returned data");console.log(`[Price Oracle] Got prices from ${t.length}/${this.sources.length} sources:`),t.forEach((t=>{console.log(` ${t.source}: $${t.price.toLocaleString()}`)}));const e=t.map((t=>t.price));e.sort(((t,e)=>t-e));const o=e[Math.floor(e.length/2)],i=e[0],r=e[e.length-1]/i,n=t.filter((t=>Math.abs(t.price-o)/o<=.02));if(r>1.05&&n.length===t.length)throw new Error(`Price consensus failed: sources too dispersed (${(100*(r-1)).toFixed(1)}% spread)`);if(n.length<t.length){const e=t.filter((t=>!n.find((e=>e.source===t.source))));if(console.log(`[Price Oracle] \u26a0\ufe0f Detected ${e.length} outlier(s):`),e.forEach((t=>{const e=Math.abs(t.price-o)/o;console.log(` ${t.source}: $${t.price.toLocaleString()} (${(100*e).toFixed(1)}% deviation)`)})),n.length<2)throw new Error("Price consensus failed: insufficient valid sources after outlier removal");console.log(`[Price Oracle] \u2705 Outliers filtered, continuing with ${n.length} valid sources`);const i=n.map((t=>t.price));i.sort(((t,e)=>t-e));const r=i[Math.floor(i.length/2)];console.log(`[Price Oracle] \u2705 Consensus price (median): $${r.toLocaleString()}`);const a=Math.round(100*r);return 1000000n*BigInt(a)}console.log(`[Price Oracle] \u2705 Consensus price (median): $${o.toLocaleString()}`);const a=Math.round(100*o);return 1000000n*BigInt(a)}},E=class{constructor(t){this.config=t,this.bitcoinProvider=t.bitcoinProvider}async calculateBalance(t,e){const o=await this.bitcoinProvider.getBalance(e),i=await this.getAuthorizedSpendsFromContract(t),r=i.reduce(((t,e)=>t+e.satoshis),0n),n=o>r?o-r:0n;return{totalUTXOs:[],totalBalance:o,authorizedUTXOs:i,authorizedBalance:r,authorizedSpendsHash:this.computeAuthorizedSpendsHash(t,i),availableUTXOs:[],availableBalance:n,vaultAddress:e,positionId:t,timestamp:Date.now()}}async calculateTrustedBalance(t,e,o){if(o)return await this.calculateBalance(t,e);const i=this.config.minConfirmations||6,r=await this.bitcoinProvider.getUTXOSet(e,i),n=await this.getAuthorizedSpendsFromContract(t);for(const t of n){if(r.utxos.some((e=>e.txid===t.txid&&e.vout===t.vout)))throw new Error(`Authorized UTXO ${t.txid}:${t.vout} not yet spent. Transaction was authorized in smart contract but not signed and broadcasted to Bitcoin network. Complete the transaction by signing and broadcasting it.`)}const a=n.reduce(((t,e)=>t+e.satoshis),0n),s=r.utxos.filter((t=>!this.isUTXOAuthorized(t,n))),c=s.reduce(((t,e)=>t+e.satoshis),0n),l=this.computeAuthorizedSpendsHash(t,n);return{totalUTXOs:r.utxos,totalBalance:r.totalBalance,authorizedUTXOs:n,authorizedBalance:a,authorizedSpendsHash:l,availableUTXOs:s,availableBalance:c,vaultAddress:e,positionId:t,timestamp:Date.now()}}async getTrustedBalance(t,e){return(await this.calculateTrustedBalance(t,e)).availableBalance}async getAvailableUTXOs(t,e){return(await this.calculateTrustedBalance(t,e)).availableUTXOs}async isUTXOAvailable(t,e,o,i){return(await this.getAvailableUTXOs(t,e)).some((t=>t.txid===o&&t.vout===i))}isUTXOAuthorized(t,e){return e.some((e=>e.txid===t.txid&&e.vout===t.vout))}async getAuthorizedSpendsFromContract(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let o;o=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const i=new ethers.providers.StaticJsonRpcProvider(o,{name:"any",chainId:this.config.chainId}),r=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 r.getAuthorizedSpends(e)).map((e=>({txid:e.txid,vout:Number(e.vout),satoshis:BigInt(e.satoshis.toString()),positionId:t,targetAddress:e.targetAddress,targetAmount:BigInt(e.targetAmount.toString()),timestamp:Number(e.authorizedAt)})))}computeAuthorizedSpendsHash(t,e){const o=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;if(0===e.length)return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[o,[]]));const i=e.map((t=>{const e=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.txid)),o=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(t.targetAddress));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","uint32","uint256","bytes32","uint256","uint256"],[e,t.vout,t.satoshis.toString(),o,t.targetAmount.toString(),t.timestamp]))}));return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32[]"],[o,i]))}};var I=class{constructor(t){this.termManagerAddress=t.termManagerAddress,this.loanOpsManagerAddress=t.loanOpsManagerAddress,this.chain=t.chain,this.chainId=t.chainId,this.rpcUrl=t.rpcUrl}async getLiquidationThreshold(){try{let t;t=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const e=new ethers.providers.StaticJsonRpcProvider(t,{name:"any",chainId:this.chainId}),o=[{inputs:[],name:"liquidationThreshold",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],i=new ethers.Contract(this.loanOpsManagerAddress,o,e),r=await i.liquidationThreshold();return Number(r.toString())}catch(t){throw console.error("[ProtocolParameters] Error fetching liquidation threshold:",t.message),new Error(`Failed to fetch liquidation threshold: ${t.message}`)}}async getTermFees(t){try{let e;e=this.rpcUrl?this.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.chain});const o=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),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"}],r=new ethers.Contract(this.termManagerAddress,i,o),n=await r.getTermFees(t);return{originationFeeBps:Number(n.originationFee?.toString?.()??n[0]?.toString?.()??n[0]),extensionFeeBps:Number(n.extensionFee?.toString?.()??n[1]?.toString?.()??n[1])}}catch(t){throw console.error("[ProtocolParameters] Error fetching term fees:",t.message),new Error(`Failed to fetch term fees: ${t.message}`)}}async getAuthorizedSpendsHash(t){try{const e=this.rpcUrl||await Lit.Actions.getRpcUrl({chain:this.chain}),o=new ethers.providers.StaticJsonRpcProvider(e,{name:"any",chainId:this.chainId}),i=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`,r=new ethers.Contract(this.loanOpsManagerAddress,[{inputs:[{name:"positionId",type:"bytes32"}],name:"getAuthorizedSpendsHash",outputs:[{name:"",type:"bytes32"}],stateMutability:"view",type:"function"}],o);return await r.getAuthorizedSpendsHash(i)}catch(t){throw console.error("[ProtocolParameters] Error fetching authorized spends hash:",t.message),new Error(`Failed to fetch authorized spends hash: ${t.message}`)}}};function P(t){return new I(t)}var C=class{constructor(t){this.config=t}async getVaultSnapshot(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const o=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress),i=await this.config.priceOracle.getBTCPrice(),r=P({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[n,a]=await Promise.all([r.getLiquidationThreshold(),r.getTermFees(e.selectedTerm)]),s=b(e.termStartTimestamp,e.selectedTerm),c=S(s.isExpired,s.daysIntoGracePeriod),l=T(o.availableBalance,i,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:i,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:s.termDurationDays,termLengthDays:s.termLengthDays,isExpired:s.isExpired,daysUntilExpiry:s.daysUntilExpiry,daysIntoGracePeriod:s.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:n,originationFeeBps:a.originationFeeBps,extensionFeeBps:a.extensionFeeBps,timestamp:Date.now()}}async getVaultSnapshotFast(t){const e=await this.queryPositionState(t);console.log(`[Vault Snapshot] Raw vault address from contract: "${e.vaultAddress}"`);const o=await this.config.vaultBalance.calculateTrustedBalance(t,e.vaultAddress,!0),i=await this.config.priceOracle.getBTCPrice(),r=P({termManagerAddress:this.config.termManagerAddress,loanOpsManagerAddress:this.config.loanOpsManagerAddress,chain:this.config.chain,chainId:this.config.chainId||1,rpcUrl:this.config.rpcUrl}),[n,a]=await Promise.all([r.getLiquidationThreshold(),r.getTermFees(e.selectedTerm)]),s=b(e.termStartTimestamp,e.selectedTerm),c=S(s.isExpired,s.daysIntoGracePeriod),l=T(o.availableBalance,i,e.ucdDebt),u=l.collateralRatioBps<c,d=l.collateralRatioBps-c;return{positionId:e.positionId,pkpId:e.pkpId,borrower:e.borrower,vaultAddress:e.vaultAddress,ucdDebt:e.ucdDebt,termStartTimestamp:e.termStartTimestamp,selectedTerm:e.selectedTerm,status:e.status,expiryAt:e.expiryAt,totalBTCSats:o.totalBalance,totalUTXOs:o.totalUTXOs,authorizedSpendsSats:o.authorizedBalance,authorizedSpendsHash:o.authorizedSpendsHash,availableBTCSats:o.availableBalance,availableUTXOs:o.availableUTXOs,btcPriceUsd:i,collateralValueUsd:l.collateralValueUsd,collateralRatioBps:l.collateralRatioBps,termDurationDays:s.termDurationDays,termLengthDays:s.termLengthDays,isExpired:s.isExpired,daysUntilExpiry:s.daysUntilExpiry,daysIntoGracePeriod:s.daysIntoGracePeriod,currentLiquidationThreshold:c,isLiquidatable:u,marginToLiquidationBps:d,liquidationThresholdBps:n,originationFeeBps:a.originationFeeBps,extensionFeeBps:a.extensionFeeBps,timestamp:Date.now()}}async queryPositionState(t){const e=t.startsWith("0x")?t:`0x${t.padStart(64,"0")}`;let o;o=this.config.rpcUrl?this.config.rpcUrl:await Lit.Actions.getRpcUrl({chain:this.config.chain});const i=new ethers.providers.StaticJsonRpcProvider(o,{name:"any",chainId:this.config.chainId}),r=new ethers.Contract(this.config.contractAddress,[{inputs:[],name:"core",outputs:[{internalType:"address",name:"",type:"address"}],stateMutability:"view",type:"function"}],i),n=await r.core(),a=new ethers.Contract(n,[{inputs:[{internalType:"bytes32",name:"positionId",type:"bytes32"}],name:"getPositionDetails",outputs:[{components:[{internalType:"bytes32",name:"positionId",type:"bytes32"},{internalType:"bytes32",name:"pkpId",type:"bytes32"},{internalType:"uint256",name:"ucdDebt",type:"uint256"},{internalType:"string",name:"vaultAddress",type:"string"},{internalType:"address",name:"borrower",type:"address"},{internalType:"uint40",name:"createdAt",type:"uint40"},{internalType:"uint40",name:"lastUpdated",type:"uint40"},{internalType:"uint16",name:"selectedTerm",type:"uint16"},{internalType:"uint40",name:"expiryAt",type:"uint40"},{internalType:"enum LoanStatusLib.LoanStatus",name:"status",type:"uint8"}],internalType:"struct IPositionManagerCore.Position",name:"",type:"tuple"}],stateMutability:"view",type:"function"}],i),s=await a.getPositionDetails(e),c=Number(s.status);if(c<0||c>7)throw console.error("[VaultSnapshot] INVALID STATUS - ABI decoding error detected"),console.error(" Position ID:",e),console.error(" Contract:",this.config.contractAddress),console.error(" Status decoded:",c,"(expected 0-7)"),console.error(" Full position:",JSON.stringify(s,null,2)),new Error(`Invalid position status decoded from contract: ${c} (expected 0-7). This indicates an ABI decoding issue. Position: ${e}`);const u=Number(s.expiryAt),d=Number(s.selectedTerm),h=(g=d,0===(p=u)?0:p-30*g*86400);var p,g;return{positionId:s.positionId,pkpId:s.pkpId,borrower:s.borrower,vaultAddress:this.parseVaultAddress(s.vaultAddress,l(this.config.chain)),ucdDebt:BigInt(s.ucdDebt.toString()),termStartTimestamp:h,selectedTerm:d,status:c,expiryAt:u}}parseVaultAddress(t,e){try{const o=JSON.parse(t);if("object"==typeof o&&null!==o){if("sepolia"===e.toLowerCase())return o.regtest||o.testnet||o.mainnet;return"testnet"===u(e)&&(o.testnet||o.regtest)||o.mainnet}}catch(t){}return t}async isLiquidatable(t){return(await this.getVaultSnapshot(t)).isLiquidatable}async getCollateralRatio(t){return(await this.getVaultSnapshot(t)).collateralRatioBps}async getAvailableBalance(t){return(await this.getVaultSnapshot(t)).availableBTCSats}async hasSufficientCollateral(t,o,i){const r=await this.getVaultSnapshot(t);return function(t,o,i,r){const n=o+i;return 0n===n||Number(t*e*10000n/n)>=r}(r.collateralValueUsd,r.ucdDebt,o,i)}};(async()=>{const e=f.create("UCD Mint Validator"),o=1000000000000n,r=100000000000000n,a=[{inputs:[],name:"getRemainingMintCapacity",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],s=[{inputs:[],name:"maxSupply",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}],h=[{inputs:[],name:"getCurrentSupply",outputs:[{internalType:"uint256",name:"",type:"uint256"}],stateMutability:"view",type:"function"}];e.stepStart("0","Validating configuration");const p=globalThis.chain,m=globalThis.bitcoinProviderUrl;if(!p)throw new Error('Missing required parameter: "chain". Must be "sepolia" or "ethereum"');if(!m)throw new Error('Missing required parameter: "bitcoinProviderUrl". Must be an approved Bitcoin RPC provider URL');const w=g.verifyAmountPositionActionAuthorizationStructure(globalThis.auth),b=w.mode;let S,T,I;if("prod"===b?(console.log(" \ud83d\udd12 Running in PRODUCTION mode - strict validation enabled"),T=l(p),I=u(T),S=function(t){for(const e of Object.values(n)){const o=c[e].find((e=>e.url===t));if(o)return o}const e=Object.entries(c).flatMap((([t,e])=>e.map((e=>`${t}: ${e.name} (${e.url})`)))).join(", ");throw new Error(`Bitcoin provider not approved. Provided URL: ${t}. Approved providers: ${e}`)}(m),console.log(` \u2705 Chain: ${p} (${T})`),console.log(` \u2705 Bitcoin Network: ${I}`),console.log(` \u2705 Bitcoin Provider: ${S.name} (${S.url})`),console.log(` \u2705 Min Confirmations: ${S.minConfirmations}`)):(console.log(" \u26a0\ufe0f Running in DEVELOPMENT mode - accepting any Bitcoin provider"),T=p,I="sepolia"===p?"testnet":"regtest",S={name:"Custom Provider",url:m,minConfirmations:1,network:I},console.log(` \u2705 EVM Chain: ${p}`),console.log(` \u2705 Bitcoin Provider: ${m} (unvalidated)`),console.log(` \u2705 Min Confirmations: ${S.minConfirmations} (dev default)`)),e.log("0","Validating contract addresses..."),e.log("0",`globalThis.contractAddresses exists: ${!!globalThis.contractAddresses}`),e.log("0","globalThis.contractAddresses type: "+typeof globalThis.contractAddresses),!globalThis.contractAddresses||"object"!=typeof globalThis.contractAddresses)throw e.error("0",`\u274c ERROR: No valid contractAddresses object found: ${JSON.stringify(globalThis.contractAddresses)}`),new Error("contractAddresses is required and must include: PositionManager, LoanOperationsManagerModule, TermManagerModule, UCDController");e.log("0","\u2705 Found valid contractAddresses object"),e.log("0",`Available keys: ${Object.keys(globalThis.contractAddresses)}`);const P=globalThis.contractAddresses.PositionManager,B=globalThis.contractAddresses.LoanOperationsManagerModule,M=globalThis.contractAddresses.TermManagerModule,U=globalThis.contractAddresses.UCDController;if(e.log("0","Extracted addresses:"),e.log("0",`POSITION_MANAGER_ADDRESS: ${P||"MISSING"}`),e.log("0",`LOAN_OPS_MANAGER_ADDRESS: ${B||"MISSING"}`),e.log("0",`TERM_MANAGER_ADDRESS: ${M||"MISSING"}`),e.log("0",`UCD_CONTROLLER_ADDRESS: ${U||"MISSING"}`),!(P&&B&&M&&U))throw new Error("All contract addresses are required: PositionManager, LoanOperationsManagerModule, TermManagerModule, UCDController");console.log(" \u2705 Contract addresses loaded and validated"),console.log(` PositionManager: ${P}`),console.log(` LoanOpsManager: ${B}`),e.stepEnd("0"),console.log("[Step 0.5] Critical system health checks..."),e.stepStart("0.5","Critical system health checks"),e.warn("0.5","System health checks temporarily disabled (SystemHealthModule not available)"),e.log("0.5","\u2705 System health checks complete (skipped)"),e.stepEnd("0.5");const x=BigInt(w.amount);if(x<=0n)throw new Error(`Amount must be positive, got: ${x.toString()} wei`);console.log(" Fetching protocol mint limits..."),e.log("0","Fetching protocol mint limits...");const D=globalThis.customRpcUrl;let k;D&&"string"==typeof D?(console.log(` Using custom RPC URL: ${D}`),k=D):(k=await Lit.Actions.getRpcUrl({chain:p}),console.log(` Using LIT-provided RPC URL for chain: ${p}`));const O=new ethers.providers.JsonRpcProvider(k),N=new ethers.Contract(B,[{inputs:[],name:"getProtocolConfig",outputs:[{internalType:"uint256",name:"_liquidationThreshold",type:"uint256"},{internalType:"uint256",name:"_maximumLtvRatio",type:"uint256"},{internalType:"uint256",name:"_minimumLoanValueUcd",type:"uint256"},{internalType:"uint256",name:"_minimumLoanValueWei",type:"uint256"},{internalType:"uint256",name:"_maximumLoanValueUcd",type:"uint256"}],stateMutability:"view",type:"function"}],O),L=await N.getProtocolConfig(),R=BigInt(L[3].toString()),F=1000000000000000000n*BigInt(L[4].toString());if(console.log(" \u2705 Protocol limits loaded from LoanOperationsManager (consolidated call)"),console.log(` - Minimum: ${R.toString()} wei`),console.log(` - Maximum: ${F.toString()} wei`),e.log("0","\u2705 Protocol limits loaded from LoanOperationsManager"),x<R)throw new Error(`Amount too small: ${x.toString()} wei. Minimum: ${R.toString()} wei`);if(x>F)throw new Error(`Amount too large: ${x.toString()} wei. Maximum: ${F.toString()} wei`);const z=globalThis.publicKey;if(!z)throw new Error('Missing required parameter: "publicKey"');if("string"!=typeof z||0===z.length)throw new Error("Invalid publicKey: must be non-empty string");const q=z;let H="0.5";try{console.log("[UCD Mint Validator] Started"),console.log(` Position: ${w.positionId}`),console.log(` Amount: ${x.toString()} wei`),e.log("0","Validator Started");const n=new y({providerUrl:S.url}),c=new $(globalThis.priceProviders,void 0,b),l=(_={contractAddress:B,chain:p,chainId:"prod"===b?d(T):w.chainId,rpcUrl:D,bitcoinProvider:n,minConfirmations:"prod"===b?S.minConfirmations:1},new E(_));H="1",console.log("[Step 1] Getting vault snapshot..."),e.stepStart("1","Getting vault snapshot");const u=function(t){return new C(t)}({contractAddress:P,termManagerAddress:M,loanOpsManagerAddress:B,chain:p,chainId:"prod"===b?d(T):w.chainId,rpcUrl:D,vaultBalance:l,priceOracle:c}),m=await u.getVaultSnapshot(w.positionId);if(m.btcPriceUsd<o||m.btcPriceUsd>r)throw new Error(`Bitcoin price ${m.btcPriceUsd} is outside acceptable range (1000000000000 - 100000000000000). This may indicate oracle manipulation or stale price data. Please try again later.`);console.log(` \u2705 BTC price validation passed: $${(Number(m.btcPriceUsd)/1e8).toFixed(2)}`),e.stepEnd("1"),H="2",console.log("[Step 2] Authenticating caller..."),e.stepStart("2","Authenticating caller");if(!await g.verifyMintAuthorization(w,m.borrower))throw new Error("Caller not authorized");console.log(" \u2705 Caller authorized"),e.stepEnd("2"),H="3",console.log("[Step 3] Validating position state..."),e.stepStart("3","Validating position state");if(![0,1,2].includes(m.status))throw new Error(`Invalid position status for minting: ${i(m.status)}. Must be PENDING_DEPOSIT, PENDING_MINT, or ACTIVE. Current status prevents minting operations.`);if(console.log(` \u2705 Position status valid for minting: ${i(m.status)}`),m.isExpired)throw new Error(`Loan term expired ${m.daysIntoGracePeriod} days ago - minting rejected. Must repay or extend term first.`);if(m.isLiquidatable)throw new Error(`Position is liquidatable (${v(m.collateralRatioBps)}% < ${v(m.currentLiquidationThreshold)}%) - minting rejected. Add collateral first.`);console.log(" \u2705 Position is healthy"),console.log(` - Term: ${m.daysUntilExpiry} days remaining`),console.log(` - Current ratio: ${v(m.collateralRatioBps)}%`),console.log(` - Liquidation threshold: ${v(m.currentLiquidationThreshold)}%`),e.stepEnd("3"),H="4",console.log("[Step 4] Calculating fees..."),e.stepStart("4","Calculating fees");const f=function(t,e){if(t<0n)throw new Error("Amount cannot be negative");if(e<0||e>1e4)throw new Error("Fee basis points must be between 0 and 10000 (0% to 100%)");return t*BigInt(e)/10000n}(x,m.originationFeeBps);console.log(` Mint fee: ${f.toString()} wei (${v(m.originationFeeBps)}%)`),e.stepEnd("4"),H="4a",console.log("[Step 4a & 4b] Validating capacity limits..."),e.stepStart("4ab","Validating capacity limits");const I=x+f,k=new ethers.Contract(U,[...a,...s,...h],O),[N,L,R]=await Promise.all([k.getRemainingMintCapacity(),k.maxSupply(),k.getCurrentSupply()]),F=BigInt(N.toString()),z=BigInt(L.toString()),V=BigInt(R.toString());if(I>F)throw new Error(`Daily mint limit would be exceeded: attempting to mint ${I.toString()} wei (${x.toString()} + ${f.toString()} fee), but only ${F.toString()} wei remaining in daily capacity`);if(console.log(" \u2705 Daily mint limit check passed"),console.log(` - Mint amount (with fee): ${I.toString()} wei`),console.log(` - Remaining capacity: ${F.toString()} wei`),z>0n){const t=V+I;if(t>z)throw new Error(`Max supply would be exceeded: minting ${I.toString()} wei (${x.toString()} + ${f.toString()} fee) would result in total supply of ${t.toString()} wei, exceeding max supply of ${z.toString()} wei`);console.log(" \u2705 Max supply check passed"),console.log(` - Current supply: ${V.toString()} wei`),console.log(` - After mint: ${t.toString()} wei`),console.log(` - Max supply: ${z.toString()} wei`),console.log(` - Remaining capacity: ${(z-V).toString()} wei`)}else console.log(" \u2705 Max supply unlimited (not enforced)");e.stepEnd("4ab"),H="5",console.log("[Step 5] Calculating new state..."),e.stepStart("5","Calculating new state");const X=m.ucdDebt+x+f,G=m.availableBTCSats;console.log(` Current debt: ${m.ucdDebt.toString()} wei`),console.log(` New debt: ${X.toString()} wei`),console.log(` Available collateral: ${G.toString()} sats`),e.stepEnd("5"),H="6",console.log("[Step 6] Validating collateral ratio after mint..."),e.stepStart("6","Validating collateral ratio after mint");const j=function(e,o){if(e<0n)throw new Error("BTC amount cannot be negative");if(o<=0n)throw new Error("BTC price must be positive");return e*o/t}(G,m.btcPriceUsd),K=A(j,X);if(console.log(` New collateral ratio: ${v(K)}%`),console.log(` Minimum required: ${v(m.liquidationThresholdBps)}%`),!function(t,e,o){if(o<0||o>1e6)throw new Error("Minimum ratio must be between 0 and 1000000 bps (0% to 10000%)");return A(t,e)>=o}(j,X,m.liquidationThresholdBps))throw new Error(`Insufficient collateral: ${v(K)}% < ${v(m.liquidationThresholdBps)}%`);console.log(" \u2705 Sufficient collateral");const J=BigInt("340282366920938463463374607431768211455");if(X>J)throw new Error(`New debt exceeds maximum allowed value: ${X.toString()} > ${J.toString()} (type(uint128).max). The contract will revert with DebtOverflow error.`);console.log(` \u2705 New debt within bounds: ${X.toString()} <= ${J.toString()}`),e.stepEnd("6"),H="7",console.log("[Step 7] Building authorization message..."),e.stepStart("7","Building authorization message");const W=m.authorizedSpendsHash,Q=ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["uint256"],[m.ucdDebt])),Y={authorizedSpendsHash:W,ucdDebtHash:Q,contractBundleHash:ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["address","address","address","address"],[P,B,M,U])),btcPrice:m.btcPriceUsd.toString(),mintAmount:x.toString(),mintFee:f.toString(),newCollateral:G.toString(),newDebt:X.toString()};console.log("[Step 7] DEBUG: Message Hash Parameters:"),console.log(" positionId:",w.positionId),console.log(" authorizedSpendsHash:",Y.authorizedSpendsHash),console.log(" ucdDebtHash:",Y.ucdDebtHash),console.log(" contractBundleHash:",Y.contractBundleHash),console.log(" btcPrice:",Y.btcPrice,"(type:",typeof Y.btcPrice,")"),console.log(" mintAmount:",Y.mintAmount,"(type:",typeof Y.mintAmount,")"),console.log(" mintFee:",Y.mintFee,"(type:",typeof Y.mintFee,")"),console.log(" newCollateral:",Y.newCollateral,"(type:",typeof Y.newCollateral,")"),console.log(" newDebt:",Y.newDebt,"(type:",typeof Y.newDebt,")"),console.log(" quantumTimestamp:",w.timestamp,"(type:",typeof w.timestamp,")");const Z=ethers.utils.keccak256(ethers.utils.toUtf8Bytes(w.action));console.log(" action:",w.action),console.log(" actionHash:",Z);const tt=ethers.utils.defaultAbiCoder.encode(["bytes32","bytes32","uint256","uint256","uint256","uint256","uint256","bytes32","bytes32","bytes32","uint256"],[Z,w.positionId,Y.mintAmount,Y.mintFee,Y.newDebt,Y.newCollateral,Y.btcPrice,Y.authorizedSpendsHash,Y.ucdDebtHash,Y.contractBundleHash,w.timestamp]);console.log("[Step 7] DEBUG: Encoded parameters (before keccak256):"),console.log(" Length:",tt.length,"bytes"),console.log(" Hex (first 100 chars):",tt.substring(0,100)+"...");const et=ethers.utils.keccak256(tt);console.log("[Step 7] Message hash for signing:",et),console.log("[Step 7] Message hash bytes length:",ethers.utils.arrayify(et).length),console.log("[Step 7] Validator PKP publicKey:",q),console.log("[Step 7] Validator PKP ethAddress (from publicKey):",ethers.utils.computeAddress("0x"+q)),e.stepEnd("7"),H="8",console.log("[Step 8] Signing authorization..."),e.stepStart("8","Signing authorization");const ot=await Lit.Actions.signEcdsa({toSign:ethers.utils.arrayify(et),publicKey:"0x"+q,sigName:"ucdMintAuth"});e.stepEnd("8"),e.summary(),console.log("[UCD Mint Validator] \u2705 Complete"),Lit.Actions.setResponse({response:JSON.stringify({approved:!0,positionId:w.positionId,mintAmount:x.toString(),mintFee:f.toString(),newDebt:X.toString(),newCollateral:G.toString(),newCollateralRatioBps:K,btcPrice:m.btcPriceUsd.toString(),authorizedSpendsHash:W,signature:ot,timestamp:w.timestamp,validatorPkp:q})})}catch(t){console.error("[UCD Mint Validator] \u274c Failed:",t.message),console.error(`[UCD Mint Validator] Failed at step: ${H}`),Lit.Actions.setResponse({response:JSON.stringify({approved:!1,reason:t.message||t.toString(),failedStep:H,positionId:w?.positionId,timestamp:Date.now()})})}var _})()})();
@@ -0,0 +1 @@
1
+ c52cca1eb9dcf26a0d276ecfe2d7707a1a4d63baf10f7d29b8b0eee49adeeab1
@@ -0,0 +1,9 @@
1
+ {
2
+ "actionName": "ucd-mint-validator",
3
+ "originalSize": 124115,
4
+ "minifiedSize": 53476,
5
+ "compressionRatio": 0.5691415219755871,
6
+ "hash": "c52cca1eb9dcf26a0d276ecfe2d7707a1a4d63baf10f7d29b8b0eee49adeeab1",
7
+ "buildTime": 1768236995641,
8
+ "version": "0.1.0"
9
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@gvnrdao/dh-lit-actions",
3
- "version": "0.0.111",
3
+ "version": "0.0.192",
4
4
  "type": "module",
5
5
  "description": "Diamond Hands Protocol LIT Actions - Deterministic, Auditable Builds",
6
6
  "main": "pkg-dist/pkg-src/index.js",
7
7
  "types": "pkg-dist/pkg-src/index.d.ts",
8
8
  "files": [
9
- "pkg-dist/**/*"
9
+ "pkg-dist/**/*",
10
+ "dist/**/*"
10
11
  ],
11
12
  "scripts": {
12
13
  "clean": "rm -rf out dist",
@@ -17,6 +18,7 @@
17
18
  "build:package": "rm -rf pkg-dist && tsc -p tsconfig.pkg.json && echo '{\"type\":\"commonjs\"}' > pkg-dist/package.json",
18
19
  "minify": "node --loader ts-node/esm scripts/minify-actions.ts",
19
20
  "deploy:lit-action": "node --loader ts-node/esm scripts/deploy-lit-action.ts",
21
+ "deploy:validator-v9": "node --loader ts-node/esm scripts/deploy-validator-v9.ts",
20
22
  "package": "npm run clean:pkg && npm run build:package",
21
23
  "release": "npm run package",
22
24
  "prepublishOnly": "npm run package",
@@ -30,7 +32,6 @@
30
32
  "collect:cids": "node --loader ts-node/esm scripts/collect-all-cids.ts"
31
33
  },
32
34
  "dependencies": {
33
- "@gvnrdao/dh-lit-actions": "file:gvnrdao-dh-lit-actions-0.0.102.tgz",
34
35
  "@lit-protocol/contracts-sdk": "^7.3.1",
35
36
  "@lit-protocol/lit-node-client": "^7.3.1",
36
37
  "axios": "1.7.7",
@@ -1 +1 @@
1
- {"version":3,"file":"lit-actions-registry.d.ts","sourceRoot":"","sources":["../../../../pkg-src/constants/chunks/lit-actions-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAG1D;;;;GAIG;AAEH,eAAO,MAAM,oBAAoB,EAAE,sBAwSlC,CAAC;AAEF;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,GACf,sBAAsB,CAExB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,wBAAuB,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,6BAA6B,QACG,CAAC;AAC9C,eAAO,MAAM,+BAA+B,QACE,CAAC;AAC/C,eAAO,MAAM,uBAAuB,QAAwC,CAAC"}
1
+ {"version":3,"file":"lit-actions-registry.d.ts","sourceRoot":"","sources":["../../../../pkg-src/constants/chunks/lit-actions-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAG1D;;;;GAIG;AAEH,eAAO,MAAM,oBAAoB,EAAE,sBAySlC,CAAC;AAEF;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,GACf,sBAAsB,CAExB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,wBAAuB,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,6BAA6B,QACG,CAAC;AAC9C,eAAO,MAAM,+BAA+B,QACE,CAAC;AAC/C,eAAO,MAAM,uBAAuB,QAAwC,CAAC"}