@hypurrquant/defi-cli 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +224 -19
- package/dist/main.js.map +1 -1
- package/dist/mcp-server.js +23 -15
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -863,6 +863,10 @@ async function multicallRead(rpcUrl, calls) {
|
|
|
863
863
|
});
|
|
864
864
|
return decoded.map((r) => r.success ? r.returnData : null);
|
|
865
865
|
}
|
|
866
|
+
function decodeU256(data) {
|
|
867
|
+
if (!data || data.length < 66) return 0n;
|
|
868
|
+
return BigInt(data.slice(0, 66));
|
|
869
|
+
}
|
|
866
870
|
var ChainConfig = class {
|
|
867
871
|
name;
|
|
868
872
|
chain_id;
|
|
@@ -6120,7 +6124,7 @@ var POOL_ABI4 = parseAbi27([
|
|
|
6120
6124
|
var ORACLE_ABI5 = parseAbi27([
|
|
6121
6125
|
"function getAssetPrice(address asset) external view returns (uint256)"
|
|
6122
6126
|
]);
|
|
6123
|
-
function
|
|
6127
|
+
function decodeU2562(data, wordOffset = 0) {
|
|
6124
6128
|
if (!data || data.length < 2 + (wordOffset + 1) * 64) return 0n;
|
|
6125
6129
|
const hex = data.slice(2 + wordOffset * 64, 2 + wordOffset * 64 + 64);
|
|
6126
6130
|
return BigInt("0x" + hex);
|
|
@@ -6200,7 +6204,7 @@ function registerPortfolio(parent, getOpts) {
|
|
|
6200
6204
|
let nativePriceUsd = 0;
|
|
6201
6205
|
if (oracleAddr) {
|
|
6202
6206
|
const priceData = results[results.length - 1] ?? null;
|
|
6203
|
-
nativePriceUsd = Number(
|
|
6207
|
+
nativePriceUsd = Number(decodeU2562(priceData)) / 1e8;
|
|
6204
6208
|
}
|
|
6205
6209
|
let totalValueUsd = 0;
|
|
6206
6210
|
let idx = 0;
|
|
@@ -6214,7 +6218,7 @@ function registerPortfolio(parent, getOpts) {
|
|
|
6214
6218
|
}
|
|
6215
6219
|
if (entry.address === "0x0000000000000000000000000000000000000000") continue;
|
|
6216
6220
|
if (idx >= results.length) break;
|
|
6217
|
-
const balance =
|
|
6221
|
+
const balance = decodeU2562(results[idx] ?? null);
|
|
6218
6222
|
if (balance > 0n) {
|
|
6219
6223
|
const decimals = entry.decimals;
|
|
6220
6224
|
const balF64 = Number(balance) / 10 ** decimals;
|
|
@@ -6234,9 +6238,9 @@ function registerPortfolio(parent, getOpts) {
|
|
|
6234
6238
|
if (idx >= results.length) break;
|
|
6235
6239
|
const data = results[idx] ?? null;
|
|
6236
6240
|
if (data && data.length >= 2 + 192 * 2) {
|
|
6237
|
-
const collateral = Number(
|
|
6238
|
-
const debt = Number(
|
|
6239
|
-
const hfRaw =
|
|
6241
|
+
const collateral = Number(decodeU2562(data, 0)) / 1e8;
|
|
6242
|
+
const debt = Number(decodeU2562(data, 1)) / 1e8;
|
|
6243
|
+
const hfRaw = decodeU2562(data, 5);
|
|
6240
6244
|
let hf = null;
|
|
6241
6245
|
if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
|
|
6242
6246
|
const v = Number(hfRaw) / 1e18;
|
|
@@ -7078,7 +7082,7 @@ function estimateTokenValue(symbol, balance, nativePrice) {
|
|
|
7078
7082
|
if (["WETH", "ETH", "METH", "CBETH", "WSTETH"].includes(s)) return balance * 2350;
|
|
7079
7083
|
return balance * nativePrice;
|
|
7080
7084
|
}
|
|
7081
|
-
function
|
|
7085
|
+
function decodeU2563(data, offset = 0) {
|
|
7082
7086
|
if (!data || data.length < 2 + (offset + 32) * 2) return 0n;
|
|
7083
7087
|
const hex = data.slice(2 + offset * 64, 2 + offset * 64 + 64);
|
|
7084
7088
|
return BigInt("0x" + hex);
|
|
@@ -7116,7 +7120,7 @@ async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracl
|
|
|
7116
7120
|
} catch {
|
|
7117
7121
|
return null;
|
|
7118
7122
|
}
|
|
7119
|
-
const nativePrice = oracleAddr ? Number(
|
|
7123
|
+
const nativePrice = oracleAddr ? Number(decodeU2563(results[results.length - 1])) / 1e8 : 0;
|
|
7120
7124
|
const tokenBalances = [];
|
|
7121
7125
|
const lendingPositions = [];
|
|
7122
7126
|
let chainValue = 0;
|
|
@@ -7126,7 +7130,7 @@ async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracl
|
|
|
7126
7130
|
const ct = callTypes[i];
|
|
7127
7131
|
const data = results[i] ?? null;
|
|
7128
7132
|
if (ct.kind === "token") {
|
|
7129
|
-
const balance =
|
|
7133
|
+
const balance = decodeU2563(data);
|
|
7130
7134
|
if (balance > 0n) {
|
|
7131
7135
|
const balF64 = Number(balance) / 10 ** ct.decimals;
|
|
7132
7136
|
const valueUsd = estimateTokenValue(ct.symbol, balF64, nativePrice);
|
|
@@ -7143,9 +7147,9 @@ async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracl
|
|
|
7143
7147
|
if (data && data.length >= 2 + 192 * 2) {
|
|
7144
7148
|
const priceDecimals = ct.iface === "aave_v2" ? 18 : 8;
|
|
7145
7149
|
const divisor = 10 ** priceDecimals;
|
|
7146
|
-
const collateral = Number(
|
|
7147
|
-
const debt = Number(
|
|
7148
|
-
const hfRaw =
|
|
7150
|
+
const collateral = Number(decodeU2563(data, 0)) / divisor;
|
|
7151
|
+
const debt = Number(decodeU2563(data, 1)) / divisor;
|
|
7152
|
+
const hfRaw = decodeU2563(data, 5);
|
|
7149
7153
|
let hf = null;
|
|
7150
7154
|
if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
|
|
7151
7155
|
const v = Number(hfRaw) / 1e18;
|
|
@@ -7491,7 +7495,7 @@ function round24(x) {
|
|
|
7491
7495
|
function round43(x) {
|
|
7492
7496
|
return Math.round(x * 1e4) / 1e4;
|
|
7493
7497
|
}
|
|
7494
|
-
function
|
|
7498
|
+
function decodeU2564(data, wordOffset = 0) {
|
|
7495
7499
|
if (!data || data.length < 2 + (wordOffset + 1) * 64) return 0n;
|
|
7496
7500
|
const hex = data.slice(2 + wordOffset * 64, 2 + wordOffset * 64 + 64);
|
|
7497
7501
|
return BigInt("0x" + hex);
|
|
@@ -7616,9 +7620,9 @@ function registerWhales(parent, getOpts) {
|
|
|
7616
7620
|
if (data && data.length >= 2 + 192 * 2) {
|
|
7617
7621
|
const dec = iface === "aave_v2" ? 18 : 8;
|
|
7618
7622
|
const divisor = 10 ** dec;
|
|
7619
|
-
const collateral = Number(
|
|
7620
|
-
const debt = Number(
|
|
7621
|
-
const hfRaw =
|
|
7623
|
+
const collateral = Number(decodeU2564(data, 0)) / divisor;
|
|
7624
|
+
const debt = Number(decodeU2564(data, 1)) / divisor;
|
|
7625
|
+
const hfRaw = decodeU2564(data, 5);
|
|
7622
7626
|
let hf = null;
|
|
7623
7627
|
if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
|
|
7624
7628
|
const v = Number(hfRaw) / 1e18;
|
|
@@ -8021,11 +8025,11 @@ function registerBridge(parent, getOpts) {
|
|
|
8021
8025
|
const amountUsdc = Number(BigInt(opts.amount)) / 1e6;
|
|
8022
8026
|
const { fee, maxFeeSubunits } = await getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc);
|
|
8023
8027
|
const recipientPadded = `0x${"0".repeat(24)}${recipient.replace("0x", "").toLowerCase()}`;
|
|
8024
|
-
const { encodeFunctionData:
|
|
8025
|
-
const tokenMessengerAbi =
|
|
8028
|
+
const { encodeFunctionData: encodeFunctionData29, parseAbi: parseAbi33 } = await import("viem");
|
|
8029
|
+
const tokenMessengerAbi = parseAbi33([
|
|
8026
8030
|
"function depositForBurn(uint256 amount, uint32 destinationDomain, bytes32 mintRecipient, address burnToken, bytes32 destinationCaller, uint256 maxFee, uint32 minFinalityThreshold) external returns (uint64 nonce)"
|
|
8027
8031
|
]);
|
|
8028
|
-
const data =
|
|
8032
|
+
const data = encodeFunctionData29({
|
|
8029
8033
|
abi: tokenMessengerAbi,
|
|
8030
8034
|
functionName: "depositForBurn",
|
|
8031
8035
|
args: [
|
|
@@ -8274,9 +8278,210 @@ program.command("agent").description("Agent mode: read JSON commands from stdin
|
|
|
8274
8278
|
process.exit(1);
|
|
8275
8279
|
});
|
|
8276
8280
|
|
|
8281
|
+
// src/landing.ts
|
|
8282
|
+
import pc2 from "picocolors";
|
|
8283
|
+
import { encodeFunctionData as encodeFunctionData28, parseAbi as parseAbi31, formatUnits } from "viem";
|
|
8284
|
+
var HYPEREVM_DISPLAY = ["HYPE", "WHYPE", "USDC", "USDT0", "USDe", "kHYPE", "wstHYPE"];
|
|
8285
|
+
var MANTLE_DISPLAY = ["MNT", "WMNT", "USDC", "USDT", "WETH", "mETH"];
|
|
8286
|
+
var balanceOfAbi = parseAbi31([
|
|
8287
|
+
"function balanceOf(address account) view returns (uint256)"
|
|
8288
|
+
]);
|
|
8289
|
+
var getEthBalanceAbi = parseAbi31([
|
|
8290
|
+
"function getEthBalance(address addr) view returns (uint256)"
|
|
8291
|
+
]);
|
|
8292
|
+
async function fetchBalances(rpcUrl, wallet, tokens) {
|
|
8293
|
+
const calls = tokens.map((t) => {
|
|
8294
|
+
const isNative = t.tags?.includes("native") || t.address === "0x0000000000000000000000000000000000000000";
|
|
8295
|
+
if (isNative) {
|
|
8296
|
+
return [
|
|
8297
|
+
MULTICALL3_ADDRESS,
|
|
8298
|
+
encodeFunctionData28({
|
|
8299
|
+
abi: getEthBalanceAbi,
|
|
8300
|
+
functionName: "getEthBalance",
|
|
8301
|
+
args: [wallet]
|
|
8302
|
+
})
|
|
8303
|
+
];
|
|
8304
|
+
}
|
|
8305
|
+
return [
|
|
8306
|
+
t.address,
|
|
8307
|
+
encodeFunctionData28({
|
|
8308
|
+
abi: balanceOfAbi,
|
|
8309
|
+
functionName: "balanceOf",
|
|
8310
|
+
args: [wallet]
|
|
8311
|
+
})
|
|
8312
|
+
];
|
|
8313
|
+
});
|
|
8314
|
+
let results;
|
|
8315
|
+
try {
|
|
8316
|
+
results = await multicallRead(rpcUrl, calls);
|
|
8317
|
+
} catch {
|
|
8318
|
+
results = tokens.map(() => null);
|
|
8319
|
+
}
|
|
8320
|
+
return tokens.map((t, i) => {
|
|
8321
|
+
const raw = decodeU256(results[i]);
|
|
8322
|
+
const formatted = formatUnits(raw, t.decimals);
|
|
8323
|
+
const num = parseFloat(formatted);
|
|
8324
|
+
const display = num === 0 ? "0.00" : num >= 1e3 ? num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 });
|
|
8325
|
+
return { symbol: t.symbol, balance: display, decimals: t.decimals };
|
|
8326
|
+
});
|
|
8327
|
+
}
|
|
8328
|
+
function shortenAddress(addr) {
|
|
8329
|
+
if (addr.length < 12) return addr;
|
|
8330
|
+
return `${addr.slice(0, 6)}...${addr.slice(-4)}`;
|
|
8331
|
+
}
|
|
8332
|
+
function padRight(s, len) {
|
|
8333
|
+
return s.length >= len ? s : s + " ".repeat(len - s.length);
|
|
8334
|
+
}
|
|
8335
|
+
function padLeft(s, len) {
|
|
8336
|
+
return s.length >= len ? s : " ".repeat(len - s.length) + s;
|
|
8337
|
+
}
|
|
8338
|
+
function formatBalanceLine(sym, bal) {
|
|
8339
|
+
const symPad = padRight(sym, 10);
|
|
8340
|
+
const balPad = padLeft(bal, 12);
|
|
8341
|
+
return ` ${symPad}${balPad}`;
|
|
8342
|
+
}
|
|
8343
|
+
async function showLandingPage(isJson) {
|
|
8344
|
+
const registry = Registry.loadEmbedded();
|
|
8345
|
+
const wallet = process.env.DEFI_WALLET_ADDRESS;
|
|
8346
|
+
if (isJson) {
|
|
8347
|
+
if (!wallet) {
|
|
8348
|
+
console.log(JSON.stringify({ error: "DEFI_WALLET_ADDRESS not set" }, null, 2));
|
|
8349
|
+
return;
|
|
8350
|
+
}
|
|
8351
|
+
const heChain2 = registry.getChain("hyperevm");
|
|
8352
|
+
const mantleChain2 = registry.getChain("mantle");
|
|
8353
|
+
const heTokens2 = (registry.tokens.get("hyperevm") ?? []).filter((t) => HYPEREVM_DISPLAY.includes(t.symbol));
|
|
8354
|
+
const mantleTokens2 = (registry.tokens.get("mantle") ?? []).filter((t) => MANTLE_DISPLAY.includes(t.symbol));
|
|
8355
|
+
const heSorted2 = HYPEREVM_DISPLAY.map((s) => heTokens2.find((t) => t.symbol === s)).filter(Boolean);
|
|
8356
|
+
const mantleSorted2 = MANTLE_DISPLAY.map((s) => mantleTokens2.find((t) => t.symbol === s)).filter(Boolean);
|
|
8357
|
+
const [heBalances2, mantleBalances2] = await Promise.all([
|
|
8358
|
+
fetchBalances(heChain2.effectiveRpcUrl(), wallet, heSorted2),
|
|
8359
|
+
fetchBalances(mantleChain2.effectiveRpcUrl(), wallet, mantleSorted2)
|
|
8360
|
+
]);
|
|
8361
|
+
console.log(JSON.stringify({
|
|
8362
|
+
wallet,
|
|
8363
|
+
chains: {
|
|
8364
|
+
hyperevm: { name: heChain2.name, balances: heBalances2 },
|
|
8365
|
+
mantle: { name: mantleChain2.name, balances: mantleBalances2 }
|
|
8366
|
+
}
|
|
8367
|
+
}, null, 2));
|
|
8368
|
+
return;
|
|
8369
|
+
}
|
|
8370
|
+
const version = "0.2.0";
|
|
8371
|
+
if (!wallet) {
|
|
8372
|
+
console.log("");
|
|
8373
|
+
console.log(pc2.bold(pc2.cyan(" DeFi CLI v" + version)));
|
|
8374
|
+
console.log("");
|
|
8375
|
+
console.log(pc2.yellow(" Wallet not configured."));
|
|
8376
|
+
console.log(" Set DEFI_WALLET_ADDRESS to see your balances:");
|
|
8377
|
+
console.log("");
|
|
8378
|
+
console.log(pc2.dim(" export DEFI_WALLET_ADDRESS=0x..."));
|
|
8379
|
+
console.log("");
|
|
8380
|
+
console.log(" Commands:");
|
|
8381
|
+
console.log(pc2.dim(" defi status Protocol overview"));
|
|
8382
|
+
console.log(pc2.dim(" defi lending rates Compare lending APYs"));
|
|
8383
|
+
console.log(pc2.dim(" defi dex quote Get swap quotes"));
|
|
8384
|
+
console.log(pc2.dim(" defi portfolio View all positions"));
|
|
8385
|
+
console.log(pc2.dim(" defi scan Exploit detection"));
|
|
8386
|
+
console.log(pc2.dim(" defi --help Full command list"));
|
|
8387
|
+
console.log("");
|
|
8388
|
+
return;
|
|
8389
|
+
}
|
|
8390
|
+
const heChain = registry.getChain("hyperevm");
|
|
8391
|
+
const mantleChain = registry.getChain("mantle");
|
|
8392
|
+
const heTokens = (registry.tokens.get("hyperevm") ?? []).filter((t) => HYPEREVM_DISPLAY.includes(t.symbol));
|
|
8393
|
+
const mantleTokens = (registry.tokens.get("mantle") ?? []).filter((t) => MANTLE_DISPLAY.includes(t.symbol));
|
|
8394
|
+
const heSorted = HYPEREVM_DISPLAY.map((s) => heTokens.find((t) => t.symbol === s)).filter(Boolean);
|
|
8395
|
+
const mantleSorted = MANTLE_DISPLAY.map((s) => mantleTokens.find((t) => t.symbol === s)).filter(Boolean);
|
|
8396
|
+
const [heBalances, mantleBalances] = await Promise.all([
|
|
8397
|
+
fetchBalances(heChain.effectiveRpcUrl(), wallet, heSorted).catch(
|
|
8398
|
+
() => heSorted.map((t) => ({ symbol: t.symbol, balance: "?", decimals: t.decimals }))
|
|
8399
|
+
),
|
|
8400
|
+
fetchBalances(mantleChain.effectiveRpcUrl(), wallet, mantleSorted).catch(
|
|
8401
|
+
() => mantleSorted.map((t) => ({ symbol: t.symbol, balance: "?", decimals: t.decimals }))
|
|
8402
|
+
)
|
|
8403
|
+
]);
|
|
8404
|
+
const colWidth = 38;
|
|
8405
|
+
const divider = "\u2500".repeat(colWidth - 2);
|
|
8406
|
+
console.log("");
|
|
8407
|
+
console.log(
|
|
8408
|
+
pc2.bold(pc2.cyan(" DeFi CLI v" + version)) + pc2.dim(" \u2014 ") + pc2.bold(heChain.name) + pc2.dim(" \xB7 ") + pc2.bold(mantleChain.name)
|
|
8409
|
+
);
|
|
8410
|
+
console.log("");
|
|
8411
|
+
console.log(" Wallet: " + pc2.yellow(shortenAddress(wallet)));
|
|
8412
|
+
console.log("");
|
|
8413
|
+
const heHeader = padRight(
|
|
8414
|
+
" " + pc2.bold(heChain.name),
|
|
8415
|
+
colWidth + 10
|
|
8416
|
+
/* account for ANSI */
|
|
8417
|
+
);
|
|
8418
|
+
const mantleHeader = pc2.bold(mantleChain.name);
|
|
8419
|
+
console.log(heHeader + " " + mantleHeader);
|
|
8420
|
+
const heDivider = padRight(" " + pc2.dim(divider), colWidth + 10);
|
|
8421
|
+
const mantleDivider = pc2.dim(divider);
|
|
8422
|
+
console.log(heDivider + " " + mantleDivider);
|
|
8423
|
+
const maxRows = Math.max(heBalances.length, mantleBalances.length);
|
|
8424
|
+
for (let i = 0; i < maxRows; i++) {
|
|
8425
|
+
const heEntry = heBalances[i];
|
|
8426
|
+
const mantleEntry = mantleBalances[i];
|
|
8427
|
+
const heText = heEntry ? formatBalanceLine(heEntry.symbol, heEntry.balance) : "";
|
|
8428
|
+
const mantleText = mantleEntry ? formatBalanceLine(mantleEntry.symbol, mantleEntry.balance) : "";
|
|
8429
|
+
const heColored = heEntry ? heEntry.balance === "0.00" || heEntry.balance === "?" ? pc2.dim(heText) : heText : "";
|
|
8430
|
+
const mantleColored = mantleEntry ? mantleEntry.balance === "0.00" || mantleEntry.balance === "?" ? pc2.dim(mantleText) : mantleText : "";
|
|
8431
|
+
const visibleLen = heText.length;
|
|
8432
|
+
const padNeeded = colWidth - visibleLen;
|
|
8433
|
+
const paddedHe = heColored + (padNeeded > 0 ? " ".repeat(padNeeded) : "");
|
|
8434
|
+
console.log(paddedHe + " " + mantleColored);
|
|
8435
|
+
}
|
|
8436
|
+
console.log("");
|
|
8437
|
+
console.log(" " + pc2.bold("Commands:"));
|
|
8438
|
+
console.log(" " + pc2.cyan("defi status") + " Protocol overview");
|
|
8439
|
+
console.log(" " + pc2.cyan("defi lending rates") + " Compare lending APYs");
|
|
8440
|
+
console.log(" " + pc2.cyan("defi dex quote") + " Get swap quotes");
|
|
8441
|
+
console.log(" " + pc2.cyan("defi portfolio") + " View all positions");
|
|
8442
|
+
console.log(" " + pc2.cyan("defi scan") + " Exploit detection");
|
|
8443
|
+
console.log(" " + pc2.cyan("defi --help") + " Full command list");
|
|
8444
|
+
console.log("");
|
|
8445
|
+
}
|
|
8446
|
+
|
|
8277
8447
|
// src/main.ts
|
|
8278
8448
|
async function main() {
|
|
8279
8449
|
try {
|
|
8450
|
+
const rawArgs = process.argv.slice(2);
|
|
8451
|
+
const knownSubcommands = /* @__PURE__ */ new Set([
|
|
8452
|
+
"status",
|
|
8453
|
+
"schema",
|
|
8454
|
+
"dex",
|
|
8455
|
+
"gauge",
|
|
8456
|
+
"lending",
|
|
8457
|
+
"cdp",
|
|
8458
|
+
"staking",
|
|
8459
|
+
"vault",
|
|
8460
|
+
"yield",
|
|
8461
|
+
"portfolio",
|
|
8462
|
+
"monitor",
|
|
8463
|
+
"alert",
|
|
8464
|
+
"scan",
|
|
8465
|
+
"arb",
|
|
8466
|
+
"positions",
|
|
8467
|
+
"price",
|
|
8468
|
+
"wallet",
|
|
8469
|
+
"token",
|
|
8470
|
+
"whales",
|
|
8471
|
+
"compare",
|
|
8472
|
+
"swap",
|
|
8473
|
+
"bridge",
|
|
8474
|
+
"nft",
|
|
8475
|
+
"farm",
|
|
8476
|
+
"agent"
|
|
8477
|
+
]);
|
|
8478
|
+
const hasSubcommand = rawArgs.some((a) => !a.startsWith("-") && knownSubcommands.has(a));
|
|
8479
|
+
const isJson = rawArgs.includes("--json") || rawArgs.includes("--ndjson");
|
|
8480
|
+
const isHelp = rawArgs.includes("--help") || rawArgs.includes("-h");
|
|
8481
|
+
if (!isHelp && (rawArgs.length === 0 || !hasSubcommand)) {
|
|
8482
|
+
await showLandingPage(isJson);
|
|
8483
|
+
return;
|
|
8484
|
+
}
|
|
8280
8485
|
await program.parseAsync(process.argv);
|
|
8281
8486
|
} catch (error) {
|
|
8282
8487
|
const isJsonMode = process.argv.includes("--json") || process.argv.includes("--ndjson");
|