@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/index.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) {
|
|
@@ -7333,6 +7337,13 @@ var Executor = class _Executor {
|
|
|
7333
7337
|
static applyGasBuffer(gas) {
|
|
7334
7338
|
return gas * GAS_BUFFER_BPS / 10000n;
|
|
7335
7339
|
}
|
|
7340
|
+
/**
|
|
7341
|
+
* EIP-1559 max-fee formula: `baseFee * 1.25 + priorityFee`.
|
|
7342
|
+
* Extracted as a static so the math is unit-testable without mocking viem.
|
|
7343
|
+
*/
|
|
7344
|
+
static computeMaxFee(baseFeePerGas, priorityFee) {
|
|
7345
|
+
return baseFeePerGas * 125n / 100n + priorityFee;
|
|
7346
|
+
}
|
|
7336
7347
|
/**
|
|
7337
7348
|
* Check allowance for a single token/spender pair and send an approve tx if needed.
|
|
7338
7349
|
* Only called in broadcast mode (not dry-run).
|
|
@@ -7453,8 +7464,7 @@ var Executor = class _Executor {
|
|
|
7453
7464
|
try {
|
|
7454
7465
|
const block = await client.getBlock({ blockTag: "latest" });
|
|
7455
7466
|
if (block.baseFeePerGas !== null && block.baseFeePerGas !== void 0) {
|
|
7456
|
-
|
|
7457
|
-
return [maxFee, priorityFee];
|
|
7467
|
+
return [_Executor.computeMaxFee(block.baseFeePerGas, priorityFee), priorityFee];
|
|
7458
7468
|
}
|
|
7459
7469
|
} catch {
|
|
7460
7470
|
}
|
|
@@ -8276,7 +8286,8 @@ function handleSchema(params) {
|
|
|
8276
8286
|
function registerSchema(parent, getOpts) {
|
|
8277
8287
|
parent.command("schema [command]").description("Output JSON schema for a command (agent-friendly)").option("--all", "Show all schemas").action(async (command, opts) => {
|
|
8278
8288
|
const mode = getOpts();
|
|
8279
|
-
const
|
|
8289
|
+
const raw = opts.all ? "all" : command ?? "all";
|
|
8290
|
+
const action = raw.replace(/-/g, ".");
|
|
8280
8291
|
const params = { action };
|
|
8281
8292
|
const schema = handleSchema(params);
|
|
8282
8293
|
printOutput(schema, mode);
|
|
@@ -8467,6 +8478,11 @@ function buildPipelineSteps(p, input = {}) {
|
|
|
8467
8478
|
["--protocol", slug, "slug"],
|
|
8468
8479
|
["--gauge", input.gauge, "gauge-from-voter.gaugeForPool"]
|
|
8469
8480
|
]);
|
|
8481
|
+
const claimAutoStakeNftGauge = () => `defi ${chainFlag}lp claim ` + buildCmd([
|
|
8482
|
+
["--protocol", slug, "slug"],
|
|
8483
|
+
["--gauge", input.gauge, "gauge-from-voter.gaugeForPool"],
|
|
8484
|
+
["--token-id", input.tokenId, "token-id-from-mint-result"]
|
|
8485
|
+
]);
|
|
8470
8486
|
switch (p.reward_strategy) {
|
|
8471
8487
|
case "lp_fee_only":
|
|
8472
8488
|
return [
|
|
@@ -8491,11 +8507,18 @@ function buildPipelineSteps(p, input = {}) {
|
|
|
8491
8507
|
{ step: "stake", function: "gauge.deposit(amount)", cli_command: baseFarm },
|
|
8492
8508
|
{ step: "claim", function: "gauge.earned(token, account) \u2192 gauge.getReward(account, tokens[])", cli_command: claimWithGauge() }
|
|
8493
8509
|
];
|
|
8494
|
-
case "auto_stake":
|
|
8510
|
+
case "auto_stake": {
|
|
8511
|
+
const isNftAutoStake = p.interface === "uniswap_v3";
|
|
8495
8512
|
return [
|
|
8496
8513
|
{ step: "mint", function: "Router.addLiquidity / NPM.mint", note: "LP automatically receives x(3,3) emissions \u2014 no separate stake step", cli_command: baseAdd },
|
|
8497
|
-
{
|
|
8514
|
+
{
|
|
8515
|
+
step: "claim",
|
|
8516
|
+
function: isNftAutoStake ? "NPM.getPeriodReward(currentEpoch, tokenId, tokens[], receiver)" : "gauge.getReward(account, tokens[])",
|
|
8517
|
+
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)",
|
|
8518
|
+
cli_command: isNftAutoStake ? claimAutoStakeNftGauge() : claimWithGauge()
|
|
8519
|
+
}
|
|
8498
8520
|
];
|
|
8521
|
+
}
|
|
8499
8522
|
case "on_chain_masterchef":
|
|
8500
8523
|
return [
|
|
8501
8524
|
{ step: "mint", function: "NPM.mint or pool.mint", cli_command: baseAdd },
|
|
@@ -9206,7 +9229,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9206
9229
|
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."
|
|
9207
9230
|
}, getOpts());
|
|
9208
9231
|
});
|
|
9209
|
-
lp.command("remove").description("Auto-unstake (if staked) and remove liquidity from a pool").requiredOption("--protocol <protocol>", "Protocol slug").
|
|
9232
|
+
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) => {
|
|
9210
9233
|
const executor = makeExecutor2();
|
|
9211
9234
|
const chainName = parent.opts().chain;
|
|
9212
9235
|
if (!chainName) {
|
|
@@ -9222,6 +9245,9 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9222
9245
|
if (iface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
|
|
9223
9246
|
if (!opts.pool) throw new Error(`--pool is required for ${protocol.name} (Liquidity Book \u2014 pass --pool <addr>)`);
|
|
9224
9247
|
if (!opts.bins) throw new Error("--bins <id1,id2,...> is required for Merchant Moe LB remove");
|
|
9248
|
+
if (!opts.tokenA || !opts.tokenB) {
|
|
9249
|
+
throw new Error(`--token-a and --token-b are required for ${protocol.name} (Liquidity Book) remove`);
|
|
9250
|
+
}
|
|
9225
9251
|
const lbAdapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
9226
9252
|
const tokenA2 = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
|
|
9227
9253
|
const tokenB2 = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
|
|
@@ -9264,8 +9290,22 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9264
9290
|
printOutput({ step: "lb_remove", ...result }, getOpts());
|
|
9265
9291
|
return;
|
|
9266
9292
|
}
|
|
9267
|
-
const
|
|
9268
|
-
const
|
|
9293
|
+
const NFT_REMOVE_IFACES = /* @__PURE__ */ new Set(["uniswap_v3", "algebra_v3", "thena_cl", "hybra"]);
|
|
9294
|
+
const isNftRemove = !!opts.tokenId && NFT_REMOVE_IFACES.has(iface);
|
|
9295
|
+
if (!isNftRemove) {
|
|
9296
|
+
const missing = [];
|
|
9297
|
+
if (!opts.tokenA) missing.push("--token-a");
|
|
9298
|
+
if (!opts.tokenB) missing.push("--token-b");
|
|
9299
|
+
if (!opts.liquidity) missing.push("--liquidity");
|
|
9300
|
+
if (missing.length > 0) {
|
|
9301
|
+
printOutput({
|
|
9302
|
+
error: `${missing.join(", ")} required for ${protocol.name} remove (or pass --token-id for V3/CL NFT-based remove).`
|
|
9303
|
+
}, getOpts());
|
|
9304
|
+
return;
|
|
9305
|
+
}
|
|
9306
|
+
}
|
|
9307
|
+
const tokenA = opts.tokenA ? opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address : void 0;
|
|
9308
|
+
const tokenB = opts.tokenB ? opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address : void 0;
|
|
9269
9309
|
const poolAddr = opts.pool ? opts.pool : void 0;
|
|
9270
9310
|
let didUnstake = false;
|
|
9271
9311
|
if (iface === "algebra_v3" && protocol.contracts?.["farming_center"] && opts.tokenId && poolAddr) {
|
|
@@ -9315,7 +9355,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9315
9355
|
if (iface === "hybra" && (!wOpts || wOpts.redeemType === 1)) {
|
|
9316
9356
|
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");
|
|
9317
9357
|
}
|
|
9318
|
-
const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, BigInt(opts.liquidity), tokenId, wOpts);
|
|
9358
|
+
const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, opts.liquidity ? BigInt(opts.liquidity) : 0n, tokenId, wOpts);
|
|
9319
9359
|
const withdrawResult = await executor.execute(withdrawTx);
|
|
9320
9360
|
printOutput({ step: "unstake_gauge", ...withdrawResult }, getOpts());
|
|
9321
9361
|
if (withdrawResult.status !== "confirmed" && withdrawResult.status !== "simulated") {
|
|
@@ -9330,11 +9370,31 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9330
9370
|
}
|
|
9331
9371
|
process.stderr.write("Step 2/2: Removing liquidity...\n");
|
|
9332
9372
|
const dexAdapter = createDex(protocol, rpcUrl);
|
|
9373
|
+
let removeLiquidity = opts.liquidity ? BigInt(opts.liquidity) : 0n;
|
|
9374
|
+
if (isNftRemove && removeLiquidity === 0n) {
|
|
9375
|
+
const npm = protocol.contracts?.["position_manager"];
|
|
9376
|
+
if (npm) {
|
|
9377
|
+
const c = createPublicClient23({ transport: http23(rpcUrl) });
|
|
9378
|
+
const pos = await detectV3Liquidity(c, npm, BigInt(opts.tokenId));
|
|
9379
|
+
if (pos) {
|
|
9380
|
+
removeLiquidity = pos.liquidity;
|
|
9381
|
+
process.stderr.write(` Read live liquidity ${removeLiquidity} from NPM.positions(${opts.tokenId}).
|
|
9382
|
+
`);
|
|
9383
|
+
}
|
|
9384
|
+
}
|
|
9385
|
+
}
|
|
9386
|
+
if (isNftRemove && removeLiquidity === 0n) {
|
|
9387
|
+
printOutput({
|
|
9388
|
+
error: `tokenId ${opts.tokenId} has zero liquidity (already removed?). Pass --liquidity explicitly to override, or pick a different tokenId.`
|
|
9389
|
+
}, getOpts());
|
|
9390
|
+
return;
|
|
9391
|
+
}
|
|
9392
|
+
const ZERO = "0x0000000000000000000000000000000000000000";
|
|
9333
9393
|
const removeTx = await dexAdapter.buildRemoveLiquidity({
|
|
9334
9394
|
protocol: protocol.name,
|
|
9335
|
-
token_a: tokenA,
|
|
9336
|
-
token_b: tokenB,
|
|
9337
|
-
liquidity:
|
|
9395
|
+
token_a: tokenA ?? ZERO,
|
|
9396
|
+
token_b: tokenB ?? ZERO,
|
|
9397
|
+
liquidity: removeLiquidity,
|
|
9338
9398
|
recipient,
|
|
9339
9399
|
token_id: opts.tokenId ? BigInt(opts.tokenId) : void 0
|
|
9340
9400
|
});
|
|
@@ -9713,12 +9773,20 @@ function resolveTokenAddress(registry, chainName, tokenOrAddress) {
|
|
|
9713
9773
|
return registry.resolveToken(chainName, tokenOrAddress).address;
|
|
9714
9774
|
}
|
|
9715
9775
|
var FALLBACK_ADDRESS = "0x0000000000000000000000000000000000000001";
|
|
9776
|
+
var warnedFallback = false;
|
|
9716
9777
|
function resolveWallet(override) {
|
|
9717
9778
|
if (override) return override;
|
|
9718
9779
|
try {
|
|
9719
9780
|
const { address } = resolveWalletWithSigner();
|
|
9720
9781
|
return address;
|
|
9721
9782
|
} catch {
|
|
9783
|
+
if (!warnedFallback) {
|
|
9784
|
+
process.stderr.write(
|
|
9785
|
+
`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.
|
|
9786
|
+
`
|
|
9787
|
+
);
|
|
9788
|
+
warnedFallback = true;
|
|
9789
|
+
}
|
|
9722
9790
|
return FALLBACK_ADDRESS;
|
|
9723
9791
|
}
|
|
9724
9792
|
}
|
|
@@ -9821,7 +9889,8 @@ function resolveAsset(registry, chain, asset) {
|
|
|
9821
9889
|
}
|
|
9822
9890
|
async function collectLendingRates(registry, chainName, rpc, assetAddr) {
|
|
9823
9891
|
const protos = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending);
|
|
9824
|
-
const
|
|
9892
|
+
const rates = [];
|
|
9893
|
+
const errors = [];
|
|
9825
9894
|
let first = true;
|
|
9826
9895
|
for (const proto of protos) {
|
|
9827
9896
|
if (!first) {
|
|
@@ -9830,19 +9899,22 @@ async function collectLendingRates(registry, chainName, rpc, assetAddr) {
|
|
|
9830
9899
|
first = false;
|
|
9831
9900
|
try {
|
|
9832
9901
|
const lending = createLending(proto, rpc);
|
|
9833
|
-
const
|
|
9834
|
-
|
|
9902
|
+
const r = await lending.getRates(assetAddr);
|
|
9903
|
+
rates.push(r);
|
|
9835
9904
|
} catch (err) {
|
|
9836
9905
|
process.stderr.write(`Warning: ${proto.name} rates unavailable: ${err}
|
|
9837
9906
|
`);
|
|
9907
|
+
errors.push({ protocol: proto.name, type: "lending_supply", reason: errMsg(err) });
|
|
9838
9908
|
}
|
|
9839
9909
|
}
|
|
9840
|
-
return
|
|
9910
|
+
return { rates, errors };
|
|
9841
9911
|
}
|
|
9842
9912
|
async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
|
|
9843
9913
|
const opportunities = [];
|
|
9844
|
-
const
|
|
9845
|
-
|
|
9914
|
+
const errors = [];
|
|
9915
|
+
const lendingResult = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
9916
|
+
errors.push(...lendingResult.errors);
|
|
9917
|
+
for (const r of lendingResult.rates) {
|
|
9846
9918
|
if (r.supply_apy > 0) {
|
|
9847
9919
|
opportunities.push({
|
|
9848
9920
|
protocol: r.protocol,
|
|
@@ -9868,7 +9940,8 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
|
|
|
9868
9940
|
utilization: rates.utilization
|
|
9869
9941
|
});
|
|
9870
9942
|
}
|
|
9871
|
-
} catch {
|
|
9943
|
+
} catch (e) {
|
|
9944
|
+
errors.push({ protocol: proto.name, type: "morpho_vault", reason: errMsg(e) });
|
|
9872
9945
|
}
|
|
9873
9946
|
}
|
|
9874
9947
|
}
|
|
@@ -9884,7 +9957,8 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
|
|
|
9884
9957
|
apy: info.apy ?? 0,
|
|
9885
9958
|
total_assets: info.total_assets.toString()
|
|
9886
9959
|
});
|
|
9887
|
-
} catch {
|
|
9960
|
+
} catch (e) {
|
|
9961
|
+
errors.push({ protocol: proto.name, type: "vault", reason: errMsg(e) });
|
|
9888
9962
|
}
|
|
9889
9963
|
}
|
|
9890
9964
|
}
|
|
@@ -9893,7 +9967,7 @@ async function collectAllYields(registry, chainName, rpc, asset, assetAddr) {
|
|
|
9893
9967
|
const ba = b["apy"] ?? 0;
|
|
9894
9968
|
return ba - aa;
|
|
9895
9969
|
});
|
|
9896
|
-
return opportunities;
|
|
9970
|
+
return { opportunities, errors };
|
|
9897
9971
|
}
|
|
9898
9972
|
async function runYieldScan(registry, asset, output) {
|
|
9899
9973
|
const t0 = Date.now();
|
|
@@ -10035,10 +10109,13 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10035
10109
|
const chain = registry.getChain(chainName);
|
|
10036
10110
|
const rpc = chain.effectiveRpcUrl();
|
|
10037
10111
|
const assetAddr = resolveAsset(registry, chainName, opts.asset);
|
|
10038
|
-
const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10112
|
+
const { rates: results, errors: ratesErrors } = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10039
10113
|
if (results.length === 0) {
|
|
10040
10114
|
printOutput(
|
|
10041
|
-
|
|
10115
|
+
ratesErrors.length > 0 ? {
|
|
10116
|
+
error: `Could not collect lending rates for '${opts.asset}': ${ratesErrors.length} probe(s) failed (likely RPC throttling). Retry, or set ${chainName.toUpperCase()}_RPC_URL.`,
|
|
10117
|
+
failed_probes: ratesErrors
|
|
10118
|
+
} : { error: `No lending rate data available for asset '${opts.asset}'` },
|
|
10042
10119
|
getOpts()
|
|
10043
10120
|
);
|
|
10044
10121
|
process.exit(1);
|
|
@@ -10280,9 +10357,16 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10280
10357
|
const assetAddr = resolveAsset(registry, chainName, asset);
|
|
10281
10358
|
const strategy = opts.strategy ?? "auto";
|
|
10282
10359
|
if (strategy === "auto") {
|
|
10283
|
-
const opportunities = await collectAllYields(registry, chainName, rpc, asset, assetAddr);
|
|
10360
|
+
const { opportunities, errors } = await collectAllYields(registry, chainName, rpc, asset, assetAddr);
|
|
10284
10361
|
if (opportunities.length === 0) {
|
|
10285
|
-
|
|
10362
|
+
if (errors.length > 0) {
|
|
10363
|
+
printOutput({
|
|
10364
|
+
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.`,
|
|
10365
|
+
failed_probes: errors
|
|
10366
|
+
}, getOpts());
|
|
10367
|
+
} else {
|
|
10368
|
+
printOutput({ error: `No yield opportunities found for '${asset}'` }, getOpts());
|
|
10369
|
+
}
|
|
10286
10370
|
process.exit(1);
|
|
10287
10371
|
return;
|
|
10288
10372
|
}
|
|
@@ -10312,9 +10396,12 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10312
10396
|
getOpts()
|
|
10313
10397
|
);
|
|
10314
10398
|
} else if (strategy === "best-supply") {
|
|
10315
|
-
const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10399
|
+
const { rates: results, errors: rErr } = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10316
10400
|
if (results.length === 0) {
|
|
10317
|
-
printOutput(
|
|
10401
|
+
printOutput(
|
|
10402
|
+
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}'` },
|
|
10403
|
+
getOpts()
|
|
10404
|
+
);
|
|
10318
10405
|
process.exit(1);
|
|
10319
10406
|
return;
|
|
10320
10407
|
}
|
|
@@ -10337,9 +10424,12 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10337
10424
|
getOpts()
|
|
10338
10425
|
);
|
|
10339
10426
|
} else if (strategy === "leverage-loop") {
|
|
10340
|
-
const results = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10427
|
+
const { rates: results, errors: lErr } = await collectLendingRates(registry, chainName, rpc, assetAddr);
|
|
10341
10428
|
if (results.length === 0) {
|
|
10342
|
-
printOutput(
|
|
10429
|
+
printOutput(
|
|
10430
|
+
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}'` },
|
|
10431
|
+
getOpts()
|
|
10432
|
+
);
|
|
10343
10433
|
process.exit(1);
|
|
10344
10434
|
return;
|
|
10345
10435
|
}
|
|
@@ -10403,14 +10493,14 @@ function registerYield(parent, getOpts, makeExecutor2) {
|
|
|
10403
10493
|
|
|
10404
10494
|
// src/commands/portfolio.ts
|
|
10405
10495
|
init_dist();
|
|
10406
|
-
import { encodeFunctionData as encodeFunctionData29, parseAbi as parseAbi33 } from "viem";
|
|
10496
|
+
import { createPublicClient as createPublicClient25, encodeFunctionData as encodeFunctionData29, http as http25, parseAbi as parseAbi33 } from "viem";
|
|
10407
10497
|
|
|
10408
10498
|
// src/portfolio-tracker.ts
|
|
10409
10499
|
init_dist();
|
|
10410
10500
|
import { mkdirSync, writeFileSync, readdirSync as readdirSync2, readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
|
|
10411
10501
|
import { homedir } from "os";
|
|
10412
10502
|
import { resolve as resolve3 } from "path";
|
|
10413
|
-
import { encodeFunctionData as encodeFunctionData28, parseAbi as parseAbi31 } from "viem";
|
|
10503
|
+
import { createPublicClient as createPublicClient24, encodeFunctionData as encodeFunctionData28, http as http24, parseAbi as parseAbi31 } from "viem";
|
|
10414
10504
|
var ERC20_ABI4 = parseAbi31([
|
|
10415
10505
|
"function balanceOf(address owner) external view returns (uint256)"
|
|
10416
10506
|
]);
|
|
@@ -10461,7 +10551,16 @@ async function takeSnapshot(chainName, wallet, registry) {
|
|
|
10461
10551
|
const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
|
|
10462
10552
|
const oracleAddr = oracleEntry?.contracts?.["oracle"];
|
|
10463
10553
|
const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
|
|
10554
|
+
const priceTokens = [];
|
|
10464
10555
|
if (oracleAddr) {
|
|
10556
|
+
for (const t of tokenEntries) {
|
|
10557
|
+
calls.push([
|
|
10558
|
+
oracleAddr,
|
|
10559
|
+
encodeFunctionData28({ abi: ORACLE_ABI4, functionName: "getAssetPrice", args: [t.address] })
|
|
10560
|
+
]);
|
|
10561
|
+
callLabels.push(`price:${t.address}`);
|
|
10562
|
+
priceTokens.push(t.address);
|
|
10563
|
+
}
|
|
10465
10564
|
calls.push([
|
|
10466
10565
|
oracleAddr,
|
|
10467
10566
|
encodeFunctionData28({ abi: ORACLE_ABI4, functionName: "getAssetPrice", args: [wrappedNative] })
|
|
@@ -10472,10 +10571,19 @@ async function takeSnapshot(chainName, wallet, registry) {
|
|
|
10472
10571
|
if (calls.length > 0) {
|
|
10473
10572
|
results = await multicallRead(rpc, calls);
|
|
10474
10573
|
}
|
|
10574
|
+
const balanceCount = tokenEntries.length;
|
|
10575
|
+
const lendingCount = lendingProtocols.length;
|
|
10576
|
+
const priceStartIdx = balanceCount + lendingCount;
|
|
10475
10577
|
let nativePriceUsd = 0;
|
|
10578
|
+
const priceByToken = /* @__PURE__ */ new Map();
|
|
10476
10579
|
if (oracleAddr) {
|
|
10477
|
-
|
|
10478
|
-
|
|
10580
|
+
for (let i = 0; i < priceTokens.length; i++) {
|
|
10581
|
+
const priceData = results[priceStartIdx + i] ?? null;
|
|
10582
|
+
const px = Number(decodeU256Word(priceData)) / 1e8;
|
|
10583
|
+
if (px > 0) priceByToken.set(priceTokens[i].toLowerCase(), px);
|
|
10584
|
+
}
|
|
10585
|
+
const nativePriceData = results[priceStartIdx + priceTokens.length] ?? null;
|
|
10586
|
+
nativePriceUsd = Number(decodeU256Word(nativePriceData)) / 1e8;
|
|
10479
10587
|
}
|
|
10480
10588
|
let idx = 0;
|
|
10481
10589
|
const tokens = [];
|
|
@@ -10485,8 +10593,20 @@ async function takeSnapshot(chainName, wallet, registry) {
|
|
|
10485
10593
|
const balance = decodeU256Word(results[idx] ?? null);
|
|
10486
10594
|
const balF64 = Number(balance) / 10 ** entry.decimals;
|
|
10487
10595
|
const symbolUpper = entry.symbol.toUpperCase();
|
|
10488
|
-
const
|
|
10489
|
-
|
|
10596
|
+
const tokenAddrLower = entry.address.toLowerCase();
|
|
10597
|
+
let priceUsd;
|
|
10598
|
+
let valueUsd;
|
|
10599
|
+
if (symbolUpper.includes("USD")) {
|
|
10600
|
+
priceUsd = 1;
|
|
10601
|
+
valueUsd = balF64;
|
|
10602
|
+
} else if (tokenAddrLower === wrappedNative.toLowerCase()) {
|
|
10603
|
+
priceUsd = nativePriceUsd;
|
|
10604
|
+
valueUsd = balF64 * nativePriceUsd;
|
|
10605
|
+
} else {
|
|
10606
|
+
const px = priceByToken.get(tokenAddrLower);
|
|
10607
|
+
priceUsd = px ?? 0;
|
|
10608
|
+
valueUsd = px && px > 0 ? balF64 * px : 0;
|
|
10609
|
+
}
|
|
10490
10610
|
totalValueUsd += valueUsd;
|
|
10491
10611
|
tokens.push({
|
|
10492
10612
|
token: entry.address,
|
|
@@ -10527,6 +10647,23 @@ async function takeSnapshot(chainName, wallet, registry) {
|
|
|
10527
10647
|
}
|
|
10528
10648
|
idx++;
|
|
10529
10649
|
}
|
|
10650
|
+
try {
|
|
10651
|
+
const client = createPublicClient24({ transport: http24(rpc) });
|
|
10652
|
+
const nativeBalance = await client.getBalance({ address: user });
|
|
10653
|
+
if (nativeBalance > 0n) {
|
|
10654
|
+
const nativeF64 = Number(nativeBalance) / 1e18;
|
|
10655
|
+
const nativeValueUsd = nativeF64 * nativePriceUsd;
|
|
10656
|
+
totalValueUsd += nativeValueUsd;
|
|
10657
|
+
tokens.push({
|
|
10658
|
+
token: wrappedNative,
|
|
10659
|
+
symbol: chain.native_token ?? "NATIVE",
|
|
10660
|
+
balance: nativeBalance,
|
|
10661
|
+
value_usd: nativeValueUsd,
|
|
10662
|
+
price_usd: nativePriceUsd
|
|
10663
|
+
});
|
|
10664
|
+
}
|
|
10665
|
+
} catch {
|
|
10666
|
+
}
|
|
10530
10667
|
return {
|
|
10531
10668
|
timestamp: Date.now(),
|
|
10532
10669
|
chain: chainName,
|
|
@@ -10644,6 +10781,10 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10644
10781
|
const calls = [];
|
|
10645
10782
|
const callLabels = [];
|
|
10646
10783
|
const tokenSymbols = (registry.tokens.get(chainName) ?? []).map((t) => t.symbol);
|
|
10784
|
+
const tokenAddrsByCallIdx = [];
|
|
10785
|
+
const oracleEntry = registry.getProtocolsForChain(chainName).find((p) => p.interface === "aave_v3" && p.contracts?.["oracle"]);
|
|
10786
|
+
const oracleAddr = oracleEntry?.contracts?.["oracle"];
|
|
10787
|
+
const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
|
|
10647
10788
|
for (const symbol of tokenSymbols) {
|
|
10648
10789
|
let entry;
|
|
10649
10790
|
try {
|
|
@@ -10657,6 +10798,7 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10657
10798
|
encodeFunctionData29({ abi: ERC20_ABI5, functionName: "balanceOf", args: [user] })
|
|
10658
10799
|
]);
|
|
10659
10800
|
callLabels.push(`balance:${symbol}`);
|
|
10801
|
+
tokenAddrsByCallIdx.push(entry.address);
|
|
10660
10802
|
}
|
|
10661
10803
|
const lendingProtocols = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending && p.interface === "aave_v3").filter((p) => p.contracts?.["pool"]);
|
|
10662
10804
|
for (const p of lendingProtocols) {
|
|
@@ -10666,10 +10808,16 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10666
10808
|
]);
|
|
10667
10809
|
callLabels.push(`lending:${p.name}`);
|
|
10668
10810
|
}
|
|
10669
|
-
const
|
|
10670
|
-
const oracleAddr = oracleEntry?.contracts?.["oracle"];
|
|
10671
|
-
const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
|
|
10811
|
+
const priceTokens = [];
|
|
10672
10812
|
if (oracleAddr) {
|
|
10813
|
+
for (const tokenAddr of tokenAddrsByCallIdx) {
|
|
10814
|
+
calls.push([
|
|
10815
|
+
oracleAddr,
|
|
10816
|
+
encodeFunctionData29({ abi: ORACLE_ABI5, functionName: "getAssetPrice", args: [tokenAddr] })
|
|
10817
|
+
]);
|
|
10818
|
+
callLabels.push(`price:${tokenAddr}`);
|
|
10819
|
+
priceTokens.push(tokenAddr);
|
|
10820
|
+
}
|
|
10673
10821
|
calls.push([
|
|
10674
10822
|
oracleAddr,
|
|
10675
10823
|
encodeFunctionData29({ abi: ORACLE_ABI5, functionName: "getAssetPrice", args: [wrappedNative] })
|
|
@@ -10694,10 +10842,19 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10694
10842
|
printOutput({ error: `Multicall failed: ${errMsg(e)}` }, mode);
|
|
10695
10843
|
return;
|
|
10696
10844
|
}
|
|
10845
|
+
const balanceCallCount = tokenAddrsByCallIdx.length;
|
|
10846
|
+
const lendingCallCount = lendingProtocols.length;
|
|
10847
|
+
const priceStartIdx = balanceCallCount + lendingCallCount;
|
|
10697
10848
|
let nativePriceUsd = 0;
|
|
10849
|
+
const priceByToken = /* @__PURE__ */ new Map();
|
|
10698
10850
|
if (oracleAddr) {
|
|
10699
|
-
|
|
10700
|
-
|
|
10851
|
+
for (let i = 0; i < priceTokens.length; i++) {
|
|
10852
|
+
const priceData = results[priceStartIdx + i] ?? null;
|
|
10853
|
+
const px = Number(decodeU2562(priceData)) / 1e8;
|
|
10854
|
+
if (px > 0) priceByToken.set(priceTokens[i].toLowerCase(), px);
|
|
10855
|
+
}
|
|
10856
|
+
const nativePriceData = results[priceStartIdx + priceTokens.length] ?? null;
|
|
10857
|
+
nativePriceUsd = Number(decodeU2562(nativePriceData)) / 1e8;
|
|
10701
10858
|
}
|
|
10702
10859
|
let totalValueUsd = 0;
|
|
10703
10860
|
let idx = 0;
|
|
@@ -10716,12 +10873,21 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10716
10873
|
const decimals = entry.decimals;
|
|
10717
10874
|
const balF64 = Number(balance) / 10 ** decimals;
|
|
10718
10875
|
const symbolUpper = symbol.toUpperCase();
|
|
10719
|
-
const
|
|
10720
|
-
|
|
10876
|
+
const tokenAddrLower = entry.address.toLowerCase();
|
|
10877
|
+
let valueUsd;
|
|
10878
|
+
if (symbolUpper.includes("USD")) {
|
|
10879
|
+
valueUsd = balF64;
|
|
10880
|
+
} else if (tokenAddrLower === wrappedNative.toLowerCase()) {
|
|
10881
|
+
valueUsd = balF64 * nativePriceUsd;
|
|
10882
|
+
} else {
|
|
10883
|
+
const px = priceByToken.get(tokenAddrLower);
|
|
10884
|
+
valueUsd = px && px > 0 ? balF64 * px : null;
|
|
10885
|
+
}
|
|
10886
|
+
if (valueUsd !== null) totalValueUsd += valueUsd;
|
|
10721
10887
|
tokenBalances.push({
|
|
10722
10888
|
symbol,
|
|
10723
10889
|
balance: balF64.toFixed(4),
|
|
10724
|
-
value_usd: valueUsd.toFixed(2)
|
|
10890
|
+
value_usd: valueUsd !== null ? valueUsd.toFixed(2) : null
|
|
10725
10891
|
});
|
|
10726
10892
|
}
|
|
10727
10893
|
idx++;
|
|
@@ -10751,11 +10917,23 @@ function registerPortfolio(parent, getOpts) {
|
|
|
10751
10917
|
}
|
|
10752
10918
|
idx++;
|
|
10753
10919
|
}
|
|
10920
|
+
let nativeBalance = 0n;
|
|
10921
|
+
let nativeValueUsd = 0;
|
|
10922
|
+
try {
|
|
10923
|
+
const client = createPublicClient25({ transport: http25(rpc) });
|
|
10924
|
+
nativeBalance = await client.getBalance({ address: user });
|
|
10925
|
+
const nativeF64 = Number(nativeBalance) / 1e18;
|
|
10926
|
+
nativeValueUsd = nativeF64 * nativePriceUsd;
|
|
10927
|
+
if (nativeBalance > 0n) totalValueUsd += nativeValueUsd;
|
|
10928
|
+
} catch {
|
|
10929
|
+
}
|
|
10754
10930
|
printOutput(
|
|
10755
10931
|
{
|
|
10756
10932
|
address: user,
|
|
10757
10933
|
chain: chain.name,
|
|
10758
10934
|
native_price_usd: nativePriceUsd.toFixed(2),
|
|
10935
|
+
native_balance: (Number(nativeBalance) / 1e18).toFixed(6),
|
|
10936
|
+
native_value_usd: nativeValueUsd.toFixed(2),
|
|
10759
10937
|
total_value_usd: totalValueUsd.toFixed(2),
|
|
10760
10938
|
token_balances: tokenBalances,
|
|
10761
10939
|
lending_positions: lendingPositions
|
|
@@ -11021,38 +11199,50 @@ function registerPrice(parent, getOpts) {
|
|
|
11021
11199
|
|
|
11022
11200
|
// src/commands/wallet.ts
|
|
11023
11201
|
init_dist();
|
|
11024
|
-
import { createPublicClient as
|
|
11202
|
+
import { createPublicClient as createPublicClient26, http as http26, formatEther } from "viem";
|
|
11203
|
+
function resolveCurrentAddress(override) {
|
|
11204
|
+
if (override) return { address: override, source: "flag" };
|
|
11205
|
+
try {
|
|
11206
|
+
const { address, signer } = resolveWalletWithSigner();
|
|
11207
|
+
return { address, source: signer ? "ows" : process.env["DEFI_PRIVATE_KEY"] ? "private_key" : "env" };
|
|
11208
|
+
} catch {
|
|
11209
|
+
return { address: null, source: "none" };
|
|
11210
|
+
}
|
|
11211
|
+
}
|
|
11025
11212
|
function registerWallet(parent, getOpts) {
|
|
11026
11213
|
const wallet = parent.command("wallet").description("Wallet management");
|
|
11027
|
-
wallet.command("balance").description("Show native token balance").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
|
|
11214
|
+
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) => {
|
|
11028
11215
|
const chainName = requireChain(parent, getOpts);
|
|
11029
11216
|
if (!chainName) return;
|
|
11030
|
-
const addr = opts.address
|
|
11217
|
+
const { address: addr, source } = resolveCurrentAddress(opts.address);
|
|
11031
11218
|
if (!addr) {
|
|
11032
|
-
printOutput({
|
|
11219
|
+
printOutput({
|
|
11220
|
+
error: "No wallet configured. Set DEFI_WALLET_ADDRESS, set DEFI_PRIVATE_KEY, or pass --address."
|
|
11221
|
+
}, getOpts());
|
|
11033
11222
|
return;
|
|
11034
11223
|
}
|
|
11035
11224
|
const registry = Registry.loadEmbedded();
|
|
11036
11225
|
const chain = registry.getChain(chainName);
|
|
11037
|
-
const client =
|
|
11226
|
+
const client = createPublicClient26({ transport: http26(chain.effectiveRpcUrl()) });
|
|
11038
11227
|
const balance = await client.getBalance({ address: addr });
|
|
11039
11228
|
printOutput({
|
|
11040
11229
|
chain: chain.name,
|
|
11041
11230
|
address: addr,
|
|
11231
|
+
wallet_source: source,
|
|
11042
11232
|
native_token: chain.native_token,
|
|
11043
11233
|
balance_wei: balance,
|
|
11044
11234
|
balance_formatted: formatEther(balance)
|
|
11045
11235
|
}, getOpts());
|
|
11046
11236
|
});
|
|
11047
11237
|
wallet.command("address").description("Show configured wallet address").action(async () => {
|
|
11048
|
-
const
|
|
11049
|
-
printOutput({ address
|
|
11238
|
+
const { address, source } = resolveCurrentAddress();
|
|
11239
|
+
printOutput({ address, source }, getOpts());
|
|
11050
11240
|
});
|
|
11051
11241
|
}
|
|
11052
11242
|
|
|
11053
11243
|
// src/commands/token.ts
|
|
11054
11244
|
init_dist();
|
|
11055
|
-
import { createPublicClient as
|
|
11245
|
+
import { createPublicClient as createPublicClient27, http as http27, maxUint256 } from "viem";
|
|
11056
11246
|
function registerToken(parent, getOpts, makeExecutor2) {
|
|
11057
11247
|
const token = parent.command("token").description("Token operations: approve, allowance, transfer, balance");
|
|
11058
11248
|
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) => {
|
|
@@ -11065,7 +11255,7 @@ function registerToken(parent, getOpts, makeExecutor2) {
|
|
|
11065
11255
|
}
|
|
11066
11256
|
const registry = Registry.loadEmbedded();
|
|
11067
11257
|
const chain = registry.getChain(chainName);
|
|
11068
|
-
const client =
|
|
11258
|
+
const client = createPublicClient27({ transport: http27(chain.effectiveRpcUrl()) });
|
|
11069
11259
|
const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
|
|
11070
11260
|
const [balance, symbol, decimals] = await Promise.all([
|
|
11071
11261
|
client.readContract({ address: tokenAddr, abi: erc20Abi, functionName: "balanceOf", args: [owner] }),
|
|
@@ -11101,7 +11291,7 @@ function registerToken(parent, getOpts, makeExecutor2) {
|
|
|
11101
11291
|
}
|
|
11102
11292
|
const registry = Registry.loadEmbedded();
|
|
11103
11293
|
const chain = registry.getChain(chainName);
|
|
11104
|
-
const client =
|
|
11294
|
+
const client = createPublicClient27({ transport: http27(chain.effectiveRpcUrl()) });
|
|
11105
11295
|
const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
|
|
11106
11296
|
const allowance = await client.readContract({
|
|
11107
11297
|
address: tokenAddr,
|
|
@@ -11219,6 +11409,17 @@ var CCTP_USDC_ADDRESSES = {
|
|
|
11219
11409
|
base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
11220
11410
|
polygon: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"
|
|
11221
11411
|
};
|
|
11412
|
+
function cctpMinFeeGuard(amountWei, maxFeeSubunits, feeUsdc) {
|
|
11413
|
+
if (amountWei <= maxFeeSubunits) {
|
|
11414
|
+
const amountUsdc = Number(amountWei) / 1e6;
|
|
11415
|
+
return {
|
|
11416
|
+
error: `CCTP: amount ${amountWei.toString()} (${amountUsdc} USDC) is below the minimum bridge fee of ${maxFeeSubunits} (${feeUsdc} USDC). Increase --amount.`,
|
|
11417
|
+
minimum_amount_wei: maxFeeSubunits.toString(),
|
|
11418
|
+
minimum_amount_usdc: feeUsdc
|
|
11419
|
+
};
|
|
11420
|
+
}
|
|
11421
|
+
return null;
|
|
11422
|
+
}
|
|
11222
11423
|
async function getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc) {
|
|
11223
11424
|
try {
|
|
11224
11425
|
const res = await fetch(`${CCTP_FEE_API}/${srcDomain}/${dstDomain}`);
|
|
@@ -11307,12 +11508,9 @@ function registerBridge(parent, getOpts) {
|
|
|
11307
11508
|
}
|
|
11308
11509
|
const amountUsdc = Number(BigInt(opts.amount)) / 1e6;
|
|
11309
11510
|
const { fee, maxFeeSubunits } = await getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc);
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
minimum_amount_wei: maxFeeSubunits.toString(),
|
|
11314
|
-
minimum_amount_usdc: fee
|
|
11315
|
-
}, getOpts());
|
|
11511
|
+
const guardErr = cctpMinFeeGuard(BigInt(opts.amount), maxFeeSubunits, fee);
|
|
11512
|
+
if (guardErr) {
|
|
11513
|
+
printOutput(guardErr, getOpts());
|
|
11316
11514
|
return;
|
|
11317
11515
|
}
|
|
11318
11516
|
const recipientPadded = `0x${"0".repeat(24)}${recipient.replace("0x", "").toLowerCase()}`;
|
|
@@ -11593,12 +11791,14 @@ function registerSwap(parent, getOpts, makeExecutor2) {
|
|
|
11593
11791
|
slippagePct,
|
|
11594
11792
|
wallet
|
|
11595
11793
|
);
|
|
11794
|
+
const fromLower = fromAddr.toLowerCase();
|
|
11795
|
+
const isNativeInput = fromLower === "0x0000000000000000000000000000000000000000" || fromLower === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
|
11596
11796
|
const tx = {
|
|
11597
11797
|
description: `OpenOcean: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
|
|
11598
11798
|
to: swap.to,
|
|
11599
11799
|
data: swap.data,
|
|
11600
11800
|
value: parseBigIntValue(swap.value),
|
|
11601
|
-
approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }]
|
|
11801
|
+
...isNativeInput ? {} : { approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }] }
|
|
11602
11802
|
};
|
|
11603
11803
|
const result = await executor.execute(tx);
|
|
11604
11804
|
printOutput({
|
|
@@ -11690,7 +11890,8 @@ import pc2 from "picocolors";
|
|
|
11690
11890
|
import { createInterface } from "readline";
|
|
11691
11891
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
11692
11892
|
import { resolve as resolve4 } from "path";
|
|
11693
|
-
|
|
11893
|
+
import { homedir as homedir2 } from "os";
|
|
11894
|
+
var DEFI_DIR = resolve4(homedir2(), ".defi");
|
|
11694
11895
|
var ENV_FILE = resolve4(DEFI_DIR, ".env");
|
|
11695
11896
|
function ensureDefiDir() {
|
|
11696
11897
|
if (!existsSync3(DEFI_DIR)) mkdirSync2(DEFI_DIR, { recursive: true, mode: 448 });
|
|
@@ -11725,6 +11926,44 @@ function writeEnvFile(env) {
|
|
|
11725
11926
|
function ask(rl, question) {
|
|
11726
11927
|
return new Promise((res) => rl.question(question, (answer) => res(answer.trim())));
|
|
11727
11928
|
}
|
|
11929
|
+
function askSecret(rl, question) {
|
|
11930
|
+
const stdin = process.stdin;
|
|
11931
|
+
if (!stdin.isTTY) return ask(rl, question);
|
|
11932
|
+
process.stdout.write(question);
|
|
11933
|
+
const rlAny = rl;
|
|
11934
|
+
const original = rlAny._writeToOutput;
|
|
11935
|
+
rlAny._writeToOutput = (s) => {
|
|
11936
|
+
if (s.includes("\n") || s.includes("\r")) {
|
|
11937
|
+
original?.call(rl, s);
|
|
11938
|
+
}
|
|
11939
|
+
};
|
|
11940
|
+
return new Promise((res) => {
|
|
11941
|
+
rl.question("", (answer) => {
|
|
11942
|
+
rlAny._writeToOutput = original;
|
|
11943
|
+
process.stdout.write("\n");
|
|
11944
|
+
res(answer.trim());
|
|
11945
|
+
});
|
|
11946
|
+
});
|
|
11947
|
+
}
|
|
11948
|
+
function maskRpcUrl(s) {
|
|
11949
|
+
try {
|
|
11950
|
+
const u = new URL(s);
|
|
11951
|
+
if (u.pathname && u.pathname !== "/" && u.pathname.length > 1) {
|
|
11952
|
+
return `${u.protocol}//${u.host}/***`;
|
|
11953
|
+
}
|
|
11954
|
+
return `${u.protocol}//${u.host}`;
|
|
11955
|
+
} catch {
|
|
11956
|
+
return "***";
|
|
11957
|
+
}
|
|
11958
|
+
}
|
|
11959
|
+
function isValidRpcUrl(s) {
|
|
11960
|
+
try {
|
|
11961
|
+
const u = new URL(s);
|
|
11962
|
+
return u.protocol === "http:" || u.protocol === "https:";
|
|
11963
|
+
} catch {
|
|
11964
|
+
return false;
|
|
11965
|
+
}
|
|
11966
|
+
}
|
|
11728
11967
|
function isValidAddress(s) {
|
|
11729
11968
|
return /^0x[0-9a-fA-F]{40}$/.test(s);
|
|
11730
11969
|
}
|
|
@@ -11749,7 +11988,14 @@ function registerSetup(program2) {
|
|
|
11749
11988
|
if (Object.keys(existing).length > 0) {
|
|
11750
11989
|
console.log(pc2.white(" Current configuration:"));
|
|
11751
11990
|
for (const [key, value] of Object.entries(existing)) {
|
|
11752
|
-
|
|
11991
|
+
let masked;
|
|
11992
|
+
if (key.toLowerCase().includes("key")) {
|
|
11993
|
+
masked = value.slice(0, 6) + "..." + value.slice(-4);
|
|
11994
|
+
} else if (key.endsWith("RPC_URL")) {
|
|
11995
|
+
masked = maskRpcUrl(value);
|
|
11996
|
+
} else {
|
|
11997
|
+
masked = value;
|
|
11998
|
+
}
|
|
11753
11999
|
console.log(` ${pc2.cyan(key.padEnd(24))} ${pc2.gray(masked)}`);
|
|
11754
12000
|
}
|
|
11755
12001
|
console.log();
|
|
@@ -11763,7 +12009,7 @@ function registerSetup(program2) {
|
|
|
11763
12009
|
}
|
|
11764
12010
|
const newEnv = {};
|
|
11765
12011
|
console.log(pc2.cyan(pc2.bold(" Wallet")));
|
|
11766
|
-
const privateKey = await
|
|
12012
|
+
const privateKey = await askSecret(rl, " Private key (optional, for --broadcast, 0x... \u2014 input hidden): ");
|
|
11767
12013
|
if (privateKey) {
|
|
11768
12014
|
const normalized = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
|
|
11769
12015
|
if (!isValidPrivateKey(normalized)) {
|
|
@@ -11799,6 +12045,10 @@ function registerSetup(program2) {
|
|
|
11799
12045
|
for (const { env, label } of rpcPrompts) {
|
|
11800
12046
|
const value = await ask(rl, ` ${label} RPC URL: `);
|
|
11801
12047
|
if (value) {
|
|
12048
|
+
if (!isValidRpcUrl(value)) {
|
|
12049
|
+
console.log(pc2.yellow(` Invalid URL (${value}). Skipped \u2014 re-run setup to retry.`));
|
|
12050
|
+
continue;
|
|
12051
|
+
}
|
|
11802
12052
|
newEnv[env] = value;
|
|
11803
12053
|
console.log(` ${pc2.green("OK")} ${label} RPC set`);
|
|
11804
12054
|
}
|
|
@@ -11822,7 +12072,7 @@ function registerSetup(program2) {
|
|
|
11822
12072
|
];
|
|
11823
12073
|
for (const [k, label] of rpcSummary) {
|
|
11824
12074
|
const v = finalEnv[k];
|
|
11825
|
-
if (v) console.log(` ${label}: ${pc2.gray(v)}`);
|
|
12075
|
+
if (v) console.log(` ${label}: ${pc2.gray(maskRpcUrl(v))}`);
|
|
11826
12076
|
}
|
|
11827
12077
|
console.log(pc2.bold(pc2.white("\n Next steps:")));
|
|
11828
12078
|
console.log(` ${pc2.green("defi portfolio")} view balances & positions`);
|
|
@@ -11841,11 +12091,11 @@ function registerSetup(program2) {
|
|
|
11841
12091
|
// src/commands/ows.ts
|
|
11842
12092
|
import pc3 from "picocolors";
|
|
11843
12093
|
init_dist();
|
|
11844
|
-
import { createPublicClient as
|
|
12094
|
+
import { createPublicClient as createPublicClient28, http as http28, formatEther as formatEther2 } from "viem";
|
|
11845
12095
|
async function getEvmBalance(address, chainName) {
|
|
11846
12096
|
const registry = Registry.loadEmbedded();
|
|
11847
12097
|
const chain = registry.getChain(chainName);
|
|
11848
|
-
const client =
|
|
12098
|
+
const client = createPublicClient28({ transport: http28(chain.effectiveRpcUrl()) });
|
|
11849
12099
|
const balance = await client.getBalance({ address });
|
|
11850
12100
|
return {
|
|
11851
12101
|
native_token: chain.native_token,
|