@gvnrdao/dh-lit-actions 0.0.28 → 0.0.29

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.
@@ -1 +1 @@
1
- 5cf86424936e8691f73e6ff8b9a905da15de40ef883495b9cfe79becaaee2c95
1
+ e8e09a379fce1f039c3cd593d07b5bade9acb509d4ef93aec46a2a4dda27f346
@@ -11,10 +11,6 @@ var _LIT_ACTION_ = (() => {
11
11
  var PRICE_ORACLE_DECIMALS = 100000000n;
12
12
  var UCD_TOKEN_DECIMALS = 1000000000000000000n;
13
13
 
14
- // src/constants/chunks/price-oracle.ts
15
- var PRICE_ORACLE_OUTLIER_DEVIATION_THRESHOLD = 0.02;
16
- var PRICE_ORACLE_DISPERSION_THRESHOLD = 0.05;
17
-
18
14
  // src/constants/chunks/bitcoin.ts
19
15
  var BITCOIN_DEFAULT_MIN_CONFIRMATIONS = 6;
20
16
 
@@ -230,43 +226,6 @@ var _LIT_ACTION_ = (() => {
230
226
  function getQuantumStart(timestamp) {
231
227
  return Math.floor(timestamp / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
232
228
  }
233
- function getCurrentQuantumTimestamp() {
234
- const now = Math.floor(Date.now() / 1e3);
235
- return getQuantumStart(now);
236
- }
237
- function isInDeadZone(now) {
238
- const currentTime = now ?? Math.floor(Date.now() / 1e3);
239
- const timeInQuantum = currentTime % QUANTUM_WINDOW_SECONDS;
240
- return timeInQuantum >= 92 || timeInQuantum < 8;
241
- }
242
- async function waitForSafeQuantumMoment() {
243
- const now = Math.floor(Date.now() / 1e3);
244
- const timeInQuantum = now % QUANTUM_WINDOW_SECONDS;
245
- if (!isInDeadZone(now)) {
246
- return;
247
- }
248
- let waitSeconds;
249
- if (timeInQuantum >= 92) {
250
- waitSeconds = 100 - timeInQuantum + 8;
251
- } else {
252
- waitSeconds = 8 - timeInQuantum;
253
- }
254
- console.log(
255
- `[Quantum] Waiting ${waitSeconds}s for safe moment (current time in quantum: ${timeInQuantum}s)`
256
- );
257
- await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1e3));
258
- console.log(`[Quantum] Safe quantum ready: ${getCurrentQuantumTimestamp()}`);
259
- }
260
- function validateQuantumTimestamp(timestampOnSignature, now) {
261
- const currentTime = now ?? Math.floor(Date.now() / 1e3);
262
- const currentQuantum = getQuantumStart(currentTime);
263
- const signatureQuantum = getQuantumStart(timestampOnSignature);
264
- if (signatureQuantum !== currentQuantum) {
265
- throw new Error(
266
- `[Quantum] Invalid timestamp: signature from quantum ${signatureQuantum}, current quantum is ${currentQuantum}`
267
- );
268
- }
269
- }
270
229
 
271
230
  // src/modules/authorization.module.ts
272
231
  var AuthorizationModule = class {
@@ -287,9 +246,26 @@ var _LIT_ACTION_ = (() => {
287
246
  * @returns True if authorized
288
247
  */
289
248
  static async verifyOwnerSignature(litActionName, auth, expectedOwner, actionParams) {
290
- await waitForSafeQuantumMoment();
291
249
  try {
292
- validateQuantumTimestamp(auth.timestamp);
250
+ const now = Math.floor(Date.now() / 1e3);
251
+ const currentQuantum = getQuantumStart(now);
252
+ const signatureQuantum = getQuantumStart(auth.timestamp);
253
+ const previousQuantum = currentQuantum - QUANTUM_WINDOW_SECONDS;
254
+ const previousQuantum2 = previousQuantum - QUANTUM_WINDOW_SECONDS;
255
+ if (signatureQuantum !== currentQuantum && signatureQuantum !== previousQuantum && signatureQuantum !== previousQuantum2) {
256
+ console.error(
257
+ "[Authorization] Quantum timestamp outside allowed window:",
258
+ `
259
+ Signature quantum: ${signatureQuantum}`,
260
+ `
261
+ Current quantum: ${currentQuantum}`,
262
+ `
263
+ Previous quantum: ${previousQuantum}`,
264
+ `
265
+ Previous-2 quantum: ${previousQuantum2}`
266
+ );
267
+ return false;
268
+ }
293
269
  } catch (error) {
294
270
  console.error(
295
271
  "[Authorization] Quantum timestamp validation failed:",
@@ -413,7 +389,11 @@ var _LIT_ACTION_ = (() => {
413
389
  positionOwner,
414
390
  {
415
391
  action: ethers.utils.keccak256(ethers.utils.toUtf8Bytes("mintUCD")),
416
- amount: ethers.utils.hexZeroPad(ethers.utils.hexlify(amount), 32)
392
+ // Normalize amount to a consistent 32-byte hex for signing regardless of input type
393
+ amount: ethers.utils.hexZeroPad(
394
+ ethers.utils.hexlify(ethers.BigNumber.from(amount)),
395
+ 32
396
+ )
417
397
  }
418
398
  );
419
399
  }
@@ -475,7 +455,9 @@ var _LIT_ACTION_ = (() => {
475
455
  * @returns Array of UTXOs
476
456
  */
477
457
  async getUTXOs(address) {
458
+ console.log(`[Bitcoin Data] Original address: "${address}"`);
478
459
  const cleanAddress = this.stripNetworkPrefix(address);
460
+ console.log(`[Bitcoin Data] Cleaned address: "${cleanAddress}"`);
479
461
  if (this.config.rpcHelper) {
480
462
  return await this.fetchUTXOsFromRPC(cleanAddress);
481
463
  }
@@ -627,6 +609,7 @@ var _LIT_ACTION_ = (() => {
627
609
  );
628
610
  }
629
611
  const data = await response.json();
612
+ console.log(`[Line 222 Context] Raw API Response:`, JSON.stringify(data, null, 2));
630
613
  if (providerName === "Diamond Hands") {
631
614
  return this.parseDiamondHandsFaucetUTXOs(data);
632
615
  } else {
@@ -740,6 +723,23 @@ var _LIT_ACTION_ = (() => {
740
723
  this.sources.sort((a, b) => a.priority - b.priority);
741
724
  }
742
725
  getDefaultSources() {
726
+ const DEFAULT_HEADERS = {
727
+ "User-Agent": "diamond-hands-lit-action/1.0",
728
+ Accept: "application/json"
729
+ };
730
+ const fetchJson = async (url) => {
731
+ const controller = new AbortController();
732
+ const id = setTimeout(() => controller.abort(), 8e3);
733
+ try {
734
+ const res = await fetch(url, { headers: DEFAULT_HEADERS, signal: controller.signal });
735
+ if (!res.ok) {
736
+ throw new Error(`HTTP ${res.status} ${res.statusText}`);
737
+ }
738
+ return await res.json();
739
+ } finally {
740
+ clearTimeout(id);
741
+ }
742
+ };
743
743
  return [
744
744
  {
745
745
  name: "CoinGecko",
@@ -747,12 +747,11 @@ var _LIT_ACTION_ = (() => {
747
747
  const url = new URL("https://api.coingecko.com/api/v3/simple/price");
748
748
  url.searchParams.set("ids", "bitcoin");
749
749
  url.searchParams.set("vs_currencies", "usd");
750
- const response = await fetch(url.toString());
751
- if (!response.ok) {
752
- throw new Error(`CoinGecko API error: ${response.status} ${response.statusText}`);
750
+ const data = await fetchJson(url.toString());
751
+ const price = Number(data?.bitcoin?.usd);
752
+ if (!Number.isFinite(price) || price <= 0) {
753
+ throw new Error("Invalid CoinGecko price payload");
753
754
  }
754
- const data = await response.json();
755
- const price = data.bitcoin.usd;
756
755
  const allPrices = await Lit.Actions.broadcastAndCollect({
757
756
  name: "coinGeckoPrice",
758
757
  value: price.toString()
@@ -768,12 +767,11 @@ var _LIT_ACTION_ = (() => {
768
767
  fetchPrice: async () => {
769
768
  const url = new URL("https://api.binance.com/api/v3/ticker/price");
770
769
  url.searchParams.set("symbol", "BTCUSDT");
771
- const response = await fetch(url.toString());
772
- if (!response.ok) {
773
- throw new Error(`Binance API error: ${response.status} ${response.statusText}`);
770
+ const data = await fetchJson(url.toString());
771
+ const price = Number(data?.price);
772
+ if (!Number.isFinite(price) || price <= 0) {
773
+ throw new Error("Invalid Binance price payload");
774
774
  }
775
- const data = await response.json();
776
- const price = parseFloat(data.price);
777
775
  const allPrices = await Lit.Actions.broadcastAndCollect({
778
776
  name: "binancePrice",
779
777
  value: price.toString()
@@ -787,12 +785,11 @@ var _LIT_ACTION_ = (() => {
787
785
  {
788
786
  name: "Coinbase",
789
787
  fetchPrice: async () => {
790
- const response = await fetch("https://api.coinbase.com/v2/prices/BTC-USD/spot");
791
- if (!response.ok) {
792
- throw new Error(`Coinbase API error: ${response.status} ${response.statusText}`);
788
+ const data = await fetchJson("https://api.coinbase.com/v2/prices/BTC-USD/spot");
789
+ const price = Number(data?.data?.amount);
790
+ if (!Number.isFinite(price) || price <= 0) {
791
+ throw new Error("Invalid Coinbase price payload");
793
792
  }
794
- const data = await response.json();
795
- const price = parseFloat(data.data.amount);
796
793
  const allPrices = await Lit.Actions.broadcastAndCollect({
797
794
  name: "coinbasePrice",
798
795
  value: price.toString()
@@ -850,6 +847,8 @@ var _LIT_ACTION_ = (() => {
850
847
  */
851
848
  async getBTCPriceConsensus() {
852
849
  console.log(`[Price Oracle] Fetching BTC price with consensus...`);
850
+ const OUTLIER_DEVIATION = 5e-3;
851
+ const DISPERSION_THRESHOLD = 0.05;
853
852
  const results = await Promise.allSettled(
854
853
  this.sources.map(async (source) => {
855
854
  const price = await source.fetchPrice();
@@ -874,9 +873,9 @@ var _LIT_ACTION_ = (() => {
874
873
  const dispersionRatio = maxPrice / minPrice;
875
874
  const validPrices = successful.filter((s) => {
876
875
  const deviation = Math.abs(s.price - initialMedian) / initialMedian;
877
- return deviation <= PRICE_ORACLE_OUTLIER_DEVIATION_THRESHOLD;
876
+ return deviation <= OUTLIER_DEVIATION;
878
877
  });
879
- if (dispersionRatio > 1 + PRICE_ORACLE_DISPERSION_THRESHOLD && validPrices.length === successful.length) {
878
+ if (dispersionRatio > 1 + DISPERSION_THRESHOLD && validPrices.length === successful.length) {
880
879
  throw new Error(
881
880
  `Price consensus failed: sources too dispersed (${((dispersionRatio - 1) * 100).toFixed(1)}% spread)`
882
881
  );
@@ -1262,24 +1261,21 @@ var _LIT_ACTION_ = (() => {
1262
1261
  * @returns Liquidation threshold in basis points
1263
1262
  */
1264
1263
  async getLiquidationThreshold() {
1265
- const abi = [
1266
- {
1267
- inputs: [],
1268
- name: "liquidationThreshold",
1269
- outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
1270
- stateMutability: "view",
1271
- type: "function"
1272
- }
1273
- ];
1274
1264
  try {
1275
- const result = await Lit.Actions.call({
1276
- chain: this.chain,
1277
- contractAddress: this.loanOpsManagerAddress,
1278
- abi,
1279
- methodName: "liquidationThreshold",
1280
- params: []
1281
- });
1282
- return Number(result);
1265
+ const rpcUrl = await Lit.Actions.getRpcUrl({ chain: this.chain });
1266
+ const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
1267
+ const abi = [
1268
+ {
1269
+ inputs: [],
1270
+ name: "liquidationThreshold",
1271
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
1272
+ stateMutability: "view",
1273
+ type: "function"
1274
+ }
1275
+ ];
1276
+ const contract = new ethers.Contract(this.loanOpsManagerAddress, abi, provider);
1277
+ const result = await contract.liquidationThreshold();
1278
+ return Number(result.toString());
1283
1279
  } catch (error) {
1284
1280
  console.error(
1285
1281
  "[ProtocolParameters] Error fetching liquidation threshold:",
@@ -1297,31 +1293,28 @@ var _LIT_ACTION_ = (() => {
1297
1293
  * @returns Term fees (origination and extension) in basis points
1298
1294
  */
1299
1295
  async getTermFees(termMonths) {
1300
- const abi = [
1301
- {
1302
- inputs: [
1303
- { internalType: "uint256", name: "_termMonths", type: "uint256" }
1304
- ],
1305
- name: "getTermFees",
1306
- outputs: [
1307
- { internalType: "uint88", name: "originationFee", type: "uint88" },
1308
- { internalType: "uint88", name: "extensionFee", type: "uint88" }
1309
- ],
1310
- stateMutability: "view",
1311
- type: "function"
1312
- }
1313
- ];
1314
1296
  try {
1315
- const result = await Lit.Actions.call({
1316
- chain: this.chain,
1317
- contractAddress: this.termManagerAddress,
1318
- abi,
1319
- methodName: "getTermFees",
1320
- params: [termMonths]
1321
- });
1297
+ const rpcUrl = await Lit.Actions.getRpcUrl({ chain: this.chain });
1298
+ const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
1299
+ const abi = [
1300
+ {
1301
+ inputs: [
1302
+ { internalType: "uint256", name: "_termMonths", type: "uint256" }
1303
+ ],
1304
+ name: "getTermFees",
1305
+ outputs: [
1306
+ { internalType: "uint88", name: "originationFee", type: "uint88" },
1307
+ { internalType: "uint88", name: "extensionFee", type: "uint88" }
1308
+ ],
1309
+ stateMutability: "view",
1310
+ type: "function"
1311
+ }
1312
+ ];
1313
+ const contract = new ethers.Contract(this.termManagerAddress, abi, provider);
1314
+ const result = await contract.getTermFees(termMonths);
1322
1315
  return {
1323
- originationFeeBps: Number(result[0]),
1324
- extensionFeeBps: Number(result[1])
1316
+ originationFeeBps: Number(result.originationFee?.toString?.() ?? result[0]?.toString?.() ?? result[0]),
1317
+ extensionFeeBps: Number(result.extensionFee?.toString?.() ?? result[1]?.toString?.() ?? result[1])
1325
1318
  };
1326
1319
  } catch (error) {
1327
1320
  console.error(
@@ -1352,6 +1345,7 @@ var _LIT_ACTION_ = (() => {
1352
1345
  */
1353
1346
  async getVaultSnapshot(positionId) {
1354
1347
  const positionState = await this.queryPositionState(positionId);
1348
+ console.log(`[Vault Snapshot] Raw vault address from contract: "${positionState.vaultAddress}"`);
1355
1349
  const balanceResult = await this.config.vaultBalance.calculateTrustedBalance(
1356
1350
  positionId,
1357
1351
  positionState.vaultAddress
@@ -2109,6 +2103,7 @@ var _LIT_ACTION_ = (() => {
2109
2103
  "bytes32",
2110
2104
  "bytes32",
2111
2105
  "bytes32",
2106
+ "bytes32",
2112
2107
  "uint256"
2113
2108
  ],
2114
2109
  [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gvnrdao/dh-lit-actions",
3
- "version": "0.0.28",
3
+ "version": "0.0.29",
4
4
  "type": "module",
5
5
  "description": "Diamond Hands Protocol LIT Actions - Deterministic, Auditable Builds",
6
6
  "main": "./pkg-dist/index.cjs",