@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.
- package/out/ucd-mint-validator.hash +1 -1
- package/out/ucd-mint-validator.js +96 -101
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
751
|
-
|
|
752
|
-
|
|
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
|
|
772
|
-
|
|
773
|
-
|
|
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
|
|
791
|
-
|
|
792
|
-
|
|
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 <=
|
|
876
|
+
return deviation <= OUTLIER_DEVIATION;
|
|
878
877
|
});
|
|
879
|
-
if (dispersionRatio > 1 +
|
|
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
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
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
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
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
|
[
|