@hypurrquant/defi-cli 1.0.11 → 1.0.12
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/index.js +317 -67
- package/dist/index.js.map +1 -1
- package/dist/main.js +317 -67
- package/dist/main.js.map +1 -1
- package/dist/mcp-server.js +13 -3
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/skills/defi-cli/SKILL.md +37 -1
- package/skills/defi-cli/references/commands.md +39 -6
- package/skills/defi-cli/scripts/bridge-quote.sh +34 -0
- package/skills/defi-cli/scripts/lending-supply-flow.sh +28 -0
- package/skills/defi-cli/scripts/lp-claim-all.sh +25 -0
- package/skills/defi-cli/scripts/lp-emission-discover.sh +20 -0
- package/skills/defi-cli/scripts/portfolio-snapshot.sh +0 -0
- package/skills/defi-cli/scripts/preflight.sh +0 -0
- package/skills/defi-cli/scripts/swap-quote.sh +37 -0
- package/skills/defi-cli/scripts/wallet-status.sh +30 -0
- package/skills/defi-cli/scripts/yield-scan.sh +0 -0
- package/skills/defi-cli/scripts/exploit-scan.sh +0 -10
package/dist/main.js
CHANGED
|
@@ -1968,12 +1968,16 @@ var init_dist2 = __esm({
|
|
|
1968
1968
|
functionName: "add_liquidity",
|
|
1969
1969
|
args: [[params.amount_a, params.amount_b], 0n]
|
|
1970
1970
|
});
|
|
1971
|
+
const approvals = [];
|
|
1972
|
+
if (params.amount_a > 0n) approvals.push({ token: params.token_a, spender: this.router, amount: params.amount_a });
|
|
1973
|
+
if (params.amount_b > 0n) approvals.push({ token: params.token_b, spender: this.router, amount: params.amount_b });
|
|
1971
1974
|
return {
|
|
1972
1975
|
description: `[${this.protocolName}] Curve add liquidity`,
|
|
1973
1976
|
to: this.router,
|
|
1974
1977
|
data,
|
|
1975
1978
|
value: 0n,
|
|
1976
|
-
gas_estimate: 4e5
|
|
1979
|
+
gas_estimate: 4e5,
|
|
1980
|
+
approvals
|
|
1977
1981
|
};
|
|
1978
1982
|
}
|
|
1979
1983
|
async buildRemoveLiquidity(params) {
|
|
@@ -7337,6 +7341,13 @@ var Executor = class _Executor {
|
|
|
7337
7341
|
static applyGasBuffer(gas) {
|
|
7338
7342
|
return gas * GAS_BUFFER_BPS / 10000n;
|
|
7339
7343
|
}
|
|
7344
|
+
/**
|
|
7345
|
+
* EIP-1559 max-fee formula: `baseFee * 1.25 + priorityFee`.
|
|
7346
|
+
* Extracted as a static so the math is unit-testable without mocking viem.
|
|
7347
|
+
*/
|
|
7348
|
+
static computeMaxFee(baseFeePerGas, priorityFee) {
|
|
7349
|
+
return baseFeePerGas * 125n / 100n + priorityFee;
|
|
7350
|
+
}
|
|
7340
7351
|
/**
|
|
7341
7352
|
* Check allowance for a single token/spender pair and send an approve tx if needed.
|
|
7342
7353
|
* Only called in broadcast mode (not dry-run).
|
|
@@ -7457,8 +7468,7 @@ var Executor = class _Executor {
|
|
|
7457
7468
|
try {
|
|
7458
7469
|
const block = await client.getBlock({ blockTag: "latest" });
|
|
7459
7470
|
if (block.baseFeePerGas !== null && block.baseFeePerGas !== void 0) {
|
|
7460
|
-
|
|
7461
|
-
return [maxFee, priorityFee];
|
|
7471
|
+
return [_Executor.computeMaxFee(block.baseFeePerGas, priorityFee), priorityFee];
|
|
7462
7472
|
}
|
|
7463
7473
|
} catch {
|
|
7464
7474
|
}
|
|
@@ -8280,7 +8290,8 @@ function handleSchema(params) {
|
|
|
8280
8290
|
function registerSchema(parent, getOpts) {
|
|
8281
8291
|
parent.command("schema [command]").description("Output JSON schema for a command (agent-friendly)").option("--all", "Show all schemas").action(async (command, opts) => {
|
|
8282
8292
|
const mode = getOpts();
|
|
8283
|
-
const
|
|
8293
|
+
const raw = opts.all ? "all" : command ?? "all";
|
|
8294
|
+
const action = raw.replace(/-/g, ".");
|
|
8284
8295
|
const params = { action };
|
|
8285
8296
|
const schema = handleSchema(params);
|
|
8286
8297
|
printOutput(schema, mode);
|
|
@@ -8471,6 +8482,11 @@ function buildPipelineSteps(p, input = {}) {
|
|
|
8471
8482
|
["--protocol", slug, "slug"],
|
|
8472
8483
|
["--gauge", input.gauge, "gauge-from-voter.gaugeForPool"]
|
|
8473
8484
|
]);
|
|
8485
|
+
const claimAutoStakeNftGauge = () => `defi ${chainFlag}lp claim ` + buildCmd([
|
|
8486
|
+
["--protocol", slug, "slug"],
|
|
8487
|
+
["--gauge", input.gauge, "gauge-from-voter.gaugeForPool"],
|
|
8488
|
+
["--token-id", input.tokenId, "token-id-from-mint-result"]
|
|
8489
|
+
]);
|
|
8474
8490
|
switch (p.reward_strategy) {
|
|
8475
8491
|
case "lp_fee_only":
|
|
8476
8492
|
return [
|
|
@@ -8495,11 +8511,18 @@ function buildPipelineSteps(p, input = {}) {
|
|
|
8495
8511
|
{ step: "stake", function: "gauge.deposit(amount)", cli_command: baseFarm },
|
|
8496
8512
|
{ step: "claim", function: "gauge.earned(token, account) \u2192 gauge.getReward(account, tokens[])", cli_command: claimWithGauge() }
|
|
8497
8513
|
];
|
|
8498
|
-
case "auto_stake":
|
|
8514
|
+
case "auto_stake": {
|
|
8515
|
+
const isNftAutoStake = p.interface === "uniswap_v3";
|
|
8499
8516
|
return [
|
|
8500
8517
|
{ step: "mint", function: "Router.addLiquidity / NPM.mint", note: "LP automatically receives x(3,3) emissions \u2014 no separate stake step", cli_command: baseAdd },
|
|
8501
|
-
{
|
|
8518
|
+
{
|
|
8519
|
+
step: "claim",
|
|
8520
|
+
function: isNftAutoStake ? "NPM.getPeriodReward(currentEpoch, tokenId, tokens[], receiver)" : "gauge.getReward(account, tokens[])",
|
|
8521
|
+
note: isNftAutoStake ? "Ramses CL: claim via NPM with --token-id; gauge.getReward* reverts NOT_AUTHORIZED_CLAIMER for EOAs" : "Multi-token reward (xRAM + WHYPE on Ramses HL)",
|
|
8522
|
+
cli_command: isNftAutoStake ? claimAutoStakeNftGauge() : claimWithGauge()
|
|
8523
|
+
}
|
|
8502
8524
|
];
|
|
8525
|
+
}
|
|
8503
8526
|
case "on_chain_masterchef":
|
|
8504
8527
|
return [
|
|
8505
8528
|
{ step: "mint", function: "NPM.mint or pool.mint", cli_command: baseAdd },
|
|
@@ -9210,7 +9233,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9210
9233
|
note: "Plan output. Run each cli_command sequentially. After the mint step, broadcast mode prints `details.minted_token_id` \u2014 feed that into the next step's --token-id."
|
|
9211
9234
|
}, getOpts());
|
|
9212
9235
|
});
|
|
9213
|
-
lp.command("remove").description("Auto-unstake (if staked) and remove liquidity from a pool").requiredOption("--protocol <protocol>", "Protocol slug").
|
|
9236
|
+
lp.command("remove").description("Auto-unstake (if staked) and remove liquidity from a pool").requiredOption("--protocol <protocol>", "Protocol slug").option("--token-a <token>", "First token symbol or address (required for V2/Curve/LB)").option("--token-b <token>", "Second token symbol or address (required for V2/Curve/LB)").option("--liquidity <amount>", "Liquidity amount to remove in wei (required for V2/Curve/LB)").option("--pool <address>", "Pool address (needed to resolve gauge)").option("--gauge <address>", "Gauge contract address (for solidly/hybra unstake)").option("--token-id <id>", "NFT tokenId (for CL gauge or farming positions)").option("--recipient <address>", "Recipient address").option("--redeem-type <n>", "Hybra: 0=instant exit (with penalty), 1=lock into 2-year veHYBR (default \u2014 WARNING: long lock)").option("--bins <binIds>", "Merchant Moe LB: comma-separated bin IDs to withdraw").option("--amounts <wei>", "Merchant Moe LB: comma-separated bin amounts (parallel to --bins, default: full balance)").action(async (opts) => {
|
|
9214
9237
|
const executor = makeExecutor2();
|
|
9215
9238
|
const chainName = parent.opts().chain;
|
|
9216
9239
|
if (!chainName) {
|
|
@@ -9226,6 +9249,9 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9226
9249
|
if (iface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
|
|
9227
9250
|
if (!opts.pool) throw new Error(`--pool is required for ${protocol.name} (Liquidity Book \u2014 pass --pool <addr>)`);
|
|
9228
9251
|
if (!opts.bins) throw new Error("--bins <id1,id2,...> is required for Merchant Moe LB remove");
|
|
9252
|
+
if (!opts.tokenA || !opts.tokenB) {
|
|
9253
|
+
throw new Error(`--token-a and --token-b are required for ${protocol.name} (Liquidity Book) remove`);
|
|
9254
|
+
}
|
|
9229
9255
|
const lbAdapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
9230
9256
|
const tokenA2 = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
|
|
9231
9257
|
const tokenB2 = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
|
|
@@ -9268,8 +9294,22 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9268
9294
|
printOutput({ step: "lb_remove", ...result }, getOpts());
|
|
9269
9295
|
return;
|
|
9270
9296
|
}
|
|
9271
|
-
const
|
|
9272
|
-
const
|
|
9297
|
+
const NFT_REMOVE_IFACES = /* @__PURE__ */ new Set(["uniswap_v3", "algebra_v3", "thena_cl", "hybra"]);
|
|
9298
|
+
const isNftRemove = !!opts.tokenId && NFT_REMOVE_IFACES.has(iface);
|
|
9299
|
+
if (!isNftRemove) {
|
|
9300
|
+
const missing = [];
|
|
9301
|
+
if (!opts.tokenA) missing.push("--token-a");
|
|
9302
|
+
if (!opts.tokenB) missing.push("--token-b");
|
|
9303
|
+
if (!opts.liquidity) missing.push("--liquidity");
|
|
9304
|
+
if (missing.length > 0) {
|
|
9305
|
+
printOutput({
|
|
9306
|
+
error: `${missing.join(", ")} required for ${protocol.name} remove (or pass --token-id for V3/CL NFT-based remove).`
|
|
9307
|
+
}, getOpts());
|
|
9308
|
+
return;
|
|
9309
|
+
}
|
|
9310
|
+
}
|
|
9311
|
+
const tokenA = opts.tokenA ? opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address : void 0;
|
|
9312
|
+
const tokenB = opts.tokenB ? opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address : void 0;
|
|
9273
9313
|
const poolAddr = opts.pool ? opts.pool : void 0;
|
|
9274
9314
|
let didUnstake = false;
|
|
9275
9315
|
if (iface === "algebra_v3" && protocol.contracts?.["farming_center"] && opts.tokenId && poolAddr) {
|
|
@@ -9319,7 +9359,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9319
9359
|
if (iface === "hybra" && (!wOpts || wOpts.redeemType === 1)) {
|
|
9320
9360
|
process.stderr.write("WARNING: Hybra default redeemType=1 locks rewards into 2-year veHYBR NFT. Pass --redeem-type 0 for instant exit (with penalty).\n");
|
|
9321
9361
|
}
|
|
9322
|
-
const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, BigInt(opts.liquidity), tokenId, wOpts);
|
|
9362
|
+
const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, opts.liquidity ? BigInt(opts.liquidity) : 0n, tokenId, wOpts);
|
|
9323
9363
|
const withdrawResult = await executor.execute(withdrawTx);
|
|
9324
9364
|
printOutput({ step: "unstake_gauge", ...withdrawResult }, getOpts());
|
|
9325
9365
|
if (withdrawResult.status !== "confirmed" && withdrawResult.status !== "simulated") {
|
|
@@ -9334,11 +9374,31 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9334
9374
|
}
|
|
9335
9375
|
process.stderr.write("Step 2/2: Removing liquidity...\n");
|
|
9336
9376
|
const dexAdapter = createDex(protocol, rpcUrl);
|
|
9377
|
+
let removeLiquidity = opts.liquidity ? BigInt(opts.liquidity) : 0n;
|
|
9378
|
+
if (isNftRemove && removeLiquidity === 0n) {
|
|
9379
|
+
const npm = protocol.contracts?.["position_manager"];
|
|
9380
|
+
if (npm) {
|
|
9381
|
+
const c = createPublicClient23({ transport: http23(rpcUrl) });
|
|
9382
|
+
const pos = await detectV3Liquidity(c, npm, BigInt(opts.tokenId));
|
|
9383
|
+
if (pos) {
|
|
9384
|
+
removeLiquidity = pos.liquidity;
|
|
9385
|
+
process.stderr.write(` Read live liquidity ${removeLiquidity} from NPM.positions(${opts.tokenId}).
|
|
9386
|
+
`);
|
|
9387
|
+
}
|
|
9388
|
+
}
|
|
9389
|
+
}
|
|
9390
|
+
if (isNftRemove && removeLiquidity === 0n) {
|
|
9391
|
+
printOutput({
|
|
9392
|
+
error: `tokenId ${opts.tokenId} has zero liquidity (already removed?). Pass --liquidity explicitly to override, or pick a different tokenId.`
|
|
9393
|
+
}, getOpts());
|
|
9394
|
+
return;
|
|
9395
|
+
}
|
|
9396
|
+
const ZERO = "0x0000000000000000000000000000000000000000";
|
|
9337
9397
|
const removeTx = await dexAdapter.buildRemoveLiquidity({
|
|
9338
9398
|
protocol: protocol.name,
|
|
9339
|
-
token_a: tokenA,
|
|
9340
|
-
token_b: tokenB,
|
|
9341
|
-
liquidity:
|
|
9399
|
+
token_a: tokenA ?? ZERO,
|
|
9400
|
+
token_b: tokenB ?? ZERO,
|
|
9401
|
+
liquidity: removeLiquidity,
|
|
9342
9402
|
recipient,
|
|
9343
9403
|
token_id: opts.tokenId ? BigInt(opts.tokenId) : void 0
|
|
9344
9404
|
});
|
|
@@ -9717,12 +9777,20 @@ function resolveTokenAddress(registry, chainName, tokenOrAddress) {
|
|
|
9717
9777
|
return registry.resolveToken(chainName, tokenOrAddress).address;
|
|
9718
9778
|
}
|
|
9719
9779
|
var FALLBACK_ADDRESS = "0x0000000000000000000000000000000000000001";
|
|
9780
|
+
var warnedFallback = false;
|
|
9720
9781
|
function resolveWallet(override) {
|
|
9721
9782
|
if (override) return override;
|
|
9722
9783
|
try {
|
|
9723
9784
|
const { address } = resolveWalletWithSigner();
|
|
9724
9785
|
return address;
|
|
9725
9786
|
} catch {
|
|
9787
|
+
if (!warnedFallback) {
|
|
9788
|
+
process.stderr.write(
|
|
9789
|
+
`WARNING: no wallet configured (set DEFI_WALLET_ADDRESS or DEFI_PRIVATE_KEY, or use --wallet <name>). Using placeholder ${FALLBACK_ADDRESS} for dry-run preview ONLY \u2014 do NOT pass --broadcast with this address.
|
|
9790
|
+
`
|
|
9791
|
+
);
|
|
9792
|
+
warnedFallback = true;
|
|
9793
|
+
}
|
|
9726
9794
|
return FALLBACK_ADDRESS;
|
|
9727
9795
|
}
|
|
9728
9796
|
}
|
|
@@ -9825,7 +9893,8 @@ function resolveAsset(registry, chain, asset) {
|
|
|
9825
9893
|
}
|
|
9826
9894
|
async function collectLendingRates(registry, chainName, rpc, assetAddr) {
|
|
9827
9895
|
const protos = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending);
|
|
9828
|
-
const
|
|
9896
|
+
const rates = [];
|
|
9897
|
+
const errors = [];
|
|
9829
9898
|
let first = true;
|
|
9830
9899
|
for (const proto of protos) {
|
|
9831
9900
|
if (!first) {
|
|
@@ -9834,19 +9903,22 @@ async function collectLendingRates(registry, chainName, rpc, assetAddr) {
|
|
|
9834
9903
|
first = false;
|
|
9835
9904
|
try {
|
|
9836
9905
|
const lending = createLending(proto, rpc);
|
|
9837
|
-
const
|
|
9838
|
-
|
|
9906
|
+
const r = await lending.getRates(assetAddr);
|
|
9907
|
+
rates.push(r);
|
|
9839
9908
|
} catch (err) {
|
|
9840
9909
|
process.stderr.write(`Warning: ${proto.name} rates unavailable: ${err}
|
|
9841
9910
|
`);
|
|
9911
|
+
errors.push({ protocol: proto.name, type: "lending_supply", reason: errMsg(err) });
|
|
9842
9912
|
}
|
|
9843
9913
|
}
|
|
9844
|
-
return
|
|
9914
|
+
return { rates, errors };
|
|
9845
9915
|
}
|
|
9846
9916
|
async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
|
|
9847
9917
|
const opportunities = [];
|
|
9848
|
-
const
|
|
9849
|
-
|
|
9918
|
+
const errors = [];
|
|
9919
|
+
const lendingResult = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
9920
|
+
errors.push(...lendingResult.errors);
|
|
9921
|
+
for (const r of lendingResult.rates) {
|
|
9850
9922
|
if (r.supply_apy > 0) {
|
|
9851
9923
|
opportunities.push({
|
|
9852
9924
|
protocol: r.protocol,
|
|
@@ -9872,7 +9944,8 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
|
|
|
9872
9944
|
utilization: rates.utilization
|
|
9873
9945
|
});
|
|
9874
9946
|
}
|
|
9875
|
-
} catch {
|
|
9947
|
+
} catch (e) {
|
|
9948
|
+
errors.push({ protocol: proto.name, type: "morpho_vault", reason: errMsg(e) });
|
|
9876
9949
|
}
|
|
9877
9950
|
}
|
|
9878
9951
|
}
|
|
@@ -9888,7 +9961,8 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
|
|
|
9888
9961
|
apy: info.apy ?? 0,
|
|
9889
9962
|
total_assets: info.total_assets.toString()
|
|
9890
9963
|
});
|
|
9891
|
-
} catch {
|
|
9964
|
+
} catch (e) {
|
|
9965
|
+
errors.push({ protocol: proto.name, type: "vault", reason: errMsg(e) });
|
|
9892
9966
|
}
|
|
9893
9967
|
}
|
|
9894
9968
|
}
|
|
@@ -9897,7 +9971,7 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
|
|
|
9897
9971
|
const ba = b["apy"] ?? 0;
|
|
9898
9972
|
return ba - aa;
|
|
9899
9973
|
});
|
|
9900
|
-
return opportunities;
|
|
9974
|
+
return { opportunities, errors };
|
|
9901
9975
|
}
|
|
9902
9976
|
async function runYieldScan(registry, asset, output) {
|
|
9903
9977
|
const t0 = Date.now();
|
|
@@ -10039,10 +10113,13 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10039
10113
|
const chain = registry.getChain(chainName);
|
|
10040
10114
|
const rpc = chain.effectiveRpcUrl();
|
|
10041
10115
|
const assetAddr = resolveAsset(registry, chainName, opts.asset);
|
|
10042
|
-
const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10116
|
+
const { rates: results, errors: ratesErrors } = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10043
10117
|
if (results.length === 0) {
|
|
10044
10118
|
printOutput(
|
|
10045
|
-
|
|
10119
|
+
ratesErrors.length > 0 ? {
|
|
10120
|
+
error: `Could not collect lending rates for '${opts.asset}': ${ratesErrors.length} probe(s) failed (likely RPC throttling). Retry, or set ${chainName.toUpperCase()}_RPC_URL.`,
|
|
10121
|
+
failed_probes: ratesErrors
|
|
10122
|
+
} : { error: `No lending rate data available for asset '${opts.asset}'` },
|
|
10046
10123
|
getOpts()
|
|
10047
10124
|
);
|
|
10048
10125
|
process.exit(1);
|
|
@@ -10284,9 +10361,16 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10284
10361
|
const assetAddr = resolveAsset(registry, chainName, asset);
|
|
10285
10362
|
const strategy = opts.strategy ?? "auto";
|
|
10286
10363
|
if (strategy === "auto") {
|
|
10287
|
-
const opportunities = await collectAllYields(registry, chainName, rpc, asset, assetAddr);
|
|
10364
|
+
const { opportunities, errors } = await collectAllYields(registry, chainName, rpc, asset, assetAddr);
|
|
10288
10365
|
if (opportunities.length === 0) {
|
|
10289
|
-
|
|
10366
|
+
if (errors.length > 0) {
|
|
10367
|
+
printOutput({
|
|
10368
|
+
error: `Could not collect yield data for '${asset}': ${errors.length} probe(s) failed (likely RPC throttling or transport error). Retry, or set a private RPC URL via ${chainName.toUpperCase()}_RPC_URL.`,
|
|
10369
|
+
failed_probes: errors
|
|
10370
|
+
}, getOpts());
|
|
10371
|
+
} else {
|
|
10372
|
+
printOutput({ error: `No yield opportunities found for '${asset}'` }, getOpts());
|
|
10373
|
+
}
|
|
10290
10374
|
process.exit(1);
|
|
10291
10375
|
return;
|
|
10292
10376
|
}
|
|
@@ -10316,9 +10400,12 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10316
10400
|
getOpts()
|
|
10317
10401
|
);
|
|
10318
10402
|
} else if (strategy === "best-supply") {
|
|
10319
|
-
const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10403
|
+
const { rates: results, errors: rErr } = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10320
10404
|
if (results.length === 0) {
|
|
10321
|
-
printOutput(
|
|
10405
|
+
printOutput(
|
|
10406
|
+
rErr.length > 0 ? { error: `Could not collect lending rates for '${asset}': ${rErr.length} probe(s) failed (RPC throttling likely).`, failed_probes: rErr } : { error: `No lending rate data available for asset '${asset}'` },
|
|
10407
|
+
getOpts()
|
|
10408
|
+
);
|
|
10322
10409
|
process.exit(1);
|
|
10323
10410
|
return;
|
|
10324
10411
|
}
|
|
@@ -10341,9 +10428,12 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10341
10428
|
getOpts()
|
|
10342
10429
|
);
|
|
10343
10430
|
} else if (strategy === "leverage-loop") {
|
|
10344
|
-
const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10431
|
+
const { rates: results, errors: lErr } = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10345
10432
|
if (results.length === 0) {
|
|
10346
|
-
printOutput(
|
|
10433
|
+
printOutput(
|
|
10434
|
+
lErr.length > 0 ? { error: `Could not collect lending rates for '${asset}': ${lErr.length} probe(s) failed (RPC throttling likely).`, failed_probes: lErr } : { error: `No lending rate data available for asset '${asset}'` },
|
|
10435
|
+
getOpts()
|
|
10436
|
+
);
|
|
10347
10437
|
process.exit(1);
|
|
10348
10438
|
return;
|
|
10349
10439
|
}
|
|
@@ -10407,14 +10497,14 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10407
10497
|
|
|
10408
10498
|
// src/commands/portfolio.ts
|
|
10409
10499
|
init_dist();
|
|
10410
|
-
import { encodeFunctionData as encodeFunctionData29, parseAbi as parseAbi33 } from "viem";
|
|
10500
|
+
import { createPublicClient as createPublicClient25, encodeFunctionData as encodeFunctionData29, http as http25, parseAbi as parseAbi33 } from "viem";
|
|
10411
10501
|
|
|
10412
10502
|
// src/portfolio-tracker.ts
|
|
10413
10503
|
init_dist();
|
|
10414
10504
|
import { mkdirSync, writeFileSync, readdirSync as readdirSync2, readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
|
|
10415
10505
|
import { homedir } from "os";
|
|
10416
10506
|
import { resolve as resolve3 } from "path";
|
|
10417
|
-
import { encodeFunctionData as encodeFunctionData28, parseAbi as parseAbi31 } from "viem";
|
|
10507
|
+
import { createPublicClient as createPublicClient24, encodeFunctionData as encodeFunctionData28, http as http24, parseAbi as parseAbi31 } from "viem";
|
|
10418
10508
|
var ERC20_ABI4 = parseAbi31([
|
|
10419
10509
|
"function balanceOf(address owner) external view returns (uint256)"
|
|
10420
10510
|
]);
|
|
@@ -10465,7 +10555,16 @@ async function takeSnapshot(chainName, wallet, registry) {
|
|
|
10465
10555
|
const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
|
|
10466
10556
|
const oracleAddr = oracleEntry?.contracts?.["oracle"];
|
|
10467
10557
|
const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
|
|
10558
|
+
const priceTokens = [];
|
|
10468
10559
|
if (oracleAddr) {
|
|
10560
|
+
for (const t of tokenEntries) {
|
|
10561
|
+
calls.push([
|
|
10562
|
+
oracleAddr,
|
|
10563
|
+
encodeFunctionData28({ abi: ORACLE_ABI4, functionName: "getAssetPrice", args: [t.address] })
|
|
10564
|
+
]);
|
|
10565
|
+
callLabels.push(`price:${t.address}`);
|
|
10566
|
+
priceTokens.push(t.address);
|
|
10567
|
+
}
|
|
10469
10568
|
calls.push([
|
|
10470
10569
|
oracleAddr,
|
|
10471
10570
|
encodeFunctionData28({ abi: ORACLE_ABI4, functionName: "getAssetPrice", args: [wrappedNative] })
|
|
@@ -10476,10 +10575,19 @@ async function takeSnapshot(chainName, wallet, registry) {
|
|
|
10476
10575
|
if (calls.length > 0) {
|
|
10477
10576
|
results = await multicallRead(rpc, calls);
|
|
10478
10577
|
}
|
|
10578
|
+
const balanceCount = tokenEntries.length;
|
|
10579
|
+
const lendingCount = lendingProtocols.length;
|
|
10580
|
+
const priceStartIdx = balanceCount + lendingCount;
|
|
10479
10581
|
let nativePriceUsd = 0;
|
|
10582
|
+
const priceByToken = /* @__PURE__ */ new Map();
|
|
10480
10583
|
if (oracleAddr) {
|
|
10481
|
-
|
|
10482
|
-
|
|
10584
|
+
for (let i = 0; i < priceTokens.length; i++) {
|
|
10585
|
+
const priceData = results[priceStartIdx + i] ?? null;
|
|
10586
|
+
const px = Number(decodeU256Word(priceData)) / 1e8;
|
|
10587
|
+
if (px > 0) priceByToken.set(priceTokens[i].toLowerCase(), px);
|
|
10588
|
+
}
|
|
10589
|
+
const nativePriceData = results[priceStartIdx + priceTokens.length] ?? null;
|
|
10590
|
+
nativePriceUsd = Number(decodeU256Word(nativePriceData)) / 1e8;
|
|
10483
10591
|
}
|
|
10484
10592
|
let idx = 0;
|
|
10485
10593
|
const tokens = [];
|
|
@@ -10489,8 +10597,20 @@ async function takeSnapshot(chainName, wallet, registry) {
|
|
|
10489
10597
|
const balance = decodeU256Word(results[idx] ?? null);
|
|
10490
10598
|
const balF64 = Number(balance) / 10 ** entry.decimals;
|
|
10491
10599
|
const symbolUpper = entry.symbol.toUpperCase();
|
|
10492
|
-
const
|
|
10493
|
-
|
|
10600
|
+
const tokenAddrLower = entry.address.toLowerCase();
|
|
10601
|
+
let priceUsd;
|
|
10602
|
+
let valueUsd;
|
|
10603
|
+
if (symbolUpper.includes("USD")) {
|
|
10604
|
+
priceUsd = 1;
|
|
10605
|
+
valueUsd = balF64;
|
|
10606
|
+
} else if (tokenAddrLower === wrappedNative.toLowerCase()) {
|
|
10607
|
+
priceUsd = nativePriceUsd;
|
|
10608
|
+
valueUsd = balF64 * nativePriceUsd;
|
|
10609
|
+
} else {
|
|
10610
|
+
const px = priceByToken.get(tokenAddrLower);
|
|
10611
|
+
priceUsd = px ?? 0;
|
|
10612
|
+
valueUsd = px && px > 0 ? balF64 * px : 0;
|
|
10613
|
+
}
|
|
10494
10614
|
totalValueUsd += valueUsd;
|
|
10495
10615
|
tokens.push({
|
|
10496
10616
|
token: entry.address,
|
|
@@ -10531,6 +10651,23 @@ async function takeSnapshot(chainName, wallet, registry) {
|
|
|
10531
10651
|
}
|
|
10532
10652
|
idx++;
|
|
10533
10653
|
}
|
|
10654
|
+
try {
|
|
10655
|
+
const client = createPublicClient24({ transport: http24(rpc) });
|
|
10656
|
+
const nativeBalance = await client.getBalance({ address: user });
|
|
10657
|
+
if (nativeBalance > 0n) {
|
|
10658
|
+
const nativeF64 = Number(nativeBalance) / 1e18;
|
|
10659
|
+
const nativeValueUsd = nativeF64 * nativePriceUsd;
|
|
10660
|
+
totalValueUsd += nativeValueUsd;
|
|
10661
|
+
tokens.push({
|
|
10662
|
+
token: wrappedNative,
|
|
10663
|
+
symbol: chain.native_token ?? "NATIVE",
|
|
10664
|
+
balance: nativeBalance,
|
|
10665
|
+
value_usd: nativeValueUsd,
|
|
10666
|
+
price_usd: nativePriceUsd
|
|
10667
|
+
});
|
|
10668
|
+
}
|
|
10669
|
+
} catch {
|
|
10670
|
+
}
|
|
10534
10671
|
return {
|
|
10535
10672
|
timestamp: Date.now(),
|
|
10536
10673
|
chain: chainName,
|
|
@@ -10648,6 +10785,10 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10648
10785
|
const calls = [];
|
|
10649
10786
|
const callLabels = [];
|
|
10650
10787
|
const tokenSymbols = (registry.tokens.get(chainName) ?? []).map((t) => t.symbol);
|
|
10788
|
+
const tokenAddrsByCallIdx = [];
|
|
10789
|
+
const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
|
|
10790
|
+
const oracleAddr = oracleEntry?.contracts?.["oracle"];
|
|
10791
|
+
const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
|
|
10651
10792
|
for (const symbol of tokenSymbols) {
|
|
10652
10793
|
let entry;
|
|
10653
10794
|
try {
|
|
@@ -10661,6 +10802,7 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10661
10802
|
encodeFunctionData29({ abi: ERC20_ABI5, functionName: "balanceOf", args: [user] })
|
|
10662
10803
|
]);
|
|
10663
10804
|
callLabels.push(`balance:${symbol}`);
|
|
10805
|
+
tokenAddrsByCallIdx.push(entry.address);
|
|
10664
10806
|
}
|
|
10665
10807
|
const lendingProtocols = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending && p.interface === "aave_v3").filter((p) => p.contracts?.["pool"]);
|
|
10666
10808
|
for (const p of lendingProtocols) {
|
|
@@ -10670,10 +10812,16 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10670
10812
|
]);
|
|
10671
10813
|
callLabels.push(`lending:${p.name}`);
|
|
10672
10814
|
}
|
|
10673
|
-
const
|
|
10674
|
-
const oracleAddr = oracleEntry?.contracts?.["oracle"];
|
|
10675
|
-
const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
|
|
10815
|
+
const priceTokens = [];
|
|
10676
10816
|
if (oracleAddr) {
|
|
10817
|
+
for (const tokenAddr of tokenAddrsByCallIdx) {
|
|
10818
|
+
calls.push([
|
|
10819
|
+
oracleAddr,
|
|
10820
|
+
encodeFunctionData29({ abi: ORACLE_ABI5, functionName: "getAssetPrice", args: [tokenAddr] })
|
|
10821
|
+
]);
|
|
10822
|
+
callLabels.push(`price:${tokenAddr}`);
|
|
10823
|
+
priceTokens.push(tokenAddr);
|
|
10824
|
+
}
|
|
10677
10825
|
calls.push([
|
|
10678
10826
|
oracleAddr,
|
|
10679
10827
|
encodeFunctionData29({ abi: ORACLE_ABI5, functionName: "getAssetPrice", args: [wrappedNative] })
|
|
@@ -10698,10 +10846,19 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10698
10846
|
printOutput({ error: `Multicall failed: ${errMsg(e)}` }, mode);
|
|
10699
10847
|
return;
|
|
10700
10848
|
}
|
|
10849
|
+
const balanceCallCount = tokenAddrsByCallIdx.length;
|
|
10850
|
+
const lendingCallCount = lendingProtocols.length;
|
|
10851
|
+
const priceStartIdx = balanceCallCount + lendingCallCount;
|
|
10701
10852
|
let nativePriceUsd = 0;
|
|
10853
|
+
const priceByToken = /* @__PURE__ */ new Map();
|
|
10702
10854
|
if (oracleAddr) {
|
|
10703
|
-
|
|
10704
|
-
|
|
10855
|
+
for (let i = 0; i < priceTokens.length; i++) {
|
|
10856
|
+
const priceData = results[priceStartIdx + i] ?? null;
|
|
10857
|
+
const px = Number(decodeU2562(priceData)) / 1e8;
|
|
10858
|
+
if (px > 0) priceByToken.set(priceTokens[i].toLowerCase(), px);
|
|
10859
|
+
}
|
|
10860
|
+
const nativePriceData = results[priceStartIdx + priceTokens.length] ?? null;
|
|
10861
|
+
nativePriceUsd = Number(decodeU2562(nativePriceData)) / 1e8;
|
|
10705
10862
|
}
|
|
10706
10863
|
let totalValueUsd = 0;
|
|
10707
10864
|
let idx = 0;
|
|
@@ -10720,12 +10877,21 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10720
10877
|
const decimals = entry.decimals;
|
|
10721
10878
|
const balF64 = Number(balance) / 10 ** decimals;
|
|
10722
10879
|
const symbolUpper = symbol.toUpperCase();
|
|
10723
|
-
const
|
|
10724
|
-
|
|
10880
|
+
const tokenAddrLower = entry.address.toLowerCase();
|
|
10881
|
+
let valueUsd;
|
|
10882
|
+
if (symbolUpper.includes("USD")) {
|
|
10883
|
+
valueUsd = balF64;
|
|
10884
|
+
} else if (tokenAddrLower === wrappedNative.toLowerCase()) {
|
|
10885
|
+
valueUsd = balF64 * nativePriceUsd;
|
|
10886
|
+
} else {
|
|
10887
|
+
const px = priceByToken.get(tokenAddrLower);
|
|
10888
|
+
valueUsd = px && px > 0 ? balF64 * px : null;
|
|
10889
|
+
}
|
|
10890
|
+
if (valueUsd !== null) totalValueUsd += valueUsd;
|
|
10725
10891
|
tokenBalances.push({
|
|
10726
10892
|
symbol,
|
|
10727
10893
|
balance: balF64.toFixed(4),
|
|
10728
|
-
value_usd: valueUsd.toFixed(2)
|
|
10894
|
+
value_usd: valueUsd !== null ? valueUsd.toFixed(2) : null
|
|
10729
10895
|
});
|
|
10730
10896
|
}
|
|
10731
10897
|
idx++;
|
|
@@ -10755,11 +10921,23 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10755
10921
|
}
|
|
10756
10922
|
idx++;
|
|
10757
10923
|
}
|
|
10924
|
+
let nativeBalance = 0n;
|
|
10925
|
+
let nativeValueUsd = 0;
|
|
10926
|
+
try {
|
|
10927
|
+
const client = createPublicClient25({ transport: http25(rpc) });
|
|
10928
|
+
nativeBalance = await client.getBalance({ address: user });
|
|
10929
|
+
const nativeF64 = Number(nativeBalance) / 1e18;
|
|
10930
|
+
nativeValueUsd = nativeF64 * nativePriceUsd;
|
|
10931
|
+
if (nativeBalance > 0n) totalValueUsd += nativeValueUsd;
|
|
10932
|
+
} catch {
|
|
10933
|
+
}
|
|
10758
10934
|
printOutput(
|
|
10759
10935
|
{
|
|
10760
10936
|
address: user,
|
|
10761
10937
|
chain: chain.name,
|
|
10762
10938
|
native_price_usd: nativePriceUsd.toFixed(2),
|
|
10939
|
+
native_balance: (Number(nativeBalance) / 1e18).toFixed(6),
|
|
10940
|
+
native_value_usd: nativeValueUsd.toFixed(2),
|
|
10763
10941
|
total_value_usd: totalValueUsd.toFixed(2),
|
|
10764
10942
|
token_balances: tokenBalances,
|
|
10765
10943
|
lending_positions: lendingPositions
|
|
@@ -11025,38 +11203,50 @@ function registerPrice(parent, getOpts) {
|
|
|
11025
11203
|
|
|
11026
11204
|
// src/commands/wallet.ts
|
|
11027
11205
|
init_dist();
|
|
11028
|
-
import { createPublicClient as
|
|
11206
|
+
import { createPublicClient as createPublicClient26, http as http26, formatEther } from "viem";
|
|
11207
|
+
function resolveCurrentAddress(override) {
|
|
11208
|
+
if (override) return { address: override, source: "flag" };
|
|
11209
|
+
try {
|
|
11210
|
+
const { address, signer } = resolveWalletWithSigner();
|
|
11211
|
+
return { address, source: signer ? "ows" : process.env["DEFI_PRIVATE_KEY"] ? "private_key" : "env" };
|
|
11212
|
+
} catch {
|
|
11213
|
+
return { address: null, source: "none" };
|
|
11214
|
+
}
|
|
11215
|
+
}
|
|
11029
11216
|
function registerWallet(parent, getOpts) {
|
|
11030
11217
|
const wallet = parent.command("wallet").description("Wallet management");
|
|
11031
|
-
wallet.command("balance").description("Show native token balance").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
|
|
11218
|
+
wallet.command("balance").description("Show native token balance").option("--address <address>", "Wallet address (defaults to OWS vault, DEFI_PRIVATE_KEY, or DEFI_WALLET_ADDRESS)").action(async (opts) => {
|
|
11032
11219
|
const chainName = requireChain(parent, getOpts);
|
|
11033
11220
|
if (!chainName) return;
|
|
11034
|
-
const addr = opts.address
|
|
11221
|
+
const { address: addr, source } = resolveCurrentAddress(opts.address);
|
|
11035
11222
|
if (!addr) {
|
|
11036
|
-
printOutput({
|
|
11223
|
+
printOutput({
|
|
11224
|
+
error: "No wallet configured. Set DEFI_WALLET_ADDRESS, set DEFI_PRIVATE_KEY, or pass --address."
|
|
11225
|
+
}, getOpts());
|
|
11037
11226
|
return;
|
|
11038
11227
|
}
|
|
11039
11228
|
const registry = Registry.loadEmbedded();
|
|
11040
11229
|
const chain = registry.getChain(chainName);
|
|
11041
|
-
const client =
|
|
11230
|
+
const client = createPublicClient26({ transport: http26(chain.effectiveRpcUrl()) });
|
|
11042
11231
|
const balance = await client.getBalance({ address: addr });
|
|
11043
11232
|
printOutput({
|
|
11044
11233
|
chain: chain.name,
|
|
11045
11234
|
address: addr,
|
|
11235
|
+
wallet_source: source,
|
|
11046
11236
|
native_token: chain.native_token,
|
|
11047
11237
|
balance_wei: balance,
|
|
11048
11238
|
balance_formatted: formatEther(balance)
|
|
11049
11239
|
}, getOpts());
|
|
11050
11240
|
});
|
|
11051
11241
|
wallet.command("address").description("Show configured wallet address").action(async () => {
|
|
11052
|
-
const
|
|
11053
|
-
printOutput({ address
|
|
11242
|
+
const { address, source } = resolveCurrentAddress();
|
|
11243
|
+
printOutput({ address, source }, getOpts());
|
|
11054
11244
|
});
|
|
11055
11245
|
}
|
|
11056
11246
|
|
|
11057
11247
|
// src/commands/token.ts
|
|
11058
11248
|
init_dist();
|
|
11059
|
-
import { createPublicClient as
|
|
11249
|
+
import { createPublicClient as createPublicClient27, http as http27, maxUint256 } from "viem";
|
|
11060
11250
|
function registerToken(parent, getOpts, makeExecutor2) {
|
|
11061
11251
|
const token = parent.command("token").description("Token operations: approve, allowance, transfer, balance");
|
|
11062
11252
|
token.command("balance").description("Query token balance for an address").requiredOption("--token <token>", "Token symbol or address").option("--owner <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
|
|
@@ -11069,7 +11259,7 @@ function registerToken(parent, getOpts, makeExecutor2) {
|
|
|
11069
11259
|
}
|
|
11070
11260
|
const registry = Registry.loadEmbedded();
|
|
11071
11261
|
const chain = registry.getChain(chainName);
|
|
11072
|
-
const client =
|
|
11262
|
+
const client = createPublicClient27({ transport: http27(chain.effectiveRpcUrl()) });
|
|
11073
11263
|
const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
|
|
11074
11264
|
const [balance, symbol, decimals] = await Promise.all([
|
|
11075
11265
|
client.readContract({ address: tokenAddr, abi: erc20Abi, functionName: "balanceOf", args: [owner] }),
|
|
@@ -11105,7 +11295,7 @@ function registerToken(parent, getOpts, makeExecutor2) {
|
|
|
11105
11295
|
}
|
|
11106
11296
|
const registry = Registry.loadEmbedded();
|
|
11107
11297
|
const chain = registry.getChain(chainName);
|
|
11108
|
-
const client =
|
|
11298
|
+
const client = createPublicClient27({ transport: http27(chain.effectiveRpcUrl()) });
|
|
11109
11299
|
const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
|
|
11110
11300
|
const allowance = await client.readContract({
|
|
11111
11301
|
address: tokenAddr,
|
|
@@ -11223,6 +11413,17 @@ var CCTP_USDC_ADDRESSES = {
|
|
|
11223
11413
|
base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
11224
11414
|
polygon: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"
|
|
11225
11415
|
};
|
|
11416
|
+
function cctpMinFeeGuard(amountWei, maxFeeSubunits, feeUsdc) {
|
|
11417
|
+
if (amountWei <= maxFeeSubunits) {
|
|
11418
|
+
const amountUsdc = Number(amountWei) / 1e6;
|
|
11419
|
+
return {
|
|
11420
|
+
error: `CCTP: amount ${amountWei.toString()} (${amountUsdc} USDC) is below the minimum bridge fee of ${maxFeeSubunits} (${feeUsdc} USDC). Increase --amount.`,
|
|
11421
|
+
minimum_amount_wei: maxFeeSubunits.toString(),
|
|
11422
|
+
minimum_amount_usdc: feeUsdc
|
|
11423
|
+
};
|
|
11424
|
+
}
|
|
11425
|
+
return null;
|
|
11426
|
+
}
|
|
11226
11427
|
async function getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc) {
|
|
11227
11428
|
try {
|
|
11228
11429
|
const res = await fetch(`${CCTP_FEE_API}/${srcDomain}/${dstDomain}`);
|
|
@@ -11311,12 +11512,9 @@ function registerBridge(parent, getOpts) {
|
|
|
11311
11512
|
}
|
|
11312
11513
|
const amountUsdc = Number(BigInt(opts.amount)) / 1e6;
|
|
11313
11514
|
const { fee, maxFeeSubunits } = await getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc);
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
minimum_amount_wei: maxFeeSubunits.toString(),
|
|
11318
|
-
minimum_amount_usdc: fee
|
|
11319
|
-
}, getOpts());
|
|
11515
|
+
const guardErr = cctpMinFeeGuard(BigInt(opts.amount), maxFeeSubunits, fee);
|
|
11516
|
+
if (guardErr) {
|
|
11517
|
+
printOutput(guardErr, getOpts());
|
|
11320
11518
|
return;
|
|
11321
11519
|
}
|
|
11322
11520
|
const recipientPadded = `0x${"0".repeat(24)}${recipient.replace("0x", "").toLowerCase()}`;
|
|
@@ -11597,12 +11795,14 @@ function registerSwap(parent, getOpts, makeExecutor2) {
|
|
|
11597
11795
|
slippagePct,
|
|
11598
11796
|
wallet
|
|
11599
11797
|
);
|
|
11798
|
+
const fromLower = fromAddr.toLowerCase();
|
|
11799
|
+
const isNativeInput = fromLower === "0x0000000000000000000000000000000000000000" || fromLower === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
|
11600
11800
|
const tx = {
|
|
11601
11801
|
description: `OpenOcean: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
|
|
11602
11802
|
to: swap.to,
|
|
11603
11803
|
data: swap.data,
|
|
11604
11804
|
value: parseBigIntValue(swap.value),
|
|
11605
|
-
approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }]
|
|
11805
|
+
...isNativeInput ? {} : { approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }] }
|
|
11606
11806
|
};
|
|
11607
11807
|
const result = await executor.execute(tx);
|
|
11608
11808
|
printOutput({
|
|
@@ -11694,7 +11894,8 @@ import pc2 from "picocolors";
|
|
|
11694
11894
|
import { createInterface } from "readline";
|
|
11695
11895
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
11696
11896
|
import { resolve as resolve4 } from "path";
|
|
11697
|
-
|
|
11897
|
+
import { homedir as homedir2 } from "os";
|
|
11898
|
+
var DEFI_DIR = resolve4(homedir2(), ".defi");
|
|
11698
11899
|
var ENV_FILE = resolve4(DEFI_DIR, ".env");
|
|
11699
11900
|
function ensureDefiDir() {
|
|
11700
11901
|
if (!existsSync3(DEFI_DIR)) mkdirSync2(DEFI_DIR, { recursive: true, mode: 448 });
|
|
@@ -11729,6 +11930,44 @@ function writeEnvFile(env) {
|
|
|
11729
11930
|
function ask(rl, question) {
|
|
11730
11931
|
return new Promise((res) => rl.question(question, (answer) => res(answer.trim())));
|
|
11731
11932
|
}
|
|
11933
|
+
function askSecret(rl, question) {
|
|
11934
|
+
const stdin = process.stdin;
|
|
11935
|
+
if (!stdin.isTTY) return ask(rl, question);
|
|
11936
|
+
process.stdout.write(question);
|
|
11937
|
+
const rlAny = rl;
|
|
11938
|
+
const original = rlAny._writeToOutput;
|
|
11939
|
+
rlAny._writeToOutput = (s) => {
|
|
11940
|
+
if (s.includes("\n") || s.includes("\r")) {
|
|
11941
|
+
original?.call(rl, s);
|
|
11942
|
+
}
|
|
11943
|
+
};
|
|
11944
|
+
return new Promise((res) => {
|
|
11945
|
+
rl.question("", (answer) => {
|
|
11946
|
+
rlAny._writeToOutput = original;
|
|
11947
|
+
process.stdout.write("\n");
|
|
11948
|
+
res(answer.trim());
|
|
11949
|
+
});
|
|
11950
|
+
});
|
|
11951
|
+
}
|
|
11952
|
+
function maskRpcUrl(s) {
|
|
11953
|
+
try {
|
|
11954
|
+
const u = new URL(s);
|
|
11955
|
+
if (u.pathname && u.pathname !== "/" && u.pathname.length > 1) {
|
|
11956
|
+
return `${u.protocol}//${u.host}/***`;
|
|
11957
|
+
}
|
|
11958
|
+
return `${u.protocol}//${u.host}`;
|
|
11959
|
+
} catch {
|
|
11960
|
+
return "***";
|
|
11961
|
+
}
|
|
11962
|
+
}
|
|
11963
|
+
function isValidRpcUrl(s) {
|
|
11964
|
+
try {
|
|
11965
|
+
const u = new URL(s);
|
|
11966
|
+
return u.protocol === "http:" || u.protocol === "https:";
|
|
11967
|
+
} catch {
|
|
11968
|
+
return false;
|
|
11969
|
+
}
|
|
11970
|
+
}
|
|
11732
11971
|
function isValidAddress(s) {
|
|
11733
11972
|
return /^0x[0-9a-fA-F]{40}$/.test(s);
|
|
11734
11973
|
}
|
|
@@ -11753,7 +11992,14 @@ function registerSetup(program2) {
|
|
|
11753
11992
|
if (Object.keys(existing).length > 0) {
|
|
11754
11993
|
console.log(pc2.white(" Current configuration:"));
|
|
11755
11994
|
for (const [key, value] of Object.entries(existing)) {
|
|
11756
|
-
|
|
11995
|
+
let masked;
|
|
11996
|
+
if (key.toLowerCase().includes("key")) {
|
|
11997
|
+
masked = value.slice(0, 6) + "..." + value.slice(-4);
|
|
11998
|
+
} else if (key.endsWith("RPC_URL")) {
|
|
11999
|
+
masked = maskRpcUrl(value);
|
|
12000
|
+
} else {
|
|
12001
|
+
masked = value;
|
|
12002
|
+
}
|
|
11757
12003
|
console.log(` ${pc2.cyan(key.padEnd(24))} ${pc2.gray(masked)}`);
|
|
11758
12004
|
}
|
|
11759
12005
|
console.log();
|
|
@@ -11767,7 +12013,7 @@ function registerSetup(program2) {
|
|
|
11767
12013
|
}
|
|
11768
12014
|
const newEnv = {};
|
|
11769
12015
|
console.log(pc2.cyan(pc2.bold(" Wallet")));
|
|
11770
|
-
const privateKey = await
|
|
12016
|
+
const privateKey = await askSecret(rl, " Private key (optional, for --broadcast, 0x... \u2014 input hidden): ");
|
|
11771
12017
|
if (privateKey) {
|
|
11772
12018
|
const normalized = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
|
|
11773
12019
|
if (!isValidPrivateKey(normalized)) {
|
|
@@ -11803,6 +12049,10 @@ function registerSetup(program2) {
|
|
|
11803
12049
|
for (const { env, label } of rpcPrompts) {
|
|
11804
12050
|
const value = await ask(rl, ` ${label} RPC URL: `);
|
|
11805
12051
|
if (value) {
|
|
12052
|
+
if (!isValidRpcUrl(value)) {
|
|
12053
|
+
console.log(pc2.yellow(` Invalid URL (${value}). Skipped \u2014 re-run setup to retry.`));
|
|
12054
|
+
continue;
|
|
12055
|
+
}
|
|
11806
12056
|
newEnv[env] = value;
|
|
11807
12057
|
console.log(` ${pc2.green("OK")} ${label} RPC set`);
|
|
11808
12058
|
}
|
|
@@ -11826,7 +12076,7 @@ function registerSetup(program2) {
|
|
|
11826
12076
|
];
|
|
11827
12077
|
for (const [k, label] of rpcSummary) {
|
|
11828
12078
|
const v = finalEnv[k];
|
|
11829
|
-
if (v) console.log(` ${label}: ${pc2.gray(v)}`);
|
|
12079
|
+
if (v) console.log(` ${label}: ${pc2.gray(maskRpcUrl(v))}`);
|
|
11830
12080
|
}
|
|
11831
12081
|
console.log(pc2.bold(pc2.white("\n Next steps:")));
|
|
11832
12082
|
console.log(` ${pc2.green("defi portfolio")} view balances & positions`);
|
|
@@ -11845,11 +12095,11 @@ function registerSetup(program2) {
|
|
|
11845
12095
|
// src/commands/ows.ts
|
|
11846
12096
|
import pc3 from "picocolors";
|
|
11847
12097
|
init_dist();
|
|
11848
|
-
import { createPublicClient as
|
|
12098
|
+
import { createPublicClient as createPublicClient28, http as http28, formatEther as formatEther2 } from "viem";
|
|
11849
12099
|
async function getEvmBalance(address, chainName) {
|
|
11850
12100
|
const registry = Registry.loadEmbedded();
|
|
11851
12101
|
const chain = registry.getChain(chainName);
|
|
11852
|
-
const client =
|
|
12102
|
+
const client = createPublicClient28({ transport: http28(chain.effectiveRpcUrl()) });
|
|
11853
12103
|
const balance = await client.getBalance({ address });
|
|
11854
12104
|
return {
|
|
11855
12105
|
native_token: chain.native_token,
|