@hypurrquant/defi-cli 1.0.12 → 1.0.13
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/README.md +9 -2
- package/config/chains.toml +6 -1
- package/config/protocols/dex/aerodrome_base.toml +2 -1
- package/config/protocols/dex/aerodrome_cl.toml +2 -1
- package/config/protocols/dex/apeswap_bnb.toml +1 -1
- package/config/protocols/dex/babydogeswap_bnb.toml +1 -1
- package/config/protocols/dex/bakeryswap_bnb.toml +2 -2
- package/config/protocols/dex/biswap_bnb.toml +1 -1
- package/config/protocols/dex/bscswap_bnb.toml +1 -1
- package/config/protocols/dex/curve_hyperevm.toml +1 -1
- package/config/protocols/dex/fstswap_bnb.toml +1 -1
- package/config/protocols/dex/hybra.toml +4 -2
- package/config/protocols/dex/hyperswap.toml +1 -1
- package/config/protocols/dex/kittenswap.toml +1 -1
- package/config/protocols/dex/merchantmoe_mantle.toml +2 -1
- package/config/protocols/dex/nest.toml +6 -5
- package/config/protocols/dex/pancakeswap_v2_bnb.toml +1 -1
- package/config/protocols/dex/pancakeswap_v3_bnb.toml +2 -1
- package/config/protocols/dex/project_x.toml +1 -1
- package/config/protocols/dex/ramses_cl.toml +1 -1
- package/config/protocols/dex/ramses_hl.toml +1 -1
- package/config/protocols/dex/thena_v1_bnb.toml +2 -1
- package/config/protocols/dex/traderjoe_monad.toml +2 -1
- package/config/protocols/dex/uniswap_v2_monad.toml +2 -1
- package/config/protocols/dex/uniswap_v3_base.toml +2 -1
- package/config/protocols/dex/uniswap_v3_bnb.toml +3 -1
- package/config/protocols/dex/uniswap_v3_mantle.toml +2 -1
- package/config/protocols/dex/uniswap_v3_monad.toml +2 -1
- package/config/protocols/lending/felix_morpho.toml +16 -0
- package/config/protocols/lending/hyperlend.toml +1 -1
- package/config/protocols/lending/hypurrfi.toml +1 -1
- package/config/protocols/lending/kinza_bnb.toml +1 -1
- package/config/protocols/lending/morpho_blue_monad.toml +17 -0
- package/config/protocols/lending/venus_flux_bnb.toml +5 -1
- package/dist/index.js +1277 -246
- package/dist/index.js.map +1 -1
- package/dist/main.js +1282 -251
- package/dist/main.js.map +1 -1
- package/dist/mcp-server.js +799 -195
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/skills/defi-cli/SKILL.md +17 -4
- package/skills/defi-cli/references/commands.md +2 -2
- package/skills/defi-cli/references/protocols.md +2 -2
package/dist/index.js
CHANGED
|
@@ -18,6 +18,12 @@ import { resolve } from "path";
|
|
|
18
18
|
import { fileURLToPath } from "url";
|
|
19
19
|
import { parse } from "smol-toml";
|
|
20
20
|
import { existsSync } from "fs";
|
|
21
|
+
function defaultSwapSlippage() {
|
|
22
|
+
return { bps: 50 };
|
|
23
|
+
}
|
|
24
|
+
function applyMinSlippage(slippage, amount) {
|
|
25
|
+
return amount * BigInt(1e4 - slippage.bps) / 10000n;
|
|
26
|
+
}
|
|
21
27
|
function jsonReplacerDecimal(_key, value) {
|
|
22
28
|
if (typeof value === "bigint") {
|
|
23
29
|
return value.toString();
|
|
@@ -55,11 +61,15 @@ function buildTransfer(token, to, amount) {
|
|
|
55
61
|
gas_estimate: 65e3
|
|
56
62
|
};
|
|
57
63
|
}
|
|
58
|
-
function getProvider(rpcUrl) {
|
|
59
|
-
const
|
|
64
|
+
function getProvider(rpcUrl, chain) {
|
|
65
|
+
const key = chain ? `${rpcUrl}@${chain.id}` : rpcUrl;
|
|
66
|
+
const cached = providerCache.get(key);
|
|
60
67
|
if (cached) return cached;
|
|
61
|
-
const client = createPublicClient({
|
|
62
|
-
|
|
68
|
+
const client = createPublicClient({
|
|
69
|
+
transport: http(rpcUrl),
|
|
70
|
+
...chain ? { chain } : {}
|
|
71
|
+
});
|
|
72
|
+
providerCache.set(key, client);
|
|
63
73
|
return client;
|
|
64
74
|
}
|
|
65
75
|
async function multicallRead(rpcUrl, calls) {
|
|
@@ -229,6 +239,28 @@ var init_dist = __esm({
|
|
|
229
239
|
const chainEnv = this.name.toUpperCase().replace(/ /g, "_") + "_RPC_URL";
|
|
230
240
|
return process.env[chainEnv] ?? this.rpc_url;
|
|
231
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Build a viem Chain object pinned to this config so wallet/public clients
|
|
244
|
+
* can sign with an explicit chainId rather than auto-fetching it from the
|
|
245
|
+
* RPC. SSOT 7.4: anchoring chainId at client-construction time defends
|
|
246
|
+
* against an MITM RPC that returns the wrong eth_chainId, and keeps
|
|
247
|
+
* offline signing safe against RPC drift.
|
|
248
|
+
*/
|
|
249
|
+
viemChain() {
|
|
250
|
+
const rpcUrl = this.effectiveRpcUrl();
|
|
251
|
+
return {
|
|
252
|
+
id: this.chain_id,
|
|
253
|
+
name: this.name,
|
|
254
|
+
nativeCurrency: {
|
|
255
|
+
name: this.native_token,
|
|
256
|
+
symbol: this.native_token,
|
|
257
|
+
decimals: 18
|
|
258
|
+
},
|
|
259
|
+
rpcUrls: { default: { http: [rpcUrl] } },
|
|
260
|
+
...this.explorer_url ? { blockExplorers: { default: { name: this.name, url: this.explorer_url } } } : {},
|
|
261
|
+
...this.multicall3 ? { contracts: { multicall3: { address: this.multicall3 } } } : {}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
232
264
|
};
|
|
233
265
|
ProtocolCategory = /* @__PURE__ */ ((ProtocolCategory2) => {
|
|
234
266
|
ProtocolCategory2["Dex"] = "dex";
|
|
@@ -420,13 +452,13 @@ __export(dist_exports, {
|
|
|
420
452
|
import { encodeFunctionData as encodeFunctionData4, parseAbi as parseAbi4, createPublicClient as createPublicClient4, http as http4, decodeAbiParameters } from "viem";
|
|
421
453
|
import { encodeFunctionData as encodeFunctionData22, parseAbi as parseAbi22, createPublicClient as createPublicClient22, http as http22, decodeFunctionResult as decodeFunctionResult2, decodeAbiParameters as decodeAbiParameters2 } from "viem";
|
|
422
454
|
import { encodeFunctionData as encodeFunctionData32, parseAbi as parseAbi32, createPublicClient as createPublicClient32, http as http32, decodeAbiParameters as decodeAbiParameters3, concatHex, zeroAddress } from "viem";
|
|
423
|
-
import { encodeFunctionData as encodeFunctionData42, parseAbi as parseAbi42
|
|
455
|
+
import { encodeFunctionData as encodeFunctionData42, parseAbi as parseAbi42 } from "viem";
|
|
424
456
|
import { encodeFunctionData as encodeFunctionData5, parseAbi as parseAbi5 } from "viem";
|
|
425
457
|
import { encodeFunctionData as encodeFunctionData6, parseAbi as parseAbi6, decodeAbiParameters as decodeAbiParameters4 } from "viem";
|
|
426
|
-
import { encodeFunctionData as encodeFunctionData7, parseAbi as parseAbi7, createPublicClient as createPublicClient42, http as http42, zeroAddress as
|
|
427
|
-
import { createPublicClient as createPublicClient5, decodeFunctionResult as decodeFunctionResult22, encodeFunctionData as encodeFunctionData8, http as http5, parseAbi as parseAbi8, zeroAddress as
|
|
428
|
-
import { encodeFunctionData as encodeFunctionData9, parseAbi as parseAbi9, zeroAddress as
|
|
429
|
-
import { createPublicClient as createPublicClient6, decodeFunctionResult as decodeFunctionResult3, encodeFunctionData as encodeFunctionData10, http as http6, parseAbi as parseAbi10, zeroAddress as
|
|
458
|
+
import { encodeFunctionData as encodeFunctionData7, parseAbi as parseAbi7, createPublicClient as createPublicClient42, http as http42, zeroAddress as zeroAddress2 } from "viem";
|
|
459
|
+
import { createPublicClient as createPublicClient5, decodeFunctionResult as decodeFunctionResult22, encodeFunctionData as encodeFunctionData8, http as http5, parseAbi as parseAbi8, zeroAddress as zeroAddress3 } from "viem";
|
|
460
|
+
import { encodeFunctionData as encodeFunctionData9, parseAbi as parseAbi9, zeroAddress as zeroAddress4 } from "viem";
|
|
461
|
+
import { createPublicClient as createPublicClient6, decodeFunctionResult as decodeFunctionResult3, encodeFunctionData as encodeFunctionData10, http as http6, parseAbi as parseAbi10, zeroAddress as zeroAddress5 } from "viem";
|
|
430
462
|
import { encodeFunctionData as encodeFunctionData11, parseAbi as parseAbi11, createPublicClient as createPublicClient7, http as http7 } from "viem";
|
|
431
463
|
import {
|
|
432
464
|
encodeFunctionData as encodeFunctionData12,
|
|
@@ -444,24 +476,24 @@ import {
|
|
|
444
476
|
keccak256,
|
|
445
477
|
parseAbi as parseAbi13,
|
|
446
478
|
decodeFunctionResult as decodeFunctionResult5,
|
|
447
|
-
zeroAddress as
|
|
479
|
+
zeroAddress as zeroAddress6
|
|
448
480
|
} from "viem";
|
|
449
|
-
import { createPublicClient as createPublicClient10, http as http10, parseAbi as parseAbi14, encodeFunctionData as encodeFunctionData14, decodeFunctionResult as decodeFunctionResult6, zeroAddress as
|
|
450
|
-
import { createPublicClient as createPublicClient11, http as http11, parseAbi as parseAbi15, encodeFunctionData as encodeFunctionData15, zeroAddress as
|
|
481
|
+
import { createPublicClient as createPublicClient10, http as http10, parseAbi as parseAbi14, encodeFunctionData as encodeFunctionData14, decodeFunctionResult as decodeFunctionResult6, zeroAddress as zeroAddress7 } from "viem";
|
|
482
|
+
import { createPublicClient as createPublicClient11, http as http11, parseAbi as parseAbi15, encodeFunctionData as encodeFunctionData15, zeroAddress as zeroAddress8 } from "viem";
|
|
451
483
|
import { createPublicClient as createPublicClient12, http as http12, parseAbi as parseAbi16 } from "viem";
|
|
452
484
|
import { createPublicClient as createPublicClient13, http as http13, parseAbi as parseAbi17, encodeFunctionData as encodeFunctionData16 } from "viem";
|
|
453
485
|
import { createPublicClient as createPublicClient14, http as http14, parseAbi as parseAbi18, encodeFunctionData as encodeFunctionData17 } from "viem";
|
|
454
486
|
import { createPublicClient as createPublicClient15, http as http15, parseAbi as parseAbi19, encodeFunctionData as encodeFunctionData18 } from "viem";
|
|
455
|
-
import { parseAbi as parseAbi20, encodeFunctionData as encodeFunctionData19, decodeFunctionResult as decodeFunctionResult7, zeroAddress as
|
|
456
|
-
import { createPublicClient as
|
|
457
|
-
import { createPublicClient as
|
|
458
|
-
import { createPublicClient as
|
|
487
|
+
import { parseAbi as parseAbi20, encodeFunctionData as encodeFunctionData19, decodeFunctionResult as decodeFunctionResult7, zeroAddress as zeroAddress9, createPublicClient as createPublicClient16, http as http16 } from "viem";
|
|
488
|
+
import { createPublicClient as createPublicClient17, http as http17, parseAbi as parseAbi21, encodeFunctionData as encodeFunctionData20, zeroAddress as zeroAddress10 } from "viem";
|
|
489
|
+
import { createPublicClient as createPublicClient18, http as http18, parseAbi as parseAbi222 } from "viem";
|
|
490
|
+
import { createPublicClient as createPublicClient19, http as http19, parseAbi as parseAbi23, encodeFunctionData as encodeFunctionData21 } from "viem";
|
|
459
491
|
import { parseAbi as parseAbi24, encodeFunctionData as encodeFunctionData222 } from "viem";
|
|
460
|
-
import { createPublicClient as
|
|
461
|
-
import { createPublicClient as
|
|
492
|
+
import { createPublicClient as createPublicClient20, http as http20, parseAbi as parseAbi25, encodeFunctionData as encodeFunctionData23, zeroAddress as zeroAddress11 } from "viem";
|
|
493
|
+
import { createPublicClient as createPublicClient21, http as http21, parseAbi as parseAbi26, encodeFunctionData as encodeFunctionData24, zeroAddress as zeroAddress12 } from "viem";
|
|
462
494
|
import { parseAbi as parseAbi27, encodeFunctionData as encodeFunctionData25 } from "viem";
|
|
463
495
|
import { parseAbi as parseAbi28, encodeFunctionData as encodeFunctionData26 } from "viem";
|
|
464
|
-
import { createPublicClient as
|
|
496
|
+
import { createPublicClient as createPublicClient222, http as http222, parseAbi as parseAbi29 } from "viem";
|
|
465
497
|
function pctToTickDelta(pct) {
|
|
466
498
|
return Math.round(Math.log(1 + pct / 100) / Math.log(1.0001));
|
|
467
499
|
}
|
|
@@ -715,15 +747,6 @@ function u256ToF642(v) {
|
|
|
715
747
|
if (v > MAX_U128) return Infinity;
|
|
716
748
|
return Number(v);
|
|
717
749
|
}
|
|
718
|
-
function defaultMarketParams(loanToken = zeroAddress10) {
|
|
719
|
-
return {
|
|
720
|
-
loanToken,
|
|
721
|
-
collateralToken: zeroAddress10,
|
|
722
|
-
oracle: zeroAddress10,
|
|
723
|
-
irm: zeroAddress10,
|
|
724
|
-
lltv: 0n
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
750
|
function decodeMarket(data) {
|
|
728
751
|
if (!data) return null;
|
|
729
752
|
try {
|
|
@@ -956,7 +979,7 @@ function createKittenSwapFarming(entry, rpcUrl) {
|
|
|
956
979
|
const bonusRewardToken = entry.contracts?.["bonus_reward_token"];
|
|
957
980
|
return new KittenSwapFarmingAdapter(entry.name, farmingCenter, eternalFarming, positionManager, rpcUrl, factory, rewardToken, bonusRewardToken);
|
|
958
981
|
}
|
|
959
|
-
var DEFAULT_FEE, swapRouterAbi, quoterAbi, ramsesQuoterAbi, positionManagerAbi, slipstreamMintAbi, UniswapV3Adapter, abi, lbQuoterAbi, UniswapV2Adapter, abi2, algebraQuoterAbi, algebraSingleQuoterAbi, algebraIntegralPmAbi, algebraV2PmAbi, algebraSharedPmAbi, AlgebraV3Adapter, abi3, BalancerV3Adapter, poolAbi, CurveStableSwapAdapter, abi4, abiV2, SolidlyAdapter, thenaPmAbi, thenaRouterAbi, thenaPoolAbi, thenaFactoryAbi, ThenaCLAdapter, _addressDecodeAbi, _symbolDecodeAbi, gaugeManagerAbi, gaugeCLAbi, nfpmAbi, veAbi, voterAbi, HybraGaugeAdapter, abi5, WooFiAdapter, gaugeAbi, veAbi2, voterAbi2, _addressDecodeAbi2, _symbolDecodeAbi2, _boolDecodeAbi, HYPEREVM_TOKENS, CL_TICK_SPACINGS, SolidlyGaugeAdapter, masterchefAbi, MasterChefAdapter, lbRouterAbi, lbFactoryAbi, lbPairAbi, lbRewarderAbi, masterChefAbi, veMoeAbi, lbPairBinAbi, lbQuoterAbi2, erc20Abi2, _addressAbi, _uint256Abi, _boolAbi, _rangeAbi, _binAbi, _uint256ArrayAbi, MerchantMoeLBAdapter, KITTEN_TOKEN, WHYPE_TOKEN, MAX_NONCE_SCAN, HYPEREVM_TOKENS2, farmingCenterAbi, positionManagerAbi2, eternalFarmingAbi, algebraFactoryAbi, _addressDecodeAbi3, nonceCache, KittenSwapFarmingAdapter, DEFAULT_BASE_URL, FALLBACK_BASE_URL, NEST_TOKEN, NEST_DECIMALS, NestOffChainAdapter, POOL_ABI, ERC20_ABI2, INCENTIVES_ABI, REWARDS_CONTROLLER_ABI, POOL_PROVIDER_ABI, ADDRESSES_PROVIDER_ABI, ORACLE_ABI, ERC20_DECIMALS_ABI, AaveV3Adapter, POOL_ABI2, ERC20_ABI22, AaveV2Adapter, ORACLE_ABI2, AaveOracleAdapter, CTOKEN_ABI, BSC_BLOCKS_PER_YEAR, CompoundV2Adapter, COMET_ABI, SECONDS_PER_YEAR, CompoundV3Adapter, EULER_VAULT_ABI, SECONDS_PER_YEAR2, EulerV2Adapter, MORPHO_ABI, META_MORPHO_ABI, ERC4626_ABI, MAX_UINT256, IRM_ABI, SECONDS_PER_YEAR3, MorphoBlueAdapter, BORROWER_OPS_ABI, TROVE_MANAGER_ABI, HINT_HELPERS_ABI, SORTED_TROVES_ABI, FelixCdpAdapter, PRICE_FEED_ABI, FelixOracleAdapter, ERC4626_ABI2, ERC4626VaultAdapter, GENERIC_LST_ABI, GenericLstAdapter, STHYPE_ABI, ERC20_ABI3, StHypeAdapter, KINETIQ_ABI, ORACLE_ABI3, WHYPE, HYPERLEND_ORACLE, KinetiqAdapter, PendleAdapter, GenericYieldAdapter, HLP_ABI, HlpVaultAdapter, GenericDerivativesAdapter, RYSK_ABI, RyskAdapter, GenericOptionsAdapter, ERC721_ABI, ERC721Adapter, DexSpotPrice;
|
|
982
|
+
var DEFAULT_FEE, swapRouterAbi, quoterAbi, ramsesQuoterAbi, positionManagerAbi, slipstreamMintAbi, ramsesMintAbi, UniswapV3Adapter, abi, lbQuoterAbi, UniswapV2Adapter, abi2, algebraQuoterAbi, algebraSingleQuoterAbi, algebraIntegralPmAbi, algebraV2PmAbi, algebraSharedPmAbi, AlgebraV3Adapter, abi3, BalancerV3Adapter, poolAbi, CurveStableSwapAdapter, abi4, abiV2, SolidlyAdapter, thenaPmAbi, thenaRouterAbi, thenaPoolAbi, thenaFactoryAbi, ThenaCLAdapter, _addressDecodeAbi, _symbolDecodeAbi, gaugeManagerAbi, gaugeCLAbi, nfpmAbi, veAbi, voterAbi, HybraGaugeAdapter, abi5, WooFiAdapter, gaugeAbi, veAbi2, voterAbi2, _addressDecodeAbi2, _symbolDecodeAbi2, _boolDecodeAbi, HYPEREVM_TOKENS, CL_TICK_SPACINGS, SolidlyGaugeAdapter, masterchefAbi, MasterChefAdapter, lbRouterAbi, lbFactoryAbi, lbPairAbi, lbRewarderAbi, masterChefAbi, veMoeAbi, lbPairBinAbi, lbQuoterAbi2, erc20Abi2, _addressAbi, _uint256Abi, _boolAbi, _rangeAbi, _binAbi, _uint256ArrayAbi, MerchantMoeLBAdapter, KITTEN_TOKEN, WHYPE_TOKEN, MAX_NONCE_SCAN, HYPEREVM_TOKENS2, farmingCenterAbi, positionManagerAbi2, eternalFarmingAbi, algebraFactoryAbi, _addressDecodeAbi3, nonceCache, KittenSwapFarmingAdapter, DEFAULT_BASE_URL, FALLBACK_BASE_URL, NEST_TOKEN, NEST_DECIMALS, NestOffChainAdapter, POOL_ABI, ERC20_ABI2, INCENTIVES_ABI, REWARDS_CONTROLLER_ABI, POOL_PROVIDER_ABI, ADDRESSES_PROVIDER_ABI, ORACLE_ABI, ERC20_DECIMALS_ABI, AaveV3Adapter, POOL_ABI2, ERC20_ABI22, AaveV2Adapter, ORACLE_ABI2, AaveOracleAdapter, CTOKEN_ABI, NATIVE_CTOKEN_ABI, COMPTROLLER_ABI, NATIVE_SENTINEL, BSC_BLOCKS_PER_YEAR, CompoundV2Adapter, COMET_ABI, SECONDS_PER_YEAR, CompoundV3Adapter, EULER_VAULT_ABI, SECONDS_PER_YEAR2, EulerV2Adapter, MORPHO_ABI, META_MORPHO_ABI, ERC4626_ABI, MAX_UINT256, IRM_ABI, SECONDS_PER_YEAR3, MorphoBlueAdapter, BORROWER_OPS_ABI, TROVE_MANAGER_ABI, HINT_HELPERS_ABI, SORTED_TROVES_ABI, FelixCdpAdapter, PRICE_FEED_ABI, FelixOracleAdapter, ERC4626_ABI2, ERC4626VaultAdapter, GENERIC_LST_ABI, GenericLstAdapter, STHYPE_ABI, ERC20_ABI3, StHypeAdapter, KINETIQ_ABI, ORACLE_ABI3, WHYPE, HYPERLEND_ORACLE, KinetiqAdapter, PendleAdapter, GenericYieldAdapter, HLP_ABI, HlpVaultAdapter, GenericDerivativesAdapter, RYSK_ABI, RyskAdapter, GenericOptionsAdapter, ERC721_ABI, ERC721Adapter, DexSpotPrice;
|
|
960
983
|
var init_dist2 = __esm({
|
|
961
984
|
"../defi-protocols/dist/index.js"() {
|
|
962
985
|
"use strict";
|
|
@@ -1024,6 +1047,10 @@ var init_dist2 = __esm({
|
|
|
1024
1047
|
"struct SlipstreamMintParams { address token0; address token1; int24 tickSpacing; int24 tickLower; int24 tickUpper; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; uint256 deadline; uint160 sqrtPriceX96; }",
|
|
1025
1048
|
"function mint(SlipstreamMintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
|
|
1026
1049
|
]);
|
|
1050
|
+
ramsesMintAbi = parseAbi4([
|
|
1051
|
+
"struct RamsesMintParams { address token0; address token1; int24 tickSpacing; int24 tickLower; int24 tickUpper; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; uint256 deadline; }",
|
|
1052
|
+
"function mint(RamsesMintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
|
|
1053
|
+
]);
|
|
1027
1054
|
UniswapV3Adapter = class {
|
|
1028
1055
|
protocolName;
|
|
1029
1056
|
router;
|
|
@@ -1033,6 +1060,7 @@ var init_dist2 = __esm({
|
|
|
1033
1060
|
fee;
|
|
1034
1061
|
rpcUrl;
|
|
1035
1062
|
useTickSpacingQuoter;
|
|
1063
|
+
clStyle;
|
|
1036
1064
|
constructor(entry, rpcUrl) {
|
|
1037
1065
|
this.protocolName = entry.name;
|
|
1038
1066
|
const router = entry.contracts?.["router"];
|
|
@@ -1045,6 +1073,7 @@ var init_dist2 = __esm({
|
|
|
1045
1073
|
this.factory = entry.contracts?.["factory"];
|
|
1046
1074
|
this.fee = DEFAULT_FEE;
|
|
1047
1075
|
this.rpcUrl = rpcUrl;
|
|
1076
|
+
this.clStyle = entry.cl_style;
|
|
1048
1077
|
this.useTickSpacingQuoter = entry.cl_style === "slipstream" || entry.cl_style === "ramses" || entry.contracts?.["pool_deployer"] !== void 0 || entry.contracts?.["gauge_factory"] !== void 0;
|
|
1049
1078
|
}
|
|
1050
1079
|
name() {
|
|
@@ -1052,7 +1081,15 @@ var init_dist2 = __esm({
|
|
|
1052
1081
|
}
|
|
1053
1082
|
async buildSwap(params) {
|
|
1054
1083
|
const deadline = BigInt(params.deadline ?? 18446744073709551615n);
|
|
1055
|
-
const amountOutMinimum =
|
|
1084
|
+
const amountOutMinimum = params.amount_out_min ?? applyMinSlippage(
|
|
1085
|
+
params.slippage,
|
|
1086
|
+
(await this.quote({
|
|
1087
|
+
protocol: this.protocolName,
|
|
1088
|
+
token_in: params.token_in,
|
|
1089
|
+
token_out: params.token_out,
|
|
1090
|
+
amount_in: params.amount_in
|
|
1091
|
+
})).amount_out
|
|
1092
|
+
);
|
|
1056
1093
|
const data = encodeFunctionData4({
|
|
1057
1094
|
abi: swapRouterAbi,
|
|
1058
1095
|
functionName: "exactInputSingle",
|
|
@@ -1218,31 +1255,37 @@ var init_dist2 = __esm({
|
|
|
1218
1255
|
if (!pm) {
|
|
1219
1256
|
throw new DefiError("CONTRACT_ERROR", "Position manager address not configured");
|
|
1220
1257
|
}
|
|
1221
|
-
const
|
|
1258
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
1259
|
+
const [token0, token1, rawAmount0, rawAmount1] = isAFirst ? [params.token_a, params.token_b, params.amount_a, params.amount_b] : [params.token_b, params.token_a, params.amount_b, params.amount_a];
|
|
1222
1260
|
const amount0 = rawAmount0 === 0n && rawAmount1 > 0n ? 1n : rawAmount0;
|
|
1223
1261
|
const amount1 = rawAmount1 === 0n && rawAmount0 > 0n ? 1n : rawAmount1;
|
|
1262
|
+
const slippage = params.slippage ?? defaultSwapSlippage();
|
|
1263
|
+
const minA = params.amount_a_min ?? applyMinSlippage(slippage, params.amount_a);
|
|
1264
|
+
const minB = params.amount_b_min ?? applyMinSlippage(slippage, params.amount_b);
|
|
1265
|
+
const [amount0Min, amount1Min] = isAFirst ? [minA, minB] : [minB, minA];
|
|
1224
1266
|
let thirdField = this.fee;
|
|
1225
1267
|
let tickLower = -887220;
|
|
1226
1268
|
let tickUpper = 887220;
|
|
1227
1269
|
if (params.pool && this.rpcUrl) {
|
|
1228
1270
|
const poolAbi2 = parseAbi4([
|
|
1229
1271
|
"function fee() view returns (uint24)",
|
|
1230
|
-
"function tickSpacing() view returns (int24)"
|
|
1231
|
-
"function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16, uint16, uint16, bool)"
|
|
1272
|
+
"function tickSpacing() view returns (int24)"
|
|
1232
1273
|
]);
|
|
1233
1274
|
const client = createPublicClient4({ transport: http4(this.rpcUrl) });
|
|
1234
|
-
const [poolFee, poolTs,
|
|
1275
|
+
const [poolFee, poolTs, slot0Raw] = await Promise.all([
|
|
1235
1276
|
client.readContract({ address: params.pool, abi: poolAbi2, functionName: "fee" }).catch(() => null),
|
|
1236
1277
|
client.readContract({ address: params.pool, abi: poolAbi2, functionName: "tickSpacing" }).catch(() => null),
|
|
1237
|
-
client.
|
|
1278
|
+
client.call({ to: params.pool, data: "0x3850c7bd" }).then((r) => r.data ?? null).catch(() => null)
|
|
1238
1279
|
]);
|
|
1239
1280
|
if (this.useTickSpacingQuoter && poolTs !== null) {
|
|
1240
1281
|
thirdField = poolTs;
|
|
1241
1282
|
} else if (poolFee !== null) {
|
|
1242
1283
|
thirdField = poolFee;
|
|
1243
1284
|
}
|
|
1244
|
-
if (params.range_pct !== void 0 &&
|
|
1245
|
-
const
|
|
1285
|
+
if (params.range_pct !== void 0 && slot0Raw && poolTs !== null) {
|
|
1286
|
+
const tickWord = slot0Raw.slice(2 + 64, 2 + 128);
|
|
1287
|
+
const tickU = BigInt("0x" + tickWord);
|
|
1288
|
+
const currentTick = tickU >= 1n << 255n ? Number(tickU - (1n << 256n)) : Number(tickU);
|
|
1246
1289
|
const rangeTicks = Math.floor(params.range_pct * 100);
|
|
1247
1290
|
tickLower = Math.floor((currentTick - rangeTicks) / poolTs) * poolTs;
|
|
1248
1291
|
tickUpper = Math.ceil((currentTick + rangeTicks) / poolTs) * poolTs;
|
|
@@ -1250,7 +1293,25 @@ var init_dist2 = __esm({
|
|
|
1250
1293
|
}
|
|
1251
1294
|
if (params.tick_lower !== void 0) tickLower = params.tick_lower;
|
|
1252
1295
|
if (params.tick_upper !== void 0) tickUpper = params.tick_upper;
|
|
1253
|
-
const data = this.
|
|
1296
|
+
const data = this.clStyle === "ramses" ? encodeFunctionData4({
|
|
1297
|
+
abi: ramsesMintAbi,
|
|
1298
|
+
functionName: "mint",
|
|
1299
|
+
args: [
|
|
1300
|
+
{
|
|
1301
|
+
token0,
|
|
1302
|
+
token1,
|
|
1303
|
+
tickSpacing: thirdField,
|
|
1304
|
+
tickLower,
|
|
1305
|
+
tickUpper,
|
|
1306
|
+
amount0Desired: amount0,
|
|
1307
|
+
amount1Desired: amount1,
|
|
1308
|
+
amount0Min,
|
|
1309
|
+
amount1Min,
|
|
1310
|
+
recipient: params.recipient,
|
|
1311
|
+
deadline: BigInt("18446744073709551615")
|
|
1312
|
+
}
|
|
1313
|
+
]
|
|
1314
|
+
}) : this.useTickSpacingQuoter ? encodeFunctionData4({
|
|
1254
1315
|
abi: slipstreamMintAbi,
|
|
1255
1316
|
functionName: "mint",
|
|
1256
1317
|
args: [
|
|
@@ -1262,8 +1323,8 @@ var init_dist2 = __esm({
|
|
|
1262
1323
|
tickUpper,
|
|
1263
1324
|
amount0Desired: amount0,
|
|
1264
1325
|
amount1Desired: amount1,
|
|
1265
|
-
amount0Min
|
|
1266
|
-
amount1Min
|
|
1326
|
+
amount0Min,
|
|
1327
|
+
amount1Min,
|
|
1267
1328
|
recipient: params.recipient,
|
|
1268
1329
|
deadline: BigInt("18446744073709551615"),
|
|
1269
1330
|
sqrtPriceX96: 0n
|
|
@@ -1281,8 +1342,8 @@ var init_dist2 = __esm({
|
|
|
1281
1342
|
tickUpper,
|
|
1282
1343
|
amount0Desired: amount0,
|
|
1283
1344
|
amount1Desired: amount1,
|
|
1284
|
-
amount0Min
|
|
1285
|
-
amount1Min
|
|
1345
|
+
amount0Min,
|
|
1346
|
+
amount1Min,
|
|
1286
1347
|
recipient: params.recipient,
|
|
1287
1348
|
deadline: BigInt("18446744073709551615")
|
|
1288
1349
|
}
|
|
@@ -1316,10 +1377,17 @@ var init_dist2 = __esm({
|
|
|
1316
1377
|
const liquidity = params.liquidity;
|
|
1317
1378
|
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
1318
1379
|
const deadline = BigInt("18446744073709551615");
|
|
1380
|
+
if (params.amount_a_min === void 0 || params.amount_b_min === void 0) {
|
|
1381
|
+
throw DefiError.invalidParam(
|
|
1382
|
+
`[${this.protocolName}] V3 remove_liquidity requires amount_a_min and amount_b_min for slippage protection (SSOT 7.3). Caller must compute expected token outputs from the pool state and apply a slippage tolerance before calling buildRemoveLiquidity.`
|
|
1383
|
+
);
|
|
1384
|
+
}
|
|
1385
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
1386
|
+
const [amount0Min, amount1Min] = isAFirst ? [params.amount_a_min, params.amount_b_min] : [params.amount_b_min, params.amount_a_min];
|
|
1319
1387
|
const decreaseData = encodeFunctionData4({
|
|
1320
1388
|
abi: positionManagerAbi,
|
|
1321
1389
|
functionName: "decreaseLiquidity",
|
|
1322
|
-
args: [{ tokenId, liquidity, amount0Min
|
|
1390
|
+
args: [{ tokenId, liquidity, amount0Min, amount1Min, deadline }]
|
|
1323
1391
|
});
|
|
1324
1392
|
const collectData = encodeFunctionData4({
|
|
1325
1393
|
abi: positionManagerAbi,
|
|
@@ -1617,7 +1685,13 @@ var init_dist2 = __esm({
|
|
|
1617
1685
|
to: this.router,
|
|
1618
1686
|
data,
|
|
1619
1687
|
value: 0n,
|
|
1620
|
-
gas_estimate: 25e4
|
|
1688
|
+
gas_estimate: 25e4,
|
|
1689
|
+
// Same LP-approval requirement as Solidly forks: the V2 router pulls the
|
|
1690
|
+
// LP pair token via transferFrom and reverts at gas ~42k otherwise.
|
|
1691
|
+
// Caller passes --pool so we can target the pair contract for approval.
|
|
1692
|
+
// Discovered live on Uniswap V2 Monad WMON/AUSD 2026-05-08
|
|
1693
|
+
// (failed tx 0x2268659a…09ae → recovered with this fix).
|
|
1694
|
+
...params.pool ? { approvals: [{ token: params.pool, spender: this.router, amount: params.liquidity }] } : {}
|
|
1621
1695
|
};
|
|
1622
1696
|
}
|
|
1623
1697
|
};
|
|
@@ -1672,7 +1746,15 @@ var init_dist2 = __esm({
|
|
|
1672
1746
|
}
|
|
1673
1747
|
async buildSwap(params) {
|
|
1674
1748
|
const deadline = BigInt(params.deadline ?? 18446744073709551615n);
|
|
1675
|
-
const amountOutMinimum =
|
|
1749
|
+
const amountOutMinimum = params.amount_out_min ?? applyMinSlippage(
|
|
1750
|
+
params.slippage,
|
|
1751
|
+
(await this.quote({
|
|
1752
|
+
protocol: this.protocolName,
|
|
1753
|
+
token_in: params.token_in,
|
|
1754
|
+
token_out: params.token_out,
|
|
1755
|
+
amount_in: params.amount_in
|
|
1756
|
+
})).amount_out
|
|
1757
|
+
);
|
|
1676
1758
|
const data = encodeFunctionData32({
|
|
1677
1759
|
abi: abi2,
|
|
1678
1760
|
functionName: "exactInputSingle",
|
|
@@ -1786,7 +1868,12 @@ var init_dist2 = __esm({
|
|
|
1786
1868
|
if (!pm) {
|
|
1787
1869
|
throw new DefiError("CONTRACT_ERROR", "Position manager address not configured");
|
|
1788
1870
|
}
|
|
1789
|
-
const
|
|
1871
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
1872
|
+
const [token0, token1, rawAmount0, rawAmount1] = isAFirst ? [params.token_a, params.token_b, params.amount_a, params.amount_b] : [params.token_b, params.token_a, params.amount_b, params.amount_a];
|
|
1873
|
+
const slippage = params.slippage ?? defaultSwapSlippage();
|
|
1874
|
+
const minA = params.amount_a_min ?? applyMinSlippage(slippage, params.amount_a);
|
|
1875
|
+
const minB = params.amount_b_min ?? applyMinSlippage(slippage, params.amount_b);
|
|
1876
|
+
const [amount0Min, amount1Min] = isAFirst ? [minA, minB] : [minB, minA];
|
|
1790
1877
|
let tickLower = params.tick_lower ?? -887220;
|
|
1791
1878
|
let tickUpper = params.tick_upper ?? 887220;
|
|
1792
1879
|
const isSingleSide = rawAmount0 === 0n || rawAmount1 === 0n;
|
|
@@ -1823,11 +1910,11 @@ var init_dist2 = __esm({
|
|
|
1823
1910
|
const data = this.useSingleQuoter ? encodeFunctionData32({
|
|
1824
1911
|
abi: algebraV2PmAbi,
|
|
1825
1912
|
functionName: "mint",
|
|
1826
|
-
args: [{ token0, token1, tickLower, tickUpper, amount0Desired: amount0, amount1Desired: amount1, amount0Min
|
|
1913
|
+
args: [{ token0, token1, tickLower, tickUpper, amount0Desired: amount0, amount1Desired: amount1, amount0Min, amount1Min, recipient: params.recipient, deadline: BigInt("18446744073709551615") }]
|
|
1827
1914
|
}) : encodeFunctionData32({
|
|
1828
1915
|
abi: algebraIntegralPmAbi,
|
|
1829
1916
|
functionName: "mint",
|
|
1830
|
-
args: [{ token0, token1, deployer: zeroAddress, tickLower, tickUpper, amount0Desired: amount0, amount1Desired: amount1, amount0Min
|
|
1917
|
+
args: [{ token0, token1, deployer: zeroAddress, tickLower, tickUpper, amount0Desired: amount0, amount1Desired: amount1, amount0Min, amount1Min, recipient: params.recipient, deadline: BigInt("18446744073709551615") }]
|
|
1831
1918
|
});
|
|
1832
1919
|
const approvals = [];
|
|
1833
1920
|
if (amount0 > 0n) approvals.push({ token: token0, spender: pm, amount: amount0 });
|
|
@@ -1845,12 +1932,19 @@ var init_dist2 = __esm({
|
|
|
1845
1932
|
const pm = this.positionManager;
|
|
1846
1933
|
if (!pm) throw DefiError.contractError(`[${this.protocolName}] Missing 'position_manager'`);
|
|
1847
1934
|
if (!params.token_id) throw DefiError.invalidParam(`[${this.protocolName}] V3 remove_liquidity requires --token-id`);
|
|
1935
|
+
if (params.amount_a_min === void 0 || params.amount_b_min === void 0) {
|
|
1936
|
+
throw DefiError.invalidParam(
|
|
1937
|
+
`[${this.protocolName}] remove_liquidity requires amount_a_min and amount_b_min for slippage protection (SSOT 7.3). Compute expected outputs from positions(tokenId) + pool.globalState off-chain and apply tolerance.`
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
1941
|
+
const [amount0Min, amount1Min] = isAFirst ? [params.amount_a_min, params.amount_b_min] : [params.amount_b_min, params.amount_a_min];
|
|
1848
1942
|
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
1849
1943
|
const deadline = BigInt("18446744073709551615");
|
|
1850
1944
|
const decreaseData = encodeFunctionData32({
|
|
1851
1945
|
abi: algebraSharedPmAbi,
|
|
1852
1946
|
functionName: "decreaseLiquidity",
|
|
1853
|
-
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min
|
|
1947
|
+
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min, amount1Min, deadline }]
|
|
1854
1948
|
});
|
|
1855
1949
|
const collectData = encodeFunctionData32({
|
|
1856
1950
|
abi: algebraSharedPmAbi,
|
|
@@ -1877,6 +1971,7 @@ var init_dist2 = __esm({
|
|
|
1877
1971
|
BalancerV3Adapter = class {
|
|
1878
1972
|
protocolName;
|
|
1879
1973
|
router;
|
|
1974
|
+
pool;
|
|
1880
1975
|
constructor(entry, _rpcUrl) {
|
|
1881
1976
|
this.protocolName = entry.name;
|
|
1882
1977
|
const router = entry.contracts?.["router"];
|
|
@@ -1884,19 +1979,29 @@ var init_dist2 = __esm({
|
|
|
1884
1979
|
throw new DefiError("CONTRACT_ERROR", "Missing 'router' contract");
|
|
1885
1980
|
}
|
|
1886
1981
|
this.router = router;
|
|
1982
|
+
this.pool = entry.contracts?.["pool"];
|
|
1887
1983
|
}
|
|
1888
1984
|
name() {
|
|
1889
1985
|
return this.protocolName;
|
|
1890
1986
|
}
|
|
1891
1987
|
async buildSwap(params) {
|
|
1892
|
-
|
|
1988
|
+
if (params.amount_out_min === void 0) {
|
|
1989
|
+
throw DefiError.invalidParam(
|
|
1990
|
+
`[${this.protocolName}] buildSwap requires amount_out_min for slippage protection (SSOT 7.3) \u2014 Balancer V3 has no on-chain quoter wired in this adapter. Compute the floor off-chain (e.g. via the Vault's static-call simulation) and pass it explicitly.`
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
if (!this.pool) {
|
|
1994
|
+
throw DefiError.invalidParam(
|
|
1995
|
+
`[${this.protocolName}] buildSwap requires a pool address. Register the pool under [protocol.contracts] as \`pool = "0x..."\` in the protocol's TOML config. Multi-pool routing is intentionally not implemented in this adapter \u2014 for that, quote against the Balancer V3 BatchRouter off-chain and route via a different surface.`
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
const minAmountOut = params.amount_out_min;
|
|
1893
1999
|
const deadline = BigInt(params.deadline ?? 18446744073709551615n);
|
|
1894
2000
|
const data = encodeFunctionData42({
|
|
1895
2001
|
abi: abi3,
|
|
1896
2002
|
functionName: "swapSingleTokenExactIn",
|
|
1897
2003
|
args: [
|
|
1898
|
-
|
|
1899
|
-
// TODO: resolve pool from registry
|
|
2004
|
+
this.pool,
|
|
1900
2005
|
params.token_in,
|
|
1901
2006
|
params.token_out,
|
|
1902
2007
|
params.amount_in,
|
|
@@ -1907,7 +2012,7 @@ var init_dist2 = __esm({
|
|
|
1907
2012
|
]
|
|
1908
2013
|
});
|
|
1909
2014
|
return {
|
|
1910
|
-
description: `[${this.protocolName}] Swap ${params.amount_in} via Balancer V3`,
|
|
2015
|
+
description: `[${this.protocolName}] Swap ${params.amount_in} via Balancer V3 pool ${this.pool}`,
|
|
1911
2016
|
to: this.router,
|
|
1912
2017
|
data,
|
|
1913
2018
|
value: 0n,
|
|
@@ -1927,8 +2032,8 @@ var init_dist2 = __esm({
|
|
|
1927
2032
|
poolAbi = parseAbi5([
|
|
1928
2033
|
"function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256)",
|
|
1929
2034
|
"function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256)",
|
|
1930
|
-
"function add_liquidity(uint256[
|
|
1931
|
-
"function remove_liquidity(uint256 amount, uint256[
|
|
2035
|
+
"function add_liquidity(uint256[] amounts, uint256 min_mint_amount) external returns (uint256)",
|
|
2036
|
+
"function remove_liquidity(uint256 amount, uint256[] min_amounts) external returns (uint256[])"
|
|
1932
2037
|
]);
|
|
1933
2038
|
CurveStableSwapAdapter = class {
|
|
1934
2039
|
protocolName;
|
|
@@ -1963,17 +2068,22 @@ var init_dist2 = __esm({
|
|
|
1963
2068
|
throw DefiError.unsupported(`[${this.protocolName}] quote requires RPC connection`);
|
|
1964
2069
|
}
|
|
1965
2070
|
async buildAddLiquidity(params) {
|
|
2071
|
+
if (!params.pool) {
|
|
2072
|
+
throw DefiError.invalidParam(
|
|
2073
|
+
`[${this.protocolName}] Curve add_liquidity needs --pool <address>. The router does not proxy this call; it lives on the pool itself.`
|
|
2074
|
+
);
|
|
2075
|
+
}
|
|
1966
2076
|
const data = encodeFunctionData5({
|
|
1967
2077
|
abi: poolAbi,
|
|
1968
2078
|
functionName: "add_liquidity",
|
|
1969
2079
|
args: [[params.amount_a, params.amount_b], 0n]
|
|
1970
2080
|
});
|
|
1971
2081
|
const approvals = [];
|
|
1972
|
-
if (params.amount_a > 0n) approvals.push({ token: params.token_a, spender:
|
|
1973
|
-
if (params.amount_b > 0n) approvals.push({ token: params.token_b, spender:
|
|
2082
|
+
if (params.amount_a > 0n) approvals.push({ token: params.token_a, spender: params.pool, amount: params.amount_a });
|
|
2083
|
+
if (params.amount_b > 0n) approvals.push({ token: params.token_b, spender: params.pool, amount: params.amount_b });
|
|
1974
2084
|
return {
|
|
1975
|
-
description: `[${this.protocolName}] Curve add liquidity`,
|
|
1976
|
-
to:
|
|
2085
|
+
description: `[${this.protocolName}] Curve add liquidity to ${params.pool}`,
|
|
2086
|
+
to: params.pool,
|
|
1977
2087
|
data,
|
|
1978
2088
|
value: 0n,
|
|
1979
2089
|
gas_estimate: 4e5,
|
|
@@ -1981,14 +2091,19 @@ var init_dist2 = __esm({
|
|
|
1981
2091
|
};
|
|
1982
2092
|
}
|
|
1983
2093
|
async buildRemoveLiquidity(params) {
|
|
2094
|
+
if (!params.pool) {
|
|
2095
|
+
throw DefiError.invalidParam(
|
|
2096
|
+
`[${this.protocolName}] Curve remove_liquidity needs --pool <address>. The router does not proxy this call.`
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
1984
2099
|
const data = encodeFunctionData5({
|
|
1985
2100
|
abi: poolAbi,
|
|
1986
2101
|
functionName: "remove_liquidity",
|
|
1987
2102
|
args: [params.liquidity, [0n, 0n]]
|
|
1988
2103
|
});
|
|
1989
2104
|
return {
|
|
1990
|
-
description: `[${this.protocolName}] Curve remove liquidity`,
|
|
1991
|
-
to:
|
|
2105
|
+
description: `[${this.protocolName}] Curve remove liquidity from ${params.pool}`,
|
|
2106
|
+
to: params.pool,
|
|
1992
2107
|
data,
|
|
1993
2108
|
value: 0n,
|
|
1994
2109
|
gas_estimate: 35e4
|
|
@@ -2154,7 +2269,12 @@ var init_dist2 = __esm({
|
|
|
2154
2269
|
to: this.router,
|
|
2155
2270
|
data,
|
|
2156
2271
|
value: 0n,
|
|
2157
|
-
gas_estimate: 3e5
|
|
2272
|
+
gas_estimate: 3e5,
|
|
2273
|
+
// The router pulls the LP token via transferFrom; without this approval
|
|
2274
|
+
// the tx reverts at gas ~42k. Caller must pass --pool so we know which
|
|
2275
|
+
// LP pair to approve. Discovered live on Aerodrome USDC/USDT 2026-05-08
|
|
2276
|
+
// (failed tx 0x6d052e0a…3298 → recovered with manual approve 0xa126fc3a).
|
|
2277
|
+
...params.pool ? { approvals: [{ token: params.pool, spender: this.router, amount: params.liquidity }] } : {}
|
|
2158
2278
|
};
|
|
2159
2279
|
}
|
|
2160
2280
|
};
|
|
@@ -2201,6 +2321,11 @@ var init_dist2 = __esm({
|
|
|
2201
2321
|
return this.protocolName;
|
|
2202
2322
|
}
|
|
2203
2323
|
async buildSwap(params) {
|
|
2324
|
+
if (params.amount_out_min === void 0) {
|
|
2325
|
+
throw DefiError.invalidParam(
|
|
2326
|
+
`[${this.protocolName}] buildSwap requires amount_out_min for slippage protection (SSOT 7.3) \u2014 Thena CL has no on-chain quoter. Compute the floor off-chain (e.g. via the router's static-call simulation) and pass it explicitly.`
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2204
2329
|
const data = encodeFunctionData7({
|
|
2205
2330
|
abi: thenaRouterAbi,
|
|
2206
2331
|
functionName: "exactInputSingle",
|
|
@@ -2211,7 +2336,7 @@ var init_dist2 = __esm({
|
|
|
2211
2336
|
recipient: params.recipient,
|
|
2212
2337
|
deadline: BigInt(params.deadline ?? 18446744073709551615n),
|
|
2213
2338
|
amountIn: params.amount_in,
|
|
2214
|
-
amountOutMinimum:
|
|
2339
|
+
amountOutMinimum: params.amount_out_min,
|
|
2215
2340
|
sqrtPriceLimitX96: 0n
|
|
2216
2341
|
}]
|
|
2217
2342
|
});
|
|
@@ -2231,7 +2356,12 @@ var init_dist2 = __esm({
|
|
|
2231
2356
|
const pm = this.positionManager;
|
|
2232
2357
|
if (!pm) throw new DefiError("CONTRACT_ERROR", "Position manager not configured");
|
|
2233
2358
|
if (!this.rpcUrl) throw DefiError.rpcError("RPC URL required");
|
|
2234
|
-
const
|
|
2359
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
2360
|
+
const [token0, token1, rawAmount0, rawAmount1] = isAFirst ? [params.token_a, params.token_b, params.amount_a, params.amount_b] : [params.token_b, params.token_a, params.amount_b, params.amount_a];
|
|
2361
|
+
const slippage = params.slippage ?? defaultSwapSlippage();
|
|
2362
|
+
const minA = params.amount_a_min ?? applyMinSlippage(slippage, params.amount_a);
|
|
2363
|
+
const minB = params.amount_b_min ?? applyMinSlippage(slippage, params.amount_b);
|
|
2364
|
+
const [amount0Min, amount1Min] = isAFirst ? [minA, minB] : [minB, minA];
|
|
2235
2365
|
const client = createPublicClient42({ transport: http42(this.rpcUrl) });
|
|
2236
2366
|
const poolAddr = params.pool;
|
|
2237
2367
|
let tickSpacing = this.defaultTickSpacing;
|
|
@@ -2246,7 +2376,7 @@ var init_dist2 = __esm({
|
|
|
2246
2376
|
functionName: "getPool",
|
|
2247
2377
|
args: [token0, token1, tickSpacing]
|
|
2248
2378
|
});
|
|
2249
|
-
if (pool ===
|
|
2379
|
+
if (pool === zeroAddress2) throw new DefiError("CONTRACT_ERROR", "Pool not found");
|
|
2250
2380
|
}
|
|
2251
2381
|
if (pool) {
|
|
2252
2382
|
const [slot0, ts] = await Promise.all([
|
|
@@ -2289,8 +2419,8 @@ var init_dist2 = __esm({
|
|
|
2289
2419
|
tickUpper,
|
|
2290
2420
|
amount0Desired: rawAmount0,
|
|
2291
2421
|
amount1Desired: rawAmount1,
|
|
2292
|
-
amount0Min
|
|
2293
|
-
amount1Min
|
|
2422
|
+
amount0Min,
|
|
2423
|
+
amount1Min,
|
|
2294
2424
|
recipient: params.recipient,
|
|
2295
2425
|
deadline: BigInt("18446744073709551615"),
|
|
2296
2426
|
sqrtPriceX96: 0n
|
|
@@ -2312,12 +2442,19 @@ var init_dist2 = __esm({
|
|
|
2312
2442
|
const pm = this.positionManager;
|
|
2313
2443
|
if (!pm) throw DefiError.contractError(`[${this.protocolName}] Missing 'position_manager'`);
|
|
2314
2444
|
if (!params.token_id) throw DefiError.invalidParam(`[${this.protocolName}] V3 remove_liquidity requires --token-id`);
|
|
2445
|
+
if (params.amount_a_min === void 0 || params.amount_b_min === void 0) {
|
|
2446
|
+
throw DefiError.invalidParam(
|
|
2447
|
+
`[${this.protocolName}] remove_liquidity requires amount_a_min and amount_b_min for slippage protection (SSOT 7.3). Compute expected outputs from positions(tokenId) + pool.slot0 off-chain and apply tolerance.`
|
|
2448
|
+
);
|
|
2449
|
+
}
|
|
2450
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
2451
|
+
const [amount0Min, amount1Min] = isAFirst ? [params.amount_a_min, params.amount_b_min] : [params.amount_b_min, params.amount_a_min];
|
|
2315
2452
|
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
2316
2453
|
const deadline = BigInt("18446744073709551615");
|
|
2317
2454
|
const decreaseData = encodeFunctionData7({
|
|
2318
2455
|
abi: thenaPmAbi,
|
|
2319
2456
|
functionName: "decreaseLiquidity",
|
|
2320
|
-
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min
|
|
2457
|
+
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min, amount1Min, deadline }]
|
|
2321
2458
|
});
|
|
2322
2459
|
const collectData = encodeFunctionData7({
|
|
2323
2460
|
abi: thenaPmAbi,
|
|
@@ -2384,8 +2521,8 @@ var init_dist2 = __esm({
|
|
|
2384
2521
|
const ve = entry.contracts?.["ve_token"];
|
|
2385
2522
|
if (!ve) throw new DefiError("CONTRACT_ERROR", "Missing 've_token' contract");
|
|
2386
2523
|
this.veToken = ve;
|
|
2387
|
-
this.voter = entry.contracts?.["voter"] ??
|
|
2388
|
-
this.positionManager = entry.contracts?.["position_manager"] ??
|
|
2524
|
+
this.voter = entry.contracts?.["voter"] ?? zeroAddress3;
|
|
2525
|
+
this.positionManager = entry.contracts?.["position_manager"] ?? zeroAddress3;
|
|
2389
2526
|
this.poolFactory = entry.contracts?.["pool_factory"];
|
|
2390
2527
|
this.rpcUrl = rpcUrl;
|
|
2391
2528
|
}
|
|
@@ -2427,7 +2564,7 @@ var init_dist2 = __esm({
|
|
|
2427
2564
|
]);
|
|
2428
2565
|
}
|
|
2429
2566
|
const poolAddressResults = await multicallRead(this.rpcUrl, poolAddressCalls);
|
|
2430
|
-
const pools = poolAddressResults.map((r) => decodeAddress(r)).filter((a) => a !== null && a !==
|
|
2567
|
+
const pools = poolAddressResults.map((r) => decodeAddress(r)).filter((a) => a !== null && a !== zeroAddress3);
|
|
2431
2568
|
if (pools.length === 0) return [];
|
|
2432
2569
|
const gaugeCalls = pools.map((pool) => [
|
|
2433
2570
|
this.gaugeManager,
|
|
@@ -2437,7 +2574,7 @@ var init_dist2 = __esm({
|
|
|
2437
2574
|
const gaugedPools = [];
|
|
2438
2575
|
for (let i = 0; i < pools.length; i++) {
|
|
2439
2576
|
const gauge = decodeAddress(gaugeResults[i] ?? null);
|
|
2440
|
-
if (gauge && gauge !==
|
|
2577
|
+
if (gauge && gauge !== zeroAddress3) {
|
|
2441
2578
|
gaugedPools.push({ pool: pools[i], gauge });
|
|
2442
2579
|
}
|
|
2443
2580
|
}
|
|
@@ -2452,8 +2589,8 @@ var init_dist2 = __esm({
|
|
|
2452
2589
|
for (let i = 0; i < gaugedPools.length; i++) {
|
|
2453
2590
|
const t0 = decodeAddress(tokenResults[i * 2] ?? null);
|
|
2454
2591
|
const t1 = decodeAddress(tokenResults[i * 2 + 1] ?? null);
|
|
2455
|
-
if (t0 && t0 !==
|
|
2456
|
-
if (t1 && t1 !==
|
|
2592
|
+
if (t0 && t0 !== zeroAddress3) tokenAddrs.add(t0);
|
|
2593
|
+
if (t1 && t1 !== zeroAddress3) tokenAddrs.add(t1);
|
|
2457
2594
|
}
|
|
2458
2595
|
const uniqueTokens = Array.from(tokenAddrs);
|
|
2459
2596
|
const symbolCalls = uniqueTokens.map((t) => [
|
|
@@ -2492,7 +2629,7 @@ var init_dist2 = __esm({
|
|
|
2492
2629
|
functionName: "gauges",
|
|
2493
2630
|
args: [pool]
|
|
2494
2631
|
});
|
|
2495
|
-
if (gauge ===
|
|
2632
|
+
if (gauge === zeroAddress3) throw new DefiError("CONTRACT_ERROR", `No gauge for pool ${pool}`);
|
|
2496
2633
|
return gauge;
|
|
2497
2634
|
}
|
|
2498
2635
|
// ─── CL Gauge: NFT Deposit/Withdraw ──────────────────────────
|
|
@@ -2654,7 +2791,7 @@ var init_dist2 = __esm({
|
|
|
2654
2791
|
params.amount_in,
|
|
2655
2792
|
minToAmount,
|
|
2656
2793
|
params.recipient,
|
|
2657
|
-
|
|
2794
|
+
zeroAddress4
|
|
2658
2795
|
]
|
|
2659
2796
|
});
|
|
2660
2797
|
return {
|
|
@@ -2879,7 +3016,7 @@ var init_dist2 = __esm({
|
|
|
2879
3016
|
poolCalls.push([this.voter, encodeFunctionData10({ abi: voterPoolsAbi, functionName: "pools", args: [BigInt(i)] })]);
|
|
2880
3017
|
}
|
|
2881
3018
|
const poolResults = await multicallRead(this.rpcUrl, poolCalls);
|
|
2882
|
-
pairs = poolResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !==
|
|
3019
|
+
pairs = poolResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !== zeroAddress5);
|
|
2883
3020
|
} else {
|
|
2884
3021
|
const v2FactoryAbi = parseAbi10(["function allPairsLength() view returns (uint256)", "function allPairs(uint256) view returns (address)"]);
|
|
2885
3022
|
const solidlyFactoryAbi = parseAbi10(["function allPoolsLength() view returns (uint256)", "function allPools(uint256) view returns (address)"]);
|
|
@@ -2904,17 +3041,17 @@ var init_dist2 = __esm({
|
|
|
2904
3041
|
const pairCalls = [];
|
|
2905
3042
|
for (let i = startIdx; i < count; i++) pairCalls.push([this.v2Factory, encodeFunctionData10({ abi: fetchAbi, functionName: fetchFn, args: [BigInt(i)] })]);
|
|
2906
3043
|
const pairResults = await multicallRead(this.rpcUrl, pairCalls);
|
|
2907
|
-
pairs = pairResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !==
|
|
3044
|
+
pairs = pairResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !== zeroAddress5);
|
|
2908
3045
|
}
|
|
2909
3046
|
if (pairs.length === 0) return;
|
|
2910
3047
|
const gaugeCalls = pairs.map((p) => [this.voter, encodeFunctionData10({ abi: gaugeForPoolAbi, functionName: "gaugeForPool", args: [p] })]);
|
|
2911
3048
|
let gaugeResults = await multicallRead(this.rpcUrl, gaugeCalls);
|
|
2912
|
-
const allNull = gaugeResults.every((r) => !r || decodeAddress2(r) ===
|
|
3049
|
+
const allNull = gaugeResults.every((r) => !r || decodeAddress2(r) === zeroAddress5 || decodeAddress2(r) === null);
|
|
2913
3050
|
if (allNull) {
|
|
2914
3051
|
const fb1 = pairs.map((p) => [this.voter, encodeFunctionData10({ abi: poolToGaugeAbi, functionName: "poolToGauge", args: [p] })]);
|
|
2915
3052
|
gaugeResults = await multicallRead(this.rpcUrl, fb1);
|
|
2916
3053
|
}
|
|
2917
|
-
const allNull2 = gaugeResults.every((r) => !r || decodeAddress2(r) ===
|
|
3054
|
+
const allNull2 = gaugeResults.every((r) => !r || decodeAddress2(r) === zeroAddress5 || decodeAddress2(r) === null);
|
|
2918
3055
|
if (allNull2) {
|
|
2919
3056
|
const fb2 = pairs.map((p) => [this.voter, encodeFunctionData10({ abi: gaugesAbi, functionName: "gauges", args: [p] })]);
|
|
2920
3057
|
gaugeResults = await multicallRead(this.rpcUrl, fb2);
|
|
@@ -2922,7 +3059,7 @@ var init_dist2 = __esm({
|
|
|
2922
3059
|
const gaugedPairs = [];
|
|
2923
3060
|
for (let i = 0; i < pairs.length; i++) {
|
|
2924
3061
|
const gauge = decodeAddress2(gaugeResults[i] ?? null);
|
|
2925
|
-
if (gauge && gauge !==
|
|
3062
|
+
if (gauge && gauge !== zeroAddress5) {
|
|
2926
3063
|
gaugedPairs.push({ pair: pairs[i], gauge });
|
|
2927
3064
|
}
|
|
2928
3065
|
}
|
|
@@ -2938,8 +3075,8 @@ var init_dist2 = __esm({
|
|
|
2938
3075
|
for (let i = 0; i < gaugedPairs.length; i++) {
|
|
2939
3076
|
const t0 = decodeAddress2(metaResults[i * 3] ?? null);
|
|
2940
3077
|
const t1 = decodeAddress2(metaResults[i * 3 + 1] ?? null);
|
|
2941
|
-
if (t0 && t0 !==
|
|
2942
|
-
if (t1 && t1 !==
|
|
3078
|
+
if (t0 && t0 !== zeroAddress5) tokenAddrs.add(t0);
|
|
3079
|
+
if (t1 && t1 !== zeroAddress5) tokenAddrs.add(t1);
|
|
2943
3080
|
}
|
|
2944
3081
|
const uniqueTokens = Array.from(tokenAddrs);
|
|
2945
3082
|
const symbolCalls = uniqueTokens.map((t) => [
|
|
@@ -3029,7 +3166,7 @@ var init_dist2 = __esm({
|
|
|
3029
3166
|
const candidatePools = [];
|
|
3030
3167
|
for (let i = 0; i < getPoolCalls.length; i++) {
|
|
3031
3168
|
const pool = decodeAddress2(getPoolResults[i] ?? null);
|
|
3032
|
-
if (pool && pool !==
|
|
3169
|
+
if (pool && pool !== zeroAddress5) {
|
|
3033
3170
|
const { pairIdx, tickSpacing } = callMeta[i];
|
|
3034
3171
|
const [tokenA, tokenB] = pairs[pairIdx];
|
|
3035
3172
|
candidatePools.push({ pool, tokenA, tokenB, tickSpacing });
|
|
@@ -3041,7 +3178,7 @@ var init_dist2 = __esm({
|
|
|
3041
3178
|
encodeFunctionData10({ abi: gaugeForPoolAbi, functionName: "gaugeForPool", args: [pool] })
|
|
3042
3179
|
]);
|
|
3043
3180
|
let gaugeResults = await multicallRead(this.rpcUrl, gaugeCalls);
|
|
3044
|
-
const allNull = gaugeResults.every((r) => !r || decodeAddress2(r) ===
|
|
3181
|
+
const allNull = gaugeResults.every((r) => !r || decodeAddress2(r) === zeroAddress5 || decodeAddress2(r) === null);
|
|
3045
3182
|
if (allNull) {
|
|
3046
3183
|
const fallbackCalls = candidatePools.map(({ pool }) => [
|
|
3047
3184
|
this.voter,
|
|
@@ -3052,7 +3189,7 @@ var init_dist2 = __esm({
|
|
|
3052
3189
|
const gaugedCL = [];
|
|
3053
3190
|
for (let i = 0; i < candidatePools.length; i++) {
|
|
3054
3191
|
const gauge = decodeAddress2(gaugeResults[i] ?? null);
|
|
3055
|
-
if (gauge && gauge !==
|
|
3192
|
+
if (gauge && gauge !== zeroAddress5) {
|
|
3056
3193
|
gaugedCL.push({ ...candidatePools[i], gauge });
|
|
3057
3194
|
}
|
|
3058
3195
|
}
|
|
@@ -3082,8 +3219,8 @@ var init_dist2 = __esm({
|
|
|
3082
3219
|
const { pool, gauge, tokenA, tokenB, tickSpacing } = gaugedCL[i];
|
|
3083
3220
|
const rawT0 = decodeAddress2(poolTokenResults[i * 2] ?? null);
|
|
3084
3221
|
const rawT1 = decodeAddress2(poolTokenResults[i * 2 + 1] ?? null);
|
|
3085
|
-
const t0 = rawT0 && rawT0 !==
|
|
3086
|
-
const t1 = rawT1 && rawT1 !==
|
|
3222
|
+
const t0 = rawT0 && rawT0 !== zeroAddress5 ? rawT0 : tokenA;
|
|
3223
|
+
const t1 = rawT1 && rawT1 !== zeroAddress5 ? rawT1 : tokenB;
|
|
3087
3224
|
out.push({
|
|
3088
3225
|
pool,
|
|
3089
3226
|
gauge,
|
|
@@ -3185,7 +3322,7 @@ var init_dist2 = __esm({
|
|
|
3185
3322
|
functionName: fn,
|
|
3186
3323
|
args: [pool]
|
|
3187
3324
|
});
|
|
3188
|
-
if (gauge !==
|
|
3325
|
+
if (gauge !== zeroAddress5) return gauge;
|
|
3189
3326
|
} catch {
|
|
3190
3327
|
}
|
|
3191
3328
|
}
|
|
@@ -3239,7 +3376,7 @@ var init_dist2 = __esm({
|
|
|
3239
3376
|
abi: gaugeAbi,
|
|
3240
3377
|
functionName: "rewardToken"
|
|
3241
3378
|
});
|
|
3242
|
-
if (rt !==
|
|
3379
|
+
if (rt !== zeroAddress5) return { tokens: [rt], multiToken: false };
|
|
3243
3380
|
} catch {
|
|
3244
3381
|
}
|
|
3245
3382
|
return { tokens: [], multiToken: false };
|
|
@@ -3249,7 +3386,7 @@ var init_dist2 = __esm({
|
|
|
3249
3386
|
const data = encodeFunctionData10({
|
|
3250
3387
|
abi: gaugeAbi,
|
|
3251
3388
|
functionName: "getReward",
|
|
3252
|
-
args: [account ??
|
|
3389
|
+
args: [account ?? zeroAddress5]
|
|
3253
3390
|
});
|
|
3254
3391
|
return { description: `[${this.protocolName}] Claim gauge rewards`, to: gauge, data, value: 0n, gas_estimate: 2e5 };
|
|
3255
3392
|
}
|
|
@@ -3473,7 +3610,7 @@ var init_dist2 = __esm({
|
|
|
3473
3610
|
functionName: "earned",
|
|
3474
3611
|
args: [user]
|
|
3475
3612
|
});
|
|
3476
|
-
results.push({ token:
|
|
3613
|
+
results.push({ token: zeroAddress5, symbol: "unknown", amount: earned });
|
|
3477
3614
|
} catch {
|
|
3478
3615
|
}
|
|
3479
3616
|
}
|
|
@@ -3835,20 +3972,47 @@ var init_dist2 = __esm({
|
|
|
3835
3972
|
/**
|
|
3836
3973
|
* Build an addLiquidity transaction for a Liquidity Book pair.
|
|
3837
3974
|
* Distributes tokenX/tokenY uniformly across active bin ± numBins.
|
|
3975
|
+
*
|
|
3976
|
+
* The LB pair stores tokenX/tokenY in the order set at factory creation,
|
|
3977
|
+
* which is **not** address-sorted. Callers may pass tokens in either order;
|
|
3978
|
+
* we always re-align with the pool's `getTokenX/getTokenY` (and swap amounts
|
|
3979
|
+
* accordingly) so the router's `if (tokenX != pair.tokenX) revert` path is
|
|
3980
|
+
* never hit.
|
|
3838
3981
|
*/
|
|
3839
3982
|
async buildAddLiquidity(params) {
|
|
3840
3983
|
const numBins = params.numBins ?? 5;
|
|
3841
3984
|
const deadline = params.deadline ?? BigInt("18446744073709551615");
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3985
|
+
const rpcUrl = this.requireRpc();
|
|
3986
|
+
const client = createPublicClient8({ transport: http8(rpcUrl) });
|
|
3987
|
+
const [poolTokenX, poolTokenY, onChainActiveId] = await Promise.all([
|
|
3988
|
+
client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getTokenX" }),
|
|
3989
|
+
client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getTokenY" }),
|
|
3990
|
+
params.activeIdDesired === void 0 ? client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getActiveId" }) : Promise.resolve(params.activeIdDesired)
|
|
3991
|
+
]);
|
|
3992
|
+
const activeIdDesired = onChainActiveId;
|
|
3993
|
+
const inX = params.tokenX.toLowerCase();
|
|
3994
|
+
const inY = params.tokenY.toLowerCase();
|
|
3995
|
+
const poolX = poolTokenX.toLowerCase();
|
|
3996
|
+
const poolY = poolTokenY.toLowerCase();
|
|
3997
|
+
let tokenX;
|
|
3998
|
+
let tokenY;
|
|
3999
|
+
let amountX;
|
|
4000
|
+
let amountY;
|
|
4001
|
+
if (inX === poolX && inY === poolY) {
|
|
4002
|
+
tokenX = params.tokenX;
|
|
4003
|
+
tokenY = params.tokenY;
|
|
4004
|
+
amountX = params.amountX;
|
|
4005
|
+
amountY = params.amountY;
|
|
4006
|
+
} else if (inX === poolY && inY === poolX) {
|
|
4007
|
+
tokenX = poolTokenX;
|
|
4008
|
+
tokenY = poolTokenY;
|
|
4009
|
+
amountX = params.amountY;
|
|
4010
|
+
amountY = params.amountX;
|
|
4011
|
+
} else {
|
|
4012
|
+
throw new DefiError(
|
|
4013
|
+
"CONTRACT_ERROR",
|
|
4014
|
+
`[${this.protocolName}] tokenX/tokenY ${params.tokenX}/${params.tokenY} do not match pool ${params.pool} (${poolTokenX}/${poolTokenY})`
|
|
4015
|
+
);
|
|
3852
4016
|
}
|
|
3853
4017
|
const deltaIds = [];
|
|
3854
4018
|
for (let d = -numBins; d <= numBins; d++) {
|
|
@@ -3860,11 +4024,11 @@ var init_dist2 = __esm({
|
|
|
3860
4024
|
functionName: "addLiquidity",
|
|
3861
4025
|
args: [
|
|
3862
4026
|
{
|
|
3863
|
-
tokenX
|
|
3864
|
-
tokenY
|
|
4027
|
+
tokenX,
|
|
4028
|
+
tokenY,
|
|
3865
4029
|
binStep: BigInt(params.binStep),
|
|
3866
|
-
amountX
|
|
3867
|
-
amountY
|
|
4030
|
+
amountX,
|
|
4031
|
+
amountY,
|
|
3868
4032
|
amountXMin: 0n,
|
|
3869
4033
|
amountYMin: 0n,
|
|
3870
4034
|
activeIdDesired: BigInt(activeIdDesired),
|
|
@@ -3879,31 +4043,63 @@ var init_dist2 = __esm({
|
|
|
3879
4043
|
]
|
|
3880
4044
|
});
|
|
3881
4045
|
return {
|
|
3882
|
-
description: `[${this.protocolName}] LB addLiquidity ${
|
|
4046
|
+
description: `[${this.protocolName}] LB addLiquidity ${amountX} tokenX + ${amountY} tokenY across ${deltaIds.length} bins`,
|
|
3883
4047
|
to: this.lbRouter,
|
|
3884
4048
|
data,
|
|
3885
4049
|
value: 0n,
|
|
3886
4050
|
gas_estimate: 8e5,
|
|
3887
4051
|
approvals: [
|
|
3888
|
-
{ token:
|
|
3889
|
-
{ token:
|
|
4052
|
+
{ token: tokenX, spender: this.lbRouter, amount: amountX },
|
|
4053
|
+
{ token: tokenY, spender: this.lbRouter, amount: amountY }
|
|
3890
4054
|
]
|
|
3891
4055
|
};
|
|
3892
4056
|
}
|
|
3893
4057
|
/**
|
|
3894
4058
|
* Build a removeLiquidity transaction for specific LB bins.
|
|
4059
|
+
*
|
|
4060
|
+
* When `pool` is supplied, the adapter realigns tokenX/tokenY (and
|
|
4061
|
+
* amountXMin/amountYMin) to the pool's actual ordering. Otherwise it trusts
|
|
4062
|
+
* the caller — the router will revert if the order is wrong.
|
|
3895
4063
|
*/
|
|
3896
4064
|
async buildRemoveLiquidity(params) {
|
|
3897
4065
|
const deadline = params.deadline ?? BigInt("18446744073709551615");
|
|
4066
|
+
let tokenX = params.tokenX;
|
|
4067
|
+
let tokenY = params.tokenY;
|
|
4068
|
+
let amountXMin = params.amountXMin ?? 0n;
|
|
4069
|
+
let amountYMin = params.amountYMin ?? 0n;
|
|
4070
|
+
if (params.pool) {
|
|
4071
|
+
const rpcUrl = this.requireRpc();
|
|
4072
|
+
const client = createPublicClient8({ transport: http8(rpcUrl) });
|
|
4073
|
+
const [poolTokenX, poolTokenY] = await Promise.all([
|
|
4074
|
+
client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getTokenX" }),
|
|
4075
|
+
client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getTokenY" })
|
|
4076
|
+
]);
|
|
4077
|
+
const inX = params.tokenX.toLowerCase();
|
|
4078
|
+
const inY = params.tokenY.toLowerCase();
|
|
4079
|
+
const poolX = poolTokenX.toLowerCase();
|
|
4080
|
+
const poolY = poolTokenY.toLowerCase();
|
|
4081
|
+
if (inX === poolY && inY === poolX) {
|
|
4082
|
+
tokenX = poolTokenX;
|
|
4083
|
+
tokenY = poolTokenY;
|
|
4084
|
+
const x = amountXMin;
|
|
4085
|
+
amountXMin = amountYMin;
|
|
4086
|
+
amountYMin = x;
|
|
4087
|
+
} else if (!(inX === poolX && inY === poolY)) {
|
|
4088
|
+
throw new DefiError(
|
|
4089
|
+
"CONTRACT_ERROR",
|
|
4090
|
+
`[${this.protocolName}] tokenX/tokenY ${params.tokenX}/${params.tokenY} do not match pool ${params.pool} (${poolTokenX}/${poolTokenY})`
|
|
4091
|
+
);
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
3898
4094
|
const data = encodeFunctionData12({
|
|
3899
4095
|
abi: lbRouterAbi,
|
|
3900
4096
|
functionName: "removeLiquidity",
|
|
3901
4097
|
args: [
|
|
3902
|
-
|
|
3903
|
-
|
|
4098
|
+
tokenX,
|
|
4099
|
+
tokenY,
|
|
3904
4100
|
params.binStep,
|
|
3905
|
-
|
|
3906
|
-
|
|
4101
|
+
amountXMin,
|
|
4102
|
+
amountYMin,
|
|
3907
4103
|
params.binIds.map(BigInt),
|
|
3908
4104
|
params.amounts,
|
|
3909
4105
|
params.recipient,
|
|
@@ -4813,7 +5009,7 @@ var init_dist2 = __esm({
|
|
|
4813
5009
|
const poolSet = /* @__PURE__ */ new Set();
|
|
4814
5010
|
for (const data of poolResults) {
|
|
4815
5011
|
const addr = decodeAddress3(data);
|
|
4816
|
-
if (addr && addr !==
|
|
5012
|
+
if (addr && addr !== zeroAddress6) {
|
|
4817
5013
|
poolSet.add(addr.toLowerCase());
|
|
4818
5014
|
}
|
|
4819
5015
|
}
|
|
@@ -4978,6 +5174,34 @@ var init_dist2 = __esm({
|
|
|
4978
5174
|
}
|
|
4979
5175
|
return apr;
|
|
4980
5176
|
}
|
|
5177
|
+
/**
|
|
5178
|
+
* Snapshot of all NEST gauged pools from the off-chain blaze API. The
|
|
5179
|
+
* on-chain ve(3,3) gauges return `rewardRate=0` (emissions are credited
|
|
5180
|
+
* off-chain via signed claim tickets), so the on-chain Solidly gauge
|
|
5181
|
+
* reader cannot surface real emission APR. This API is the canonical
|
|
5182
|
+
* read source — used by `lp discover` to expose non-zero NEST yields.
|
|
5183
|
+
*/
|
|
5184
|
+
async getLiquidityPools() {
|
|
5185
|
+
const raw = await this.fetchJson("/liquidity-pools");
|
|
5186
|
+
const out = [];
|
|
5187
|
+
for (const p of raw) {
|
|
5188
|
+
const t0 = p.token0?.basetoken;
|
|
5189
|
+
const t1 = p.token1?.basetoken;
|
|
5190
|
+
if (!t0?.address || !t1?.address) continue;
|
|
5191
|
+
out.push({
|
|
5192
|
+
pool: p.id,
|
|
5193
|
+
gauge: p.gauge ?? null,
|
|
5194
|
+
token0: { address: t0.address, symbol: t0.symbol ?? "?" },
|
|
5195
|
+
token1: { address: t1.address, symbol: t1.symbol ?? "?" },
|
|
5196
|
+
tvlUSD: Number(p.tvlUSD ?? 0),
|
|
5197
|
+
aprPercent: Number(p.apr ?? 0),
|
|
5198
|
+
curEpochEmissionRewardsUSD: Number(p.curEpochEmissionRewardsUSD ?? 0),
|
|
5199
|
+
poolType: p.poolType,
|
|
5200
|
+
isStable: p.isStable
|
|
5201
|
+
});
|
|
5202
|
+
}
|
|
5203
|
+
return out;
|
|
5204
|
+
}
|
|
4981
5205
|
/** Pending NEST emissions as IGauge-compatible RewardInfo[] */
|
|
4982
5206
|
async getPendingRewards(user) {
|
|
4983
5207
|
const status = await this.getClaimStatus(user);
|
|
@@ -5133,6 +5357,16 @@ var init_dist2 = __esm({
|
|
|
5133
5357
|
"function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf) external",
|
|
5134
5358
|
"function repay(address asset, uint256 amount, uint256 interestRateMode, address onBehalfOf) external returns (uint256)",
|
|
5135
5359
|
"function withdraw(address asset, uint256 amount, address to) external returns (uint256)",
|
|
5360
|
+
// Toggles required to actually borrow against an isolation-mode reserve
|
|
5361
|
+
// (https://aave.com/docs/aave-v3/smart-contracts/pool):
|
|
5362
|
+
// - setUserUseReserveAsCollateral: an isolation reserve can be enabled
|
|
5363
|
+
// only if no other asset is enabled; LTV=0 reserves can never be
|
|
5364
|
+
// enabled; disable reverts if the resulting HF would drop below the
|
|
5365
|
+
// liquidation threshold.
|
|
5366
|
+
// - setUserEMode: reverts if the user is borrowing a non-eMode-
|
|
5367
|
+
// compatible asset, or if the change would push HF below threshold.
|
|
5368
|
+
"function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external",
|
|
5369
|
+
"function setUserEMode(uint8 categoryId) external",
|
|
5136
5370
|
"function getUserAccountData(address user) external view returns (uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor)",
|
|
5137
5371
|
"function getReservesList() external view returns (address[])",
|
|
5138
5372
|
"function getReserveData(address asset) external view returns (uint256 configuration, uint128 liquidityIndex, uint128 currentLiquidityRate, uint128 variableBorrowIndex, uint128 currentVariableBorrowRate, uint128 currentStableBorrowRate, uint40 lastUpdateTimestamp, uint16 id, address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress, address interestRateStrategyAddress, uint128 accruedToTreasury, uint128 unbacked, uint128 isolationModeTotalDebt)"
|
|
@@ -5258,6 +5492,34 @@ var init_dist2 = __esm({
|
|
|
5258
5492
|
gas_estimate: 25e4
|
|
5259
5493
|
};
|
|
5260
5494
|
}
|
|
5495
|
+
async buildSetUseReserveAsCollateral(asset, useAsCollateral) {
|
|
5496
|
+
const data = encodeFunctionData14({
|
|
5497
|
+
abi: POOL_ABI,
|
|
5498
|
+
functionName: "setUserUseReserveAsCollateral",
|
|
5499
|
+
args: [asset, useAsCollateral]
|
|
5500
|
+
});
|
|
5501
|
+
return {
|
|
5502
|
+
description: `[${this.protocolName}] ${useAsCollateral ? "Enable" : "Disable"} ${asset} as collateral`,
|
|
5503
|
+
to: this.pool,
|
|
5504
|
+
data,
|
|
5505
|
+
value: 0n,
|
|
5506
|
+
gas_estimate: 1e5
|
|
5507
|
+
};
|
|
5508
|
+
}
|
|
5509
|
+
async buildSetEMode(categoryId) {
|
|
5510
|
+
const data = encodeFunctionData14({
|
|
5511
|
+
abi: POOL_ABI,
|
|
5512
|
+
functionName: "setUserEMode",
|
|
5513
|
+
args: [categoryId]
|
|
5514
|
+
});
|
|
5515
|
+
return {
|
|
5516
|
+
description: `[${this.protocolName}] Set eMode category to ${categoryId}${categoryId === 0 ? " (opt out)" : ""}`,
|
|
5517
|
+
to: this.pool,
|
|
5518
|
+
data,
|
|
5519
|
+
value: 0n,
|
|
5520
|
+
gas_estimate: 15e4
|
|
5521
|
+
};
|
|
5522
|
+
}
|
|
5261
5523
|
async getRates(asset) {
|
|
5262
5524
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
5263
5525
|
const reserveCallData = encodeFunctionData14({
|
|
@@ -5302,7 +5564,7 @@ var init_dist2 = __esm({
|
|
|
5302
5564
|
[aTokenAddress, encodeFunctionData14({ abi: INCENTIVES_ABI, functionName: "getIncentivesController" })]
|
|
5303
5565
|
]);
|
|
5304
5566
|
const controllerAddr = decodeAddress4(controllerRaw ?? null);
|
|
5305
|
-
if (controllerAddr && controllerAddr !==
|
|
5567
|
+
if (controllerAddr && controllerAddr !== zeroAddress7) {
|
|
5306
5568
|
const [supplyRewardsRaw, borrowRewardsRaw] = await multicallRead(this.rpcUrl, [
|
|
5307
5569
|
[controllerAddr, encodeFunctionData14({ abi: REWARDS_CONTROLLER_ABI, functionName: "getRewardsByAsset", args: [aTokenAddress] })],
|
|
5308
5570
|
[controllerAddr, encodeFunctionData14({ abi: REWARDS_CONTROLLER_ABI, functionName: "getRewardsByAsset", args: [variableDebtTokenAddress] })]
|
|
@@ -5498,8 +5760,8 @@ var init_dist2 = __esm({
|
|
|
5498
5760
|
if (p.value.borrow > 0n) borrows.push({ asset: p.value.asset, symbol: p.value.symbol, amount: p.value.borrow });
|
|
5499
5761
|
}
|
|
5500
5762
|
} catch {
|
|
5501
|
-
if (collateralUsd > 0) supplies.push({ asset:
|
|
5502
|
-
if (debtUsd > 0) borrows.push({ asset:
|
|
5763
|
+
if (collateralUsd > 0) supplies.push({ asset: zeroAddress7, symbol: "Total Collateral (per-asset breakdown unavailable)", amount: totalCollateralBase });
|
|
5764
|
+
if (debtUsd > 0) borrows.push({ asset: zeroAddress7, symbol: "Total Debt (per-asset breakdown unavailable)", amount: totalDebtBase });
|
|
5503
5765
|
}
|
|
5504
5766
|
return {
|
|
5505
5767
|
protocol: this.protocolName,
|
|
@@ -5666,8 +5928,8 @@ var init_dist2 = __esm({
|
|
|
5666
5928
|
const collateralUsd = u256ToF642(totalCollateralBase) / 1e18;
|
|
5667
5929
|
const debtUsd = u256ToF642(totalDebtBase) / 1e18;
|
|
5668
5930
|
const ltvBps = u256ToF642(ltv);
|
|
5669
|
-
const supplies = collateralUsd > 0 ? [{ asset:
|
|
5670
|
-
const borrows = debtUsd > 0 ? [{ asset:
|
|
5931
|
+
const supplies = collateralUsd > 0 ? [{ asset: zeroAddress8, symbol: "Total Collateral", amount: totalCollateralBase, value_usd: collateralUsd }] : [];
|
|
5932
|
+
const borrows = debtUsd > 0 ? [{ asset: zeroAddress8, symbol: "Total Debt", amount: totalDebtBase, value_usd: debtUsd }] : [];
|
|
5671
5933
|
return {
|
|
5672
5934
|
protocol: this.protocolName,
|
|
5673
5935
|
user,
|
|
@@ -5770,14 +6032,31 @@ var init_dist2 = __esm({
|
|
|
5770
6032
|
"function borrow(uint256 borrowAmount) external returns (uint256)",
|
|
5771
6033
|
"function repayBorrow(uint256 repayAmount) external returns (uint256)"
|
|
5772
6034
|
]);
|
|
6035
|
+
NATIVE_CTOKEN_ABI = parseAbi17([
|
|
6036
|
+
"function mint() external payable",
|
|
6037
|
+
"function repayBorrow() external payable"
|
|
6038
|
+
]);
|
|
6039
|
+
COMPTROLLER_ABI = parseAbi17([
|
|
6040
|
+
"function enterMarkets(address[] cTokens) external returns (uint256[])",
|
|
6041
|
+
"function exitMarket(address cToken) external returns (uint256)"
|
|
6042
|
+
]);
|
|
6043
|
+
NATIVE_SENTINEL = "0x0000000000000000000000000000000000000000";
|
|
5773
6044
|
BSC_BLOCKS_PER_YEAR = 10512e3;
|
|
5774
6045
|
CompoundV2Adapter = class {
|
|
5775
6046
|
protocolName;
|
|
5776
6047
|
defaultVtoken;
|
|
5777
6048
|
vTokenCandidates;
|
|
6049
|
+
comptroller;
|
|
5778
6050
|
rpcUrl;
|
|
5779
|
-
// Lazy cache: underlying asset address (lowercased) → vToken address
|
|
6051
|
+
// Lazy cache: underlying asset address (lowercased) → vToken address.
|
|
6052
|
+
// The native sentinel (0x0…) is mapped to the cETH/vBNB-style vToken
|
|
6053
|
+
// when one is detected during resolveVtoken().
|
|
5780
6054
|
vTokenByAsset = null;
|
|
6055
|
+
// The cETH/vBNB-style vToken whose underlying() reverts (it has no
|
|
6056
|
+
// ERC20 underlying — the underlying is the chain's native gas token).
|
|
6057
|
+
// Set lazily by resolveVtoken() and consulted by buildSupply/buildRepay
|
|
6058
|
+
// to switch to the payable mint() / repayBorrow() variants.
|
|
6059
|
+
nativeVtoken = null;
|
|
5781
6060
|
constructor(entry, rpcUrl) {
|
|
5782
6061
|
this.protocolName = entry.name;
|
|
5783
6062
|
this.rpcUrl = rpcUrl;
|
|
@@ -5785,6 +6064,7 @@ var init_dist2 = __esm({
|
|
|
5785
6064
|
const vtoken = contracts["vusdt"] ?? contracts["vusdc"] ?? contracts["vbnb"] ?? contracts["comptroller"];
|
|
5786
6065
|
if (!vtoken) throw DefiError.contractError("Missing vToken or comptroller address");
|
|
5787
6066
|
this.defaultVtoken = vtoken;
|
|
6067
|
+
this.comptroller = contracts["comptroller"];
|
|
5788
6068
|
this.vTokenCandidates = Object.entries(contracts).filter(([k]) => /^v[a-z][a-z0-9]*$/i.test(k)).map(([, v]) => v);
|
|
5789
6069
|
if (this.vTokenCandidates.length === 0) this.vTokenCandidates = [vtoken];
|
|
5790
6070
|
}
|
|
@@ -5793,19 +6073,38 @@ var init_dist2 = __esm({
|
|
|
5793
6073
|
if (!this.vTokenByAsset) {
|
|
5794
6074
|
const client = createPublicClient13({ transport: http13(this.rpcUrl) });
|
|
5795
6075
|
const map = /* @__PURE__ */ new Map();
|
|
6076
|
+
let nativeVtoken = null;
|
|
5796
6077
|
const lookups = await Promise.allSettled(
|
|
5797
6078
|
this.vTokenCandidates.map(async (v) => {
|
|
5798
|
-
|
|
5799
|
-
|
|
6079
|
+
try {
|
|
6080
|
+
const u = await client.readContract({ address: v, abi: CTOKEN_ABI, functionName: "underlying" });
|
|
6081
|
+
return { vtoken: v, underlying: u };
|
|
6082
|
+
} catch {
|
|
6083
|
+
return { vtoken: v, underlying: null };
|
|
6084
|
+
}
|
|
5800
6085
|
})
|
|
5801
6086
|
);
|
|
5802
6087
|
for (const r of lookups) {
|
|
5803
|
-
if (r.status
|
|
6088
|
+
if (r.status !== "fulfilled") continue;
|
|
6089
|
+
const { vtoken, underlying } = r.value;
|
|
6090
|
+
if (underlying) {
|
|
6091
|
+
map.set(underlying.toLowerCase(), vtoken);
|
|
6092
|
+
} else if (!nativeVtoken) {
|
|
6093
|
+
nativeVtoken = vtoken;
|
|
6094
|
+
}
|
|
6095
|
+
}
|
|
6096
|
+
if (nativeVtoken) {
|
|
6097
|
+
map.set(NATIVE_SENTINEL, nativeVtoken);
|
|
5804
6098
|
}
|
|
5805
6099
|
this.vTokenByAsset = map;
|
|
6100
|
+
this.nativeVtoken = nativeVtoken;
|
|
5806
6101
|
}
|
|
5807
6102
|
return this.vTokenByAsset.get(asset.toLowerCase()) ?? null;
|
|
5808
6103
|
}
|
|
6104
|
+
/** True iff `vtoken` is the cETH/vBNB-style native cToken for this protocol. */
|
|
6105
|
+
isNativeVtoken(vtoken) {
|
|
6106
|
+
return this.nativeVtoken !== null && vtoken.toLowerCase() === this.nativeVtoken.toLowerCase();
|
|
6107
|
+
}
|
|
5809
6108
|
name() {
|
|
5810
6109
|
return this.protocolName;
|
|
5811
6110
|
}
|
|
@@ -5819,6 +6118,16 @@ var init_dist2 = __esm({
|
|
|
5819
6118
|
}
|
|
5820
6119
|
async buildSupply(params) {
|
|
5821
6120
|
const vtoken = await this.vtokenFor(params.asset);
|
|
6121
|
+
if (this.isNativeVtoken(vtoken)) {
|
|
6122
|
+
const data2 = encodeFunctionData16({ abi: NATIVE_CTOKEN_ABI, functionName: "mint" });
|
|
6123
|
+
return {
|
|
6124
|
+
description: `[${this.protocolName}] Supply ${params.amount} (native) to Venus`,
|
|
6125
|
+
to: vtoken,
|
|
6126
|
+
data: data2,
|
|
6127
|
+
value: params.amount,
|
|
6128
|
+
gas_estimate: 3e5
|
|
6129
|
+
};
|
|
6130
|
+
}
|
|
5822
6131
|
const data = encodeFunctionData16({ abi: CTOKEN_ABI, functionName: "mint", args: [params.amount] });
|
|
5823
6132
|
return {
|
|
5824
6133
|
description: `[${this.protocolName}] Supply ${params.amount} of ${params.asset} to Venus`,
|
|
@@ -5842,6 +6151,16 @@ var init_dist2 = __esm({
|
|
|
5842
6151
|
}
|
|
5843
6152
|
async buildRepay(params) {
|
|
5844
6153
|
const vtoken = await this.vtokenFor(params.asset);
|
|
6154
|
+
if (this.isNativeVtoken(vtoken)) {
|
|
6155
|
+
const data2 = encodeFunctionData16({ abi: NATIVE_CTOKEN_ABI, functionName: "repayBorrow" });
|
|
6156
|
+
return {
|
|
6157
|
+
description: `[${this.protocolName}] Repay ${params.amount} (native) to Venus`,
|
|
6158
|
+
to: vtoken,
|
|
6159
|
+
data: data2,
|
|
6160
|
+
value: params.amount,
|
|
6161
|
+
gas_estimate: 3e5
|
|
6162
|
+
};
|
|
6163
|
+
}
|
|
5845
6164
|
const data = encodeFunctionData16({ abi: CTOKEN_ABI, functionName: "repayBorrow", args: [params.amount] });
|
|
5846
6165
|
return {
|
|
5847
6166
|
description: `[${this.protocolName}] Repay ${params.amount} of ${params.asset} to Venus`,
|
|
@@ -5854,6 +6173,37 @@ var init_dist2 = __esm({
|
|
|
5854
6173
|
}
|
|
5855
6174
|
async buildWithdraw(params) {
|
|
5856
6175
|
const vtoken = await this.vtokenFor(params.asset);
|
|
6176
|
+
const MAX_UINT2562 = (1n << 256n) - 1n;
|
|
6177
|
+
if (params.amount === MAX_UINT2562 && this.rpcUrl) {
|
|
6178
|
+
const client = createPublicClient13({ transport: http13(this.rpcUrl) });
|
|
6179
|
+
const [vtokenBalance, borrowBalance] = await Promise.all([
|
|
6180
|
+
client.readContract({
|
|
6181
|
+
address: vtoken,
|
|
6182
|
+
abi: CTOKEN_ABI,
|
|
6183
|
+
functionName: "balanceOf",
|
|
6184
|
+
args: [params.to]
|
|
6185
|
+
}),
|
|
6186
|
+
client.readContract({
|
|
6187
|
+
address: vtoken,
|
|
6188
|
+
abi: CTOKEN_ABI,
|
|
6189
|
+
functionName: "borrowBalanceStored",
|
|
6190
|
+
args: [params.to]
|
|
6191
|
+
}).catch(() => 0n)
|
|
6192
|
+
]);
|
|
6193
|
+
if (borrowBalance > 0n) {
|
|
6194
|
+
throw DefiError.contractError(
|
|
6195
|
+
`[${this.protocolName}] Cannot withdraw all (uint256.max) \u2014 wallet has an outstanding borrow of ${borrowBalance} on this market. Repay the borrow first, or pass an explicit --amount that leaves enough collateral.`
|
|
6196
|
+
);
|
|
6197
|
+
}
|
|
6198
|
+
const redeemData = encodeFunctionData16({ abi: CTOKEN_ABI, functionName: "redeem", args: [vtokenBalance] });
|
|
6199
|
+
return {
|
|
6200
|
+
description: `[${this.protocolName}] Withdraw all (auto-max, ${vtokenBalance} vTokens) of ${params.asset} from Venus`,
|
|
6201
|
+
to: vtoken,
|
|
6202
|
+
data: redeemData,
|
|
6203
|
+
value: 0n,
|
|
6204
|
+
gas_estimate: 35e4
|
|
6205
|
+
};
|
|
6206
|
+
}
|
|
5857
6207
|
const data = encodeFunctionData16({ abi: CTOKEN_ABI, functionName: "redeemUnderlying", args: [params.amount] });
|
|
5858
6208
|
return {
|
|
5859
6209
|
description: `[${this.protocolName}] Withdraw ${params.amount} of ${params.asset} from Venus`,
|
|
@@ -5863,6 +6213,37 @@ var init_dist2 = __esm({
|
|
|
5863
6213
|
gas_estimate: 25e4
|
|
5864
6214
|
};
|
|
5865
6215
|
}
|
|
6216
|
+
/**
|
|
6217
|
+
* Compound V2 family: enter cTokens as collateral via Comptroller.
|
|
6218
|
+
* Without this call, supplied assets sit dormant in the Comptroller's
|
|
6219
|
+
* accountAssets[] and `getAccountLiquidity` reports zero collateral —
|
|
6220
|
+
* any borrow then reverts. Mirrors the role of Aave V3's
|
|
6221
|
+
* setUserUseReserveAsCollateral, but the API is batch-by-cToken.
|
|
6222
|
+
*/
|
|
6223
|
+
async buildEnterMarkets(cTokens) {
|
|
6224
|
+
if (!this.comptroller) {
|
|
6225
|
+
throw DefiError.contractError(
|
|
6226
|
+
`[${this.protocolName}] enterMarkets requires the Comptroller address to be registered under [protocol.contracts] as 'comptroller'.`
|
|
6227
|
+
);
|
|
6228
|
+
}
|
|
6229
|
+
if (cTokens.length === 0) {
|
|
6230
|
+
throw DefiError.invalidParam(
|
|
6231
|
+
`[${this.protocolName}] enterMarkets requires at least one cToken address.`
|
|
6232
|
+
);
|
|
6233
|
+
}
|
|
6234
|
+
const data = encodeFunctionData16({
|
|
6235
|
+
abi: COMPTROLLER_ABI,
|
|
6236
|
+
functionName: "enterMarkets",
|
|
6237
|
+
args: [cTokens]
|
|
6238
|
+
});
|
|
6239
|
+
return {
|
|
6240
|
+
description: `[${this.protocolName}] Enter ${cTokens.length} market(s) as collateral`,
|
|
6241
|
+
to: this.comptroller,
|
|
6242
|
+
data,
|
|
6243
|
+
value: 0n,
|
|
6244
|
+
gas_estimate: 2e5
|
|
6245
|
+
};
|
|
6246
|
+
}
|
|
5866
6247
|
async getRates(asset) {
|
|
5867
6248
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
5868
6249
|
const client = createPublicClient13({ transport: http13(this.rpcUrl) });
|
|
@@ -6230,9 +6611,11 @@ var init_dist2 = __esm({
|
|
|
6230
6611
|
"function market(bytes32 id) external view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee)",
|
|
6231
6612
|
"function idToMarketParams(bytes32 id) external view returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv)",
|
|
6232
6613
|
"function supply((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) external returns (uint256 assetsSupplied, uint256 sharesSupplied)",
|
|
6614
|
+
"function supplyCollateral((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, bytes data) external",
|
|
6233
6615
|
"function borrow((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed)",
|
|
6234
6616
|
"function repay((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) external returns (uint256 assetsRepaid, uint256 sharesRepaid)",
|
|
6235
|
-
"function withdraw((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn)"
|
|
6617
|
+
"function withdraw((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn)",
|
|
6618
|
+
"function withdrawCollateral((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, address receiver) external"
|
|
6236
6619
|
]);
|
|
6237
6620
|
META_MORPHO_ABI = parseAbi20([
|
|
6238
6621
|
"function supplyQueueLength() external view returns (uint256)",
|
|
@@ -6259,6 +6642,8 @@ var init_dist2 = __esm({
|
|
|
6259
6642
|
rpcUrl;
|
|
6260
6643
|
metaMorphoVaults;
|
|
6261
6644
|
metaMorphoVaultEntries;
|
|
6645
|
+
namedMarkets;
|
|
6646
|
+
namedMarketByName;
|
|
6262
6647
|
vaultAssetMap = null;
|
|
6263
6648
|
constructor(entry, rpcUrl) {
|
|
6264
6649
|
this.protocolName = entry.name;
|
|
@@ -6270,6 +6655,26 @@ var init_dist2 = __esm({
|
|
|
6270
6655
|
this.defaultVault = contracts["fehype"] ?? contracts["vault"] ?? contracts["feusdc"];
|
|
6271
6656
|
this.metaMorphoVaultEntries = Object.entries(contracts).filter(([key]) => /^fe[a-z0-9_]+$/i.test(key) || key === "vault").map(([key, addr]) => ({ key, addr }));
|
|
6272
6657
|
this.metaMorphoVaults = this.metaMorphoVaultEntries.map((e) => e.addr);
|
|
6658
|
+
this.namedMarkets = entry.markets ?? [];
|
|
6659
|
+
const byName = /* @__PURE__ */ new Map();
|
|
6660
|
+
for (const m of this.namedMarkets) byName.set(m.name.toLowerCase(), m.id);
|
|
6661
|
+
this.namedMarketByName = byName;
|
|
6662
|
+
}
|
|
6663
|
+
/**
|
|
6664
|
+
* Resolve a friendly market name (e.g. `WMON-AUSD`) to its 32-byte
|
|
6665
|
+
* marketId via the per-protocol TOML registry. Returns null when the
|
|
6666
|
+
* adapter has no markets[] block or the name doesn't match any entry —
|
|
6667
|
+
* callers fall back to treating the input as a raw hex marketId.
|
|
6668
|
+
*/
|
|
6669
|
+
resolveMarketIdByName(name) {
|
|
6670
|
+
return this.namedMarketByName.get(name.toLowerCase()) ?? null;
|
|
6671
|
+
}
|
|
6672
|
+
/**
|
|
6673
|
+
* Returns the registered named markets for diagnostics (e.g. CLI error
|
|
6674
|
+
* messages listing valid choices when the user passes an unknown name).
|
|
6675
|
+
*/
|
|
6676
|
+
listNamedMarkets() {
|
|
6677
|
+
return this.namedMarkets;
|
|
6273
6678
|
}
|
|
6274
6679
|
async resolveVault(asset, preferKey) {
|
|
6275
6680
|
if (this.metaMorphoVaultEntries.length === 0 || !this.rpcUrl) return null;
|
|
@@ -6303,10 +6708,61 @@ var init_dist2 = __esm({
|
|
|
6303
6708
|
name() {
|
|
6304
6709
|
return this.protocolName;
|
|
6305
6710
|
}
|
|
6711
|
+
/**
|
|
6712
|
+
* Resolve a Morpho Blue marketId into the full MarketParams tuple by
|
|
6713
|
+
* calling Morpho.idToMarketParams(id). Used by every direct-market
|
|
6714
|
+
* method (supply / borrow / repay / withdraw / supplyCollateral /
|
|
6715
|
+
* withdrawCollateral) so the caller only has to pass the 32-byte
|
|
6716
|
+
* marketId — same shape as the Morpho UI / API.
|
|
6717
|
+
*/
|
|
6718
|
+
async resolveMarketParams(marketId) {
|
|
6719
|
+
if (!this.rpcUrl) {
|
|
6720
|
+
throw DefiError.rpcError(
|
|
6721
|
+
`[${this.protocolName}] No RPC URL configured \u2014 cannot resolve marketId ${marketId}`
|
|
6722
|
+
);
|
|
6723
|
+
}
|
|
6724
|
+
const client = createPublicClient16({ transport: http16(this.rpcUrl) });
|
|
6725
|
+
let result;
|
|
6726
|
+
try {
|
|
6727
|
+
result = await client.readContract({
|
|
6728
|
+
address: this.morpho,
|
|
6729
|
+
abi: MORPHO_ABI,
|
|
6730
|
+
functionName: "idToMarketParams",
|
|
6731
|
+
args: [marketId]
|
|
6732
|
+
});
|
|
6733
|
+
} catch (e) {
|
|
6734
|
+
throw DefiError.rpcError(
|
|
6735
|
+
`[${this.protocolName}] idToMarketParams(${marketId}) failed: ${e}`
|
|
6736
|
+
);
|
|
6737
|
+
}
|
|
6738
|
+
const [loanToken, collateralToken, oracle, irm, lltv] = result;
|
|
6739
|
+
if (loanToken === zeroAddress9 || collateralToken === zeroAddress9 || lltv === 0n) {
|
|
6740
|
+
throw DefiError.invalidParam(
|
|
6741
|
+
`[${this.protocolName}] marketId ${marketId} resolves to an empty MarketParams (loan=${loanToken}, collateral=${collateralToken}, lltv=${lltv}). Verify the id matches a registered market on this chain.`
|
|
6742
|
+
);
|
|
6743
|
+
}
|
|
6744
|
+
return { loanToken, collateralToken, oracle, irm, lltv };
|
|
6745
|
+
}
|
|
6306
6746
|
async buildSupply(params) {
|
|
6747
|
+
if (params.market_id) {
|
|
6748
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6749
|
+
const data = encodeFunctionData19({
|
|
6750
|
+
abi: MORPHO_ABI,
|
|
6751
|
+
functionName: "supply",
|
|
6752
|
+
args: [market, params.amount, 0n, params.on_behalf_of, "0x"]
|
|
6753
|
+
});
|
|
6754
|
+
return {
|
|
6755
|
+
description: `[${this.protocolName}] Supply ${params.amount} of ${params.asset} to market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6756
|
+
to: this.morpho,
|
|
6757
|
+
data,
|
|
6758
|
+
value: 0n,
|
|
6759
|
+
gas_estimate: 35e4,
|
|
6760
|
+
approvals: [{ token: params.asset, spender: this.morpho, amount: params.amount }]
|
|
6761
|
+
};
|
|
6762
|
+
}
|
|
6307
6763
|
const vault = await this.resolveVault(params.asset);
|
|
6308
6764
|
if (vault) {
|
|
6309
|
-
const
|
|
6765
|
+
const data = encodeFunctionData19({
|
|
6310
6766
|
abi: ERC4626_ABI,
|
|
6311
6767
|
functionName: "deposit",
|
|
6312
6768
|
args: [params.amount, params.on_behalf_of]
|
|
@@ -6314,50 +6770,118 @@ var init_dist2 = __esm({
|
|
|
6314
6770
|
return {
|
|
6315
6771
|
description: `[${this.protocolName}] Deposit ${params.amount} into MetaMorpho vault`,
|
|
6316
6772
|
to: vault,
|
|
6317
|
-
data
|
|
6773
|
+
data,
|
|
6318
6774
|
value: 0n,
|
|
6319
6775
|
gas_estimate: 4e5,
|
|
6320
6776
|
approvals: [{ token: params.asset, spender: vault, amount: params.amount }]
|
|
6321
6777
|
};
|
|
6322
6778
|
}
|
|
6323
|
-
|
|
6779
|
+
throw DefiError.invalidParam(
|
|
6780
|
+
`[${this.protocolName}] supply requires either a registered MetaMorpho vault for ${params.asset} or an explicit --market <marketId>. The legacy zero-MarketParams stub was removed (it always reverted on-chain).`
|
|
6781
|
+
);
|
|
6782
|
+
}
|
|
6783
|
+
async buildBorrow(params) {
|
|
6784
|
+
if (!params.market_id) {
|
|
6785
|
+
throw DefiError.invalidParam(
|
|
6786
|
+
`[${this.protocolName}] Morpho Blue borrow requires --market <marketId>. Find one via the Morpho API (https://blue-api.morpho.org/graphql).`
|
|
6787
|
+
);
|
|
6788
|
+
}
|
|
6789
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6324
6790
|
const data = encodeFunctionData19({
|
|
6325
6791
|
abi: MORPHO_ABI,
|
|
6326
|
-
functionName: "
|
|
6792
|
+
functionName: "borrow",
|
|
6793
|
+
args: [market, params.amount, 0n, params.on_behalf_of, params.on_behalf_of]
|
|
6794
|
+
});
|
|
6795
|
+
return {
|
|
6796
|
+
description: `[${this.protocolName}] Borrow ${params.amount} of ${params.asset} from market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6797
|
+
to: this.morpho,
|
|
6798
|
+
data,
|
|
6799
|
+
value: 0n,
|
|
6800
|
+
gas_estimate: 4e5
|
|
6801
|
+
};
|
|
6802
|
+
}
|
|
6803
|
+
async buildRepay(params) {
|
|
6804
|
+
if (!params.market_id) {
|
|
6805
|
+
throw DefiError.invalidParam(
|
|
6806
|
+
`[${this.protocolName}] Morpho Blue repay requires --market <marketId>.`
|
|
6807
|
+
);
|
|
6808
|
+
}
|
|
6809
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6810
|
+
if (params.amount === MAX_UINT256) {
|
|
6811
|
+
if (!this.rpcUrl) {
|
|
6812
|
+
throw DefiError.rpcError(
|
|
6813
|
+
`[${this.protocolName}] max-repay requires an RPC URL to read borrowShares.`
|
|
6814
|
+
);
|
|
6815
|
+
}
|
|
6816
|
+
const client = createPublicClient16({ transport: http16(this.rpcUrl) });
|
|
6817
|
+
const positionAbi = parseAbi20([
|
|
6818
|
+
"function position(bytes32 id, address user) external view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral)"
|
|
6819
|
+
]);
|
|
6820
|
+
const pos = await client.readContract({
|
|
6821
|
+
address: this.morpho,
|
|
6822
|
+
abi: positionAbi,
|
|
6823
|
+
functionName: "position",
|
|
6824
|
+
args: [params.market_id, params.on_behalf_of]
|
|
6825
|
+
});
|
|
6826
|
+
const [, borrowShares] = pos;
|
|
6827
|
+
if (borrowShares === 0n) {
|
|
6828
|
+
throw DefiError.invalidParam(
|
|
6829
|
+
`[${this.protocolName}] cannot repay max \u2014 user has no borrow position in market ${params.market_id}.`
|
|
6830
|
+
);
|
|
6831
|
+
}
|
|
6832
|
+
const data2 = encodeFunctionData19({
|
|
6833
|
+
abi: MORPHO_ABI,
|
|
6834
|
+
functionName: "repay",
|
|
6835
|
+
args: [market, 0n, borrowShares, params.on_behalf_of, "0x"]
|
|
6836
|
+
});
|
|
6837
|
+
return {
|
|
6838
|
+
description: `[${this.protocolName}] Repay max (${borrowShares} shares) to market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6839
|
+
to: this.morpho,
|
|
6840
|
+
data: data2,
|
|
6841
|
+
value: 0n,
|
|
6842
|
+
gas_estimate: 35e4,
|
|
6843
|
+
approvals: [{ token: params.asset, spender: this.morpho, amount: MAX_UINT256 }]
|
|
6844
|
+
};
|
|
6845
|
+
}
|
|
6846
|
+
const data = encodeFunctionData19({
|
|
6847
|
+
abi: MORPHO_ABI,
|
|
6848
|
+
functionName: "repay",
|
|
6327
6849
|
args: [market, params.amount, 0n, params.on_behalf_of, "0x"]
|
|
6328
6850
|
});
|
|
6329
6851
|
return {
|
|
6330
|
-
description: `[${this.protocolName}]
|
|
6852
|
+
description: `[${this.protocolName}] Repay ${params.amount} of ${params.asset} to market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6331
6853
|
to: this.morpho,
|
|
6332
6854
|
data,
|
|
6333
6855
|
value: 0n,
|
|
6334
|
-
gas_estimate:
|
|
6856
|
+
gas_estimate: 35e4,
|
|
6857
|
+
approvals: [{ token: params.asset, spender: this.morpho, amount: params.amount }]
|
|
6335
6858
|
};
|
|
6336
6859
|
}
|
|
6337
|
-
async
|
|
6338
|
-
const market =
|
|
6860
|
+
async buildSupplyCollateral(params) {
|
|
6861
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6339
6862
|
const data = encodeFunctionData19({
|
|
6340
6863
|
abi: MORPHO_ABI,
|
|
6341
|
-
functionName: "
|
|
6342
|
-
args: [market, params.amount,
|
|
6864
|
+
functionName: "supplyCollateral",
|
|
6865
|
+
args: [market, params.amount, params.on_behalf_of, "0x"]
|
|
6343
6866
|
});
|
|
6344
6867
|
return {
|
|
6345
|
-
description: `[${this.protocolName}]
|
|
6868
|
+
description: `[${this.protocolName}] Supply collateral ${params.amount} of ${params.asset} to market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6346
6869
|
to: this.morpho,
|
|
6347
6870
|
data,
|
|
6348
6871
|
value: 0n,
|
|
6349
|
-
gas_estimate: 35e4
|
|
6872
|
+
gas_estimate: 35e4,
|
|
6873
|
+
approvals: [{ token: params.asset, spender: this.morpho, amount: params.amount }]
|
|
6350
6874
|
};
|
|
6351
6875
|
}
|
|
6352
|
-
async
|
|
6353
|
-
const market =
|
|
6876
|
+
async buildWithdrawCollateral(params) {
|
|
6877
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6354
6878
|
const data = encodeFunctionData19({
|
|
6355
6879
|
abi: MORPHO_ABI,
|
|
6356
|
-
functionName: "
|
|
6357
|
-
args: [market, params.amount,
|
|
6880
|
+
functionName: "withdrawCollateral",
|
|
6881
|
+
args: [market, params.amount, params.to, params.to]
|
|
6358
6882
|
});
|
|
6359
6883
|
return {
|
|
6360
|
-
description: `[${this.protocolName}]
|
|
6884
|
+
description: `[${this.protocolName}] Withdraw collateral ${params.amount} of ${params.asset} from market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6361
6885
|
to: this.morpho,
|
|
6362
6886
|
data,
|
|
6363
6887
|
value: 0n,
|
|
@@ -6365,6 +6889,21 @@ var init_dist2 = __esm({
|
|
|
6365
6889
|
};
|
|
6366
6890
|
}
|
|
6367
6891
|
async buildWithdraw(params) {
|
|
6892
|
+
if (params.market_id) {
|
|
6893
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6894
|
+
const data = encodeFunctionData19({
|
|
6895
|
+
abi: MORPHO_ABI,
|
|
6896
|
+
functionName: "withdraw",
|
|
6897
|
+
args: [market, params.amount, 0n, params.to, params.to]
|
|
6898
|
+
});
|
|
6899
|
+
return {
|
|
6900
|
+
description: `[${this.protocolName}] Withdraw ${params.amount} of ${params.asset} from market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6901
|
+
to: this.morpho,
|
|
6902
|
+
data,
|
|
6903
|
+
value: 0n,
|
|
6904
|
+
gas_estimate: 3e5
|
|
6905
|
+
};
|
|
6906
|
+
}
|
|
6368
6907
|
const vault = await this.resolveVault(params.asset);
|
|
6369
6908
|
if (vault) {
|
|
6370
6909
|
if (params.amount === MAX_UINT256) {
|
|
@@ -6373,7 +6912,7 @@ var init_dist2 = __esm({
|
|
|
6373
6912
|
[vault, encodeFunctionData19({ abi: ERC4626_ABI, functionName: "balanceOf", args: [params.to] })]
|
|
6374
6913
|
]);
|
|
6375
6914
|
const shares = decodeU256(balRaw ?? null);
|
|
6376
|
-
const
|
|
6915
|
+
const data2 = encodeFunctionData19({
|
|
6377
6916
|
abi: ERC4626_ABI,
|
|
6378
6917
|
functionName: "redeem",
|
|
6379
6918
|
args: [shares, params.to, params.to]
|
|
@@ -6381,12 +6920,12 @@ var init_dist2 = __esm({
|
|
|
6381
6920
|
return {
|
|
6382
6921
|
description: `[${this.protocolName}] Redeem all shares (${shares}) from MetaMorpho vault`,
|
|
6383
6922
|
to: vault,
|
|
6384
|
-
data:
|
|
6923
|
+
data: data2,
|
|
6385
6924
|
value: 0n,
|
|
6386
6925
|
gas_estimate: 4e5
|
|
6387
6926
|
};
|
|
6388
6927
|
}
|
|
6389
|
-
const
|
|
6928
|
+
const data = encodeFunctionData19({
|
|
6390
6929
|
abi: ERC4626_ABI,
|
|
6391
6930
|
functionName: "withdraw",
|
|
6392
6931
|
args: [params.amount, params.to, params.to]
|
|
@@ -6394,24 +6933,14 @@ var init_dist2 = __esm({
|
|
|
6394
6933
|
return {
|
|
6395
6934
|
description: `[${this.protocolName}] Withdraw ${params.amount} assets from MetaMorpho vault`,
|
|
6396
6935
|
to: vault,
|
|
6397
|
-
data
|
|
6936
|
+
data,
|
|
6398
6937
|
value: 0n,
|
|
6399
6938
|
gas_estimate: 4e5
|
|
6400
6939
|
};
|
|
6401
6940
|
}
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
functionName: "withdraw",
|
|
6406
|
-
args: [market, params.amount, 0n, params.to, params.to]
|
|
6407
|
-
});
|
|
6408
|
-
return {
|
|
6409
|
-
description: `[${this.protocolName}] Withdraw ${params.amount} from Morpho market`,
|
|
6410
|
-
to: this.morpho,
|
|
6411
|
-
data,
|
|
6412
|
-
value: 0n,
|
|
6413
|
-
gas_estimate: 25e4
|
|
6414
|
-
};
|
|
6941
|
+
throw DefiError.invalidParam(
|
|
6942
|
+
`[${this.protocolName}] withdraw requires either a registered MetaMorpho vault for ${params.asset} or an explicit --market <marketId>. The legacy zero-MarketParams stub was removed (it always reverted on-chain).`
|
|
6943
|
+
);
|
|
6415
6944
|
}
|
|
6416
6945
|
async getRates(asset) {
|
|
6417
6946
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
@@ -6528,7 +7057,7 @@ var init_dist2 = __esm({
|
|
|
6528
7057
|
if (!this.hintHelpers || !this.sortedTroves || !this.rpcUrl) {
|
|
6529
7058
|
return [0n, 0n];
|
|
6530
7059
|
}
|
|
6531
|
-
const client =
|
|
7060
|
+
const client = createPublicClient17({ transport: http17(this.rpcUrl) });
|
|
6532
7061
|
const approxResult = await client.readContract({
|
|
6533
7062
|
address: this.hintHelpers,
|
|
6534
7063
|
abi: HINT_HELPERS_ABI,
|
|
@@ -6619,7 +7148,7 @@ var init_dist2 = __esm({
|
|
|
6619
7148
|
async getCdpInfo(cdpId) {
|
|
6620
7149
|
if (!this.rpcUrl) throw DefiError.rpcError(`[${this.protocolName}] getCdpInfo requires RPC \u2014 set HYPEREVM_RPC_URL`);
|
|
6621
7150
|
if (!this.troveManager) throw DefiError.contractError(`[${this.protocolName}] trove_manager contract not configured`);
|
|
6622
|
-
const client =
|
|
7151
|
+
const client = createPublicClient17({ transport: http17(this.rpcUrl) });
|
|
6623
7152
|
const data = await client.readContract({
|
|
6624
7153
|
address: this.troveManager,
|
|
6625
7154
|
abi: TROVE_MANAGER_ABI,
|
|
@@ -6637,13 +7166,13 @@ var init_dist2 = __esm({
|
|
|
6637
7166
|
protocol: this.protocolName,
|
|
6638
7167
|
cdp_id: cdpId,
|
|
6639
7168
|
collateral: {
|
|
6640
|
-
token:
|
|
7169
|
+
token: zeroAddress10,
|
|
6641
7170
|
symbol: "WHYPE",
|
|
6642
7171
|
amount: entireColl,
|
|
6643
7172
|
decimals: 18
|
|
6644
7173
|
},
|
|
6645
7174
|
debt: {
|
|
6646
|
-
token:
|
|
7175
|
+
token: zeroAddress10,
|
|
6647
7176
|
symbol: "feUSD",
|
|
6648
7177
|
amount: entireDebt,
|
|
6649
7178
|
decimals: 18
|
|
@@ -6678,7 +7207,7 @@ var init_dist2 = __esm({
|
|
|
6678
7207
|
if (asset !== this.asset && this.asset !== "0x0000000000000000000000000000000000000000") {
|
|
6679
7208
|
throw DefiError.unsupported(`[${this.protocolName}] Felix PriceFeed only supports asset ${this.asset}`);
|
|
6680
7209
|
}
|
|
6681
|
-
const client =
|
|
7210
|
+
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
6682
7211
|
let priceVal;
|
|
6683
7212
|
try {
|
|
6684
7213
|
const result = await client.readContract({
|
|
@@ -6770,7 +7299,7 @@ var init_dist2 = __esm({
|
|
|
6770
7299
|
}
|
|
6771
7300
|
async totalAssets() {
|
|
6772
7301
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6773
|
-
const client =
|
|
7302
|
+
const client = createPublicClient19({ transport: http19(this.rpcUrl) });
|
|
6774
7303
|
return client.readContract({
|
|
6775
7304
|
address: this.vaultAddress,
|
|
6776
7305
|
abi: ERC4626_ABI2,
|
|
@@ -6781,7 +7310,7 @@ var init_dist2 = __esm({
|
|
|
6781
7310
|
}
|
|
6782
7311
|
async convertToShares(assets) {
|
|
6783
7312
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6784
|
-
const client =
|
|
7313
|
+
const client = createPublicClient19({ transport: http19(this.rpcUrl) });
|
|
6785
7314
|
return client.readContract({
|
|
6786
7315
|
address: this.vaultAddress,
|
|
6787
7316
|
abi: ERC4626_ABI2,
|
|
@@ -6793,7 +7322,7 @@ var init_dist2 = __esm({
|
|
|
6793
7322
|
}
|
|
6794
7323
|
async convertToAssets(shares) {
|
|
6795
7324
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6796
|
-
const client =
|
|
7325
|
+
const client = createPublicClient19({ transport: http19(this.rpcUrl) });
|
|
6797
7326
|
return client.readContract({
|
|
6798
7327
|
address: this.vaultAddress,
|
|
6799
7328
|
abi: ERC4626_ABI2,
|
|
@@ -6805,7 +7334,7 @@ var init_dist2 = __esm({
|
|
|
6805
7334
|
}
|
|
6806
7335
|
async getVaultInfo() {
|
|
6807
7336
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6808
|
-
const client =
|
|
7337
|
+
const client = createPublicClient19({ transport: http19(this.rpcUrl) });
|
|
6809
7338
|
const [totalAssets, totalSupply, asset] = await Promise.all([
|
|
6810
7339
|
client.readContract({ address: this.vaultAddress, abi: ERC4626_ABI2, functionName: "totalAssets" }).catch((e) => {
|
|
6811
7340
|
throw DefiError.rpcError(`[${this.protocolName}] totalAssets failed: ${e}`);
|
|
@@ -6897,7 +7426,7 @@ var init_dist2 = __esm({
|
|
|
6897
7426
|
const data = encodeFunctionData23({
|
|
6898
7427
|
abi: STHYPE_ABI,
|
|
6899
7428
|
functionName: "submit",
|
|
6900
|
-
args: [
|
|
7429
|
+
args: [zeroAddress11]
|
|
6901
7430
|
});
|
|
6902
7431
|
return {
|
|
6903
7432
|
description: `[${this.protocolName}] Stake ${params.amount} HYPE for stHYPE`,
|
|
@@ -6923,7 +7452,7 @@ var init_dist2 = __esm({
|
|
|
6923
7452
|
}
|
|
6924
7453
|
async getInfo() {
|
|
6925
7454
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6926
|
-
const client =
|
|
7455
|
+
const client = createPublicClient20({ transport: http20(this.rpcUrl) });
|
|
6927
7456
|
const tokenAddr = this.sthypeToken ?? this.staking;
|
|
6928
7457
|
const totalSupply = await client.readContract({
|
|
6929
7458
|
address: tokenAddr,
|
|
@@ -6934,7 +7463,7 @@ var init_dist2 = __esm({
|
|
|
6934
7463
|
});
|
|
6935
7464
|
return {
|
|
6936
7465
|
protocol: this.protocolName,
|
|
6937
|
-
staked_token:
|
|
7466
|
+
staked_token: zeroAddress11,
|
|
6938
7467
|
liquid_token: tokenAddr,
|
|
6939
7468
|
exchange_rate: 1,
|
|
6940
7469
|
total_staked: totalSupply
|
|
@@ -6993,7 +7522,7 @@ var init_dist2 = __esm({
|
|
|
6993
7522
|
}
|
|
6994
7523
|
async getInfo() {
|
|
6995
7524
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6996
|
-
const client =
|
|
7525
|
+
const client = createPublicClient21({ transport: http21(this.rpcUrl) });
|
|
6997
7526
|
const totalStaked = await client.readContract({
|
|
6998
7527
|
address: this.staking,
|
|
6999
7528
|
abi: KINETIQ_ABI,
|
|
@@ -7008,7 +7537,7 @@ var init_dist2 = __esm({
|
|
|
7008
7537
|
const rateF64 = hypePrice > 0n && khypePrice > 0n ? Number(khypePrice * 10n ** 18n / hypePrice) / 1e18 : 1;
|
|
7009
7538
|
return {
|
|
7010
7539
|
protocol: this.protocolName,
|
|
7011
|
-
staked_token:
|
|
7540
|
+
staked_token: zeroAddress12,
|
|
7012
7541
|
liquid_token: this.liquidToken,
|
|
7013
7542
|
exchange_rate: rateF64,
|
|
7014
7543
|
total_staked: totalStaked
|
|
@@ -7228,7 +7757,7 @@ var init_dist2 = __esm({
|
|
|
7228
7757
|
}
|
|
7229
7758
|
async getCollectionInfo(collection) {
|
|
7230
7759
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7231
|
-
const client =
|
|
7760
|
+
const client = createPublicClient222({ transport: http222(this.rpcUrl) });
|
|
7232
7761
|
const [collectionName, symbol, totalSupply] = await Promise.all([
|
|
7233
7762
|
client.readContract({ address: collection, abi: ERC721_ABI, functionName: "name" }).catch((e) => {
|
|
7234
7763
|
throw DefiError.rpcError(`[${this.protocolName}] name failed: ${e}`);
|
|
@@ -7247,7 +7776,7 @@ var init_dist2 = __esm({
|
|
|
7247
7776
|
}
|
|
7248
7777
|
async getTokenInfo(collection, tokenId) {
|
|
7249
7778
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7250
|
-
const client =
|
|
7779
|
+
const client = createPublicClient222({ transport: http222(this.rpcUrl) });
|
|
7251
7780
|
const [owner, tokenUri] = await Promise.all([
|
|
7252
7781
|
client.readContract({ address: collection, abi: ERC721_ABI, functionName: "ownerOf", args: [tokenId] }).catch((e) => {
|
|
7253
7782
|
throw DefiError.rpcError(`[${this.protocolName}] ownerOf failed: ${e}`);
|
|
@@ -7263,7 +7792,7 @@ var init_dist2 = __esm({
|
|
|
7263
7792
|
}
|
|
7264
7793
|
async getBalance(owner, collection) {
|
|
7265
7794
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7266
|
-
const client =
|
|
7795
|
+
const client = createPublicClient222({ transport: http222(this.rpcUrl) });
|
|
7267
7796
|
return client.readContract({ address: collection, abi: ERC721_ABI, functionName: "balanceOf", args: [owner] }).catch((e) => {
|
|
7268
7797
|
throw DefiError.rpcError(`[${this.protocolName}] balanceOf failed: ${e}`);
|
|
7269
7798
|
});
|
|
@@ -7328,10 +7857,22 @@ var Executor = class _Executor {
|
|
|
7328
7857
|
dryRun;
|
|
7329
7858
|
rpcUrl;
|
|
7330
7859
|
explorerUrl;
|
|
7331
|
-
|
|
7860
|
+
/**
|
|
7861
|
+
* Optional viem Chain object. When set, all wallet/public clients built
|
|
7862
|
+
* inside this executor anchor to the explicit chainId at construction
|
|
7863
|
+
* time, defending against MITM RPCs that lie about eth_chainId and
|
|
7864
|
+
* keeping offline-signing safe under RPC drift (SSOT 7.4).
|
|
7865
|
+
*/
|
|
7866
|
+
chain;
|
|
7867
|
+
constructor(broadcast, rpcUrl, explorerUrl, chain) {
|
|
7332
7868
|
this.dryRun = !broadcast;
|
|
7333
7869
|
this.rpcUrl = rpcUrl;
|
|
7334
7870
|
this.explorerUrl = explorerUrl;
|
|
7871
|
+
this.chain = chain;
|
|
7872
|
+
}
|
|
7873
|
+
/** Returns the optional `{ chain }` spread for viem client constructors. */
|
|
7874
|
+
chainOpt() {
|
|
7875
|
+
return this.chain ? { chain: this.chain } : {};
|
|
7335
7876
|
}
|
|
7336
7877
|
/** Apply 20% buffer to a gas estimate */
|
|
7337
7878
|
static applyGasBuffer(gas) {
|
|
@@ -7455,7 +7996,7 @@ var Executor = class _Executor {
|
|
|
7455
7996
|
*/
|
|
7456
7997
|
async fetchEip1559Fees(rpcUrl) {
|
|
7457
7998
|
try {
|
|
7458
|
-
const client = createPublicClient2({ transport: http2(rpcUrl) });
|
|
7999
|
+
const client = createPublicClient2({ transport: http2(rpcUrl), ...this.chainOpt() });
|
|
7459
8000
|
let priorityFee = DEFAULT_PRIORITY_FEE_WEI;
|
|
7460
8001
|
try {
|
|
7461
8002
|
priorityFee = await client.estimateMaxPriorityFeePerGas();
|
|
@@ -7477,7 +8018,7 @@ var Executor = class _Executor {
|
|
|
7477
8018
|
/** Estimate gas dynamically with buffer, falling back to a hardcoded estimate */
|
|
7478
8019
|
async estimateGasWithBuffer(rpcUrl, tx, from) {
|
|
7479
8020
|
try {
|
|
7480
|
-
const client = createPublicClient2({ transport: http2(rpcUrl) });
|
|
8021
|
+
const client = createPublicClient2({ transport: http2(rpcUrl), ...this.chainOpt() });
|
|
7481
8022
|
const estimated = await client.estimateGas({
|
|
7482
8023
|
to: tx.to,
|
|
7483
8024
|
data: tx.data,
|
|
@@ -7501,7 +8042,7 @@ var Executor = class _Executor {
|
|
|
7501
8042
|
if (!rpcUrl) {
|
|
7502
8043
|
throw DefiError.rpcError("No RPC URL \u2014 cannot simulate. Set HYPEREVM_RPC_URL.");
|
|
7503
8044
|
}
|
|
7504
|
-
const client = createPublicClient2({ transport: http2(rpcUrl) });
|
|
8045
|
+
const client = createPublicClient2({ transport: http2(rpcUrl), ...this.chainOpt() });
|
|
7505
8046
|
const privateKey = process.env["DEFI_PRIVATE_KEY"];
|
|
7506
8047
|
const from = privateKey ? privateKeyToAccount(privateKey).address : "0x0000000000000000000000000000000000000001";
|
|
7507
8048
|
if (tx.approvals && tx.approvals.length > 0) {
|
|
@@ -7615,8 +8156,8 @@ var Executor = class _Executor {
|
|
|
7615
8156
|
if (!rpcUrl) {
|
|
7616
8157
|
throw DefiError.rpcError("No RPC URL configured for broadcasting");
|
|
7617
8158
|
}
|
|
7618
|
-
const publicClient = createPublicClient2({ transport: http2(rpcUrl) });
|
|
7619
|
-
const walletClient = createWalletClient({ account, transport: http2(rpcUrl) });
|
|
8159
|
+
const publicClient = createPublicClient2({ transport: http2(rpcUrl), ...this.chainOpt() });
|
|
8160
|
+
const walletClient = createWalletClient({ account, transport: http2(rpcUrl), ...this.chainOpt() });
|
|
7620
8161
|
if (tx.pre_txs && tx.pre_txs.length > 0) {
|
|
7621
8162
|
for (const preTx of tx.pre_txs) {
|
|
7622
8163
|
process.stderr.write(` Pre-tx: ${preTx.description}...
|
|
@@ -8298,7 +8839,7 @@ function registerSchema(parent, getOpts) {
|
|
|
8298
8839
|
init_dist();
|
|
8299
8840
|
init_dist();
|
|
8300
8841
|
init_dist2();
|
|
8301
|
-
import { parseAbi as parseAbi30, encodeFunctionData as encodeFunctionData27, decodeFunctionResult as decodeFunctionResult8, createPublicClient as createPublicClient23, http as http23, zeroAddress as
|
|
8842
|
+
import { parseAbi as parseAbi30, encodeFunctionData as encodeFunctionData27, decodeFunctionResult as decodeFunctionResult8, createPublicClient as createPublicClient23, http as http23, zeroAddress as zeroAddress13 } from "viem";
|
|
8302
8843
|
|
|
8303
8844
|
// src/whitelist.ts
|
|
8304
8845
|
import { readFileSync as readFileSync2 } from "fs";
|
|
@@ -8719,6 +9260,24 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
8719
9260
|
await Promise.allSettled(
|
|
8720
9261
|
protocols.map(async (protocol) => {
|
|
8721
9262
|
try {
|
|
9263
|
+
if (protocol.reward_strategy === "off_chain_api") {
|
|
9264
|
+
const adapter = createNestOffChain(protocol);
|
|
9265
|
+
const pools = await adapter.getLiquidityPools();
|
|
9266
|
+
for (const p of pools) {
|
|
9267
|
+
if (p.aprPercent === 0 && opts.emissionOnly) continue;
|
|
9268
|
+
results.push({
|
|
9269
|
+
protocol: protocol.slug,
|
|
9270
|
+
pool: p.pool,
|
|
9271
|
+
pair: `${p.token0.symbol}/${p.token1.symbol}`,
|
|
9272
|
+
type: p.aprPercent > 0 ? "EMISSION" : "FEE",
|
|
9273
|
+
source: "off_chain_api",
|
|
9274
|
+
aprPercent: p.aprPercent,
|
|
9275
|
+
poolTvlUsd: p.tvlUSD,
|
|
9276
|
+
emissionUsd: p.curEpochEmissionRewardsUSD
|
|
9277
|
+
});
|
|
9278
|
+
}
|
|
9279
|
+
return;
|
|
9280
|
+
}
|
|
8722
9281
|
const isGaugeProtocol = ["solidly_v2", "solidly_cl", "algebra_v3", "hybra"].includes(protocol.interface) || protocol.interface === "uniswap_v3" && protocol.contracts?.["voter"];
|
|
8723
9282
|
if (isGaugeProtocol) {
|
|
8724
9283
|
const chainTokens = registry.tokens.get(chainName)?.map((t) => t.address);
|
|
@@ -8834,7 +9393,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
8834
9393
|
try {
|
|
8835
9394
|
const decoded = decodeFunctionResult8({ abi: mcAbi, functionName: "poolInfo", data: raw });
|
|
8836
9395
|
const [allocPoint, v3Pool, t0, t1, , totalLiq] = decoded;
|
|
8837
|
-
if (allocPoint === 0n || v3Pool ===
|
|
9396
|
+
if (allocPoint === 0n || v3Pool === zeroAddress13) continue;
|
|
8838
9397
|
const tokens = registry.tokens.get(chainName);
|
|
8839
9398
|
const sym0 = tokens?.find((t) => t.address.toLowerCase() === t0?.toLowerCase())?.symbol ?? t0?.slice(0, 8) ?? "?";
|
|
8840
9399
|
const sym1 = tokens?.find((t) => t.address.toLowerCase() === t1?.toLowerCase())?.symbol ?? t1?.slice(0, 8) ?? "?";
|
|
@@ -8901,7 +9460,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
8901
9460
|
if (opts.emissionOnly) {
|
|
8902
9461
|
printOutput(
|
|
8903
9462
|
results.filter(
|
|
8904
|
-
(r) => r.type === "EMISSION" && ((r.moePerDay ?? 0) > 0 || r.rewardRate && BigInt(r.rewardRate) > 0n)
|
|
9463
|
+
(r) => r.type === "EMISSION" && ((r.moePerDay ?? 0) > 0 || r.rewardRate && BigInt(r.rewardRate) > 0n || (r.aprPercent ?? 0) > 0)
|
|
8905
9464
|
),
|
|
8906
9465
|
getOpts()
|
|
8907
9466
|
);
|
|
@@ -8909,7 +9468,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
8909
9468
|
printOutput(results, getOpts());
|
|
8910
9469
|
}
|
|
8911
9470
|
});
|
|
8912
|
-
lp.command("add").description("Add liquidity to a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--amount-a <amount>", "Amount of token A in wei").requiredOption("--amount-b <amount>", "Amount of token B in wei").option("--pool <name_or_address>", "Pool name (e.g. WHYPE/USDC) or address").option("--recipient <address>", "Recipient address").option("--tick-lower <tick>", "Lower tick for concentrated LP (default: full range)").option("--tick-upper <tick>", "Upper tick for concentrated LP (default: full range)").option("--range <percent>", "\xB1N% concentrated range around current price (e.g. --range 2)").option("--num-bins <n>", "Merchant Moe LB: bins on each side of active (default 5)").action(async (opts) => {
|
|
9471
|
+
lp.command("add").description("Add liquidity to a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--amount-a <amount>", "Amount of token A in wei").requiredOption("--amount-b <amount>", "Amount of token B in wei").option("--pool <name_or_address>", "Pool name (e.g. WHYPE/USDC) or address").option("--recipient <address>", "Recipient address").option("--tick-lower <tick>", "Lower tick for concentrated LP (default: full range)").option("--tick-upper <tick>", "Upper tick for concentrated LP (default: full range)").option("--range <percent>", "\xB1N% concentrated range around current price (e.g. --range 2)").option("--num-bins <n>", "Merchant Moe LB: bins on each side of active (default 5)").option("--slippage <bps>", "Slippage tolerance in basis points (default 50 = 0.5%). Sets amount{0,1}Min per side via applyMinSlippage.").option("--amount-a-min <wei>", "Explicit minimum of token_a accepted on add (overrides --slippage for that side).").option("--amount-b-min <wei>", "Explicit minimum of token_b accepted on add (overrides --slippage for that side).").action(async (opts) => {
|
|
8913
9472
|
const executor = makeExecutor2();
|
|
8914
9473
|
const chainName = parent.opts().chain;
|
|
8915
9474
|
if (!chainName) {
|
|
@@ -8926,13 +9485,27 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
8926
9485
|
if (protocol.interface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
|
|
8927
9486
|
if (!poolAddr) throw new Error(`--pool is required for ${protocol.name} (Liquidity Book \u2014 pass --pool <addr>; use \`lp discover --protocol ${protocol.slug}\` to list active pools)`);
|
|
8928
9487
|
const lbAdapter = createMerchantMoeLB(protocol, chain.effectiveRpcUrl());
|
|
8929
|
-
const [tokenX, tokenY, amountX, amountY] = tokenA.toLowerCase() < tokenB.toLowerCase() ? [tokenA, tokenB, BigInt(opts.amountA), BigInt(opts.amountB)] : [tokenB, tokenA, BigInt(opts.amountB), BigInt(opts.amountA)];
|
|
8930
9488
|
const client = createPublicClient23({ transport: http23(chain.effectiveRpcUrl()) });
|
|
8931
|
-
const
|
|
8932
|
-
address
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
9489
|
+
const lbPairOrderAbi = parseAbi30([
|
|
9490
|
+
"function getTokenX() view returns (address)",
|
|
9491
|
+
"function getTokenY() view returns (address)",
|
|
9492
|
+
"function getBinStep() view returns (uint16)"
|
|
9493
|
+
]);
|
|
9494
|
+
const [poolTokenX, poolTokenY, binStep] = await Promise.all([
|
|
9495
|
+
client.readContract({ address: poolAddr, abi: lbPairOrderAbi, functionName: "getTokenX" }),
|
|
9496
|
+
client.readContract({ address: poolAddr, abi: lbPairOrderAbi, functionName: "getTokenY" }),
|
|
9497
|
+
client.readContract({ address: poolAddr, abi: lbPairOrderAbi, functionName: "getBinStep" })
|
|
9498
|
+
]);
|
|
9499
|
+
const aIsX = tokenA.toLowerCase() === poolTokenX.toLowerCase();
|
|
9500
|
+
const bIsX = tokenB.toLowerCase() === poolTokenX.toLowerCase();
|
|
9501
|
+
const aIsY = tokenA.toLowerCase() === poolTokenY.toLowerCase();
|
|
9502
|
+
const bIsY = tokenB.toLowerCase() === poolTokenY.toLowerCase();
|
|
9503
|
+
if (!(aIsX && bIsY || bIsX && aIsY)) {
|
|
9504
|
+
throw new Error(
|
|
9505
|
+
`[${protocol.name}] --token-a/--token-b (${tokenA}/${tokenB}) do not match pool tokens (X=${poolTokenX}, Y=${poolTokenY})`
|
|
9506
|
+
);
|
|
9507
|
+
}
|
|
9508
|
+
const [tokenX, tokenY, amountX, amountY] = aIsX ? [tokenA, tokenB, BigInt(opts.amountA), BigInt(opts.amountB)] : [tokenB, tokenA, BigInt(opts.amountB), BigInt(opts.amountA)];
|
|
8936
9509
|
const tx2 = await lbAdapter.buildAddLiquidity({
|
|
8937
9510
|
pool: poolAddr,
|
|
8938
9511
|
tokenX,
|
|
@@ -8958,12 +9531,15 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
8958
9531
|
tick_lower: opts.tickLower !== void 0 ? parseInt(opts.tickLower) : void 0,
|
|
8959
9532
|
tick_upper: opts.tickUpper !== void 0 ? parseInt(opts.tickUpper) : void 0,
|
|
8960
9533
|
range_pct: opts.range !== void 0 ? parseFloat(opts.range) : void 0,
|
|
8961
|
-
pool: poolAddr
|
|
9534
|
+
pool: poolAddr,
|
|
9535
|
+
slippage: opts.slippage !== void 0 ? { bps: parseInt(opts.slippage, 10) } : void 0,
|
|
9536
|
+
amount_a_min: opts.amountAMin !== void 0 ? BigInt(opts.amountAMin) : void 0,
|
|
9537
|
+
amount_b_min: opts.amountBMin !== void 0 ? BigInt(opts.amountBMin) : void 0
|
|
8962
9538
|
});
|
|
8963
9539
|
const result = await executor.execute(tx);
|
|
8964
9540
|
printOutput(result, getOpts());
|
|
8965
9541
|
});
|
|
8966
|
-
lp.command("farm").description("Add liquidity and auto-stake into gauge/farming for emissions").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--amount-a <amount>", "Amount of token A in wei").requiredOption("--amount-b <amount>", "Amount of token B in wei").option("--pool <name_or_address>", "Pool name (e.g. WHYPE/USDC) or address").option("--gauge <address>", "Gauge address (required for solidly/hybra if not resolved automatically)").option("--recipient <address>", "Recipient / owner address").option("--tick-lower <tick>", "Lower tick for concentrated LP").option("--tick-upper <tick>", "Upper tick for concentrated LP").option("--range <percent>", "\xB1N% concentrated range around current price").action(async (opts) => {
|
|
9542
|
+
lp.command("farm").description("Add liquidity and auto-stake into gauge/farming for emissions").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--amount-a <amount>", "Amount of token A in wei").requiredOption("--amount-b <amount>", "Amount of token B in wei").option("--pool <name_or_address>", "Pool name (e.g. WHYPE/USDC) or address").option("--gauge <address>", "Gauge address (required for solidly/hybra if not resolved automatically)").option("--recipient <address>", "Recipient / owner address").option("--tick-lower <tick>", "Lower tick for concentrated LP").option("--tick-upper <tick>", "Upper tick for concentrated LP").option("--range <percent>", "\xB1N% concentrated range around current price").option("--slippage <bps>", "Slippage tolerance in basis points (default 50 = 0.5%). Applied to the underlying mint step.").option("--amount-a-min <wei>", "Explicit minimum of token_a accepted on add (overrides --slippage for that side).").option("--amount-b-min <wei>", "Explicit minimum of token_b accepted on add (overrides --slippage for that side).").action(async (opts) => {
|
|
8967
9543
|
const executor = makeExecutor2();
|
|
8968
9544
|
const chainName = parent.opts().chain;
|
|
8969
9545
|
if (!chainName) {
|
|
@@ -8989,7 +9565,10 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
8989
9565
|
tick_lower: opts.tickLower !== void 0 ? parseInt(opts.tickLower) : void 0,
|
|
8990
9566
|
tick_upper: opts.tickUpper !== void 0 ? parseInt(opts.tickUpper) : void 0,
|
|
8991
9567
|
range_pct: opts.range !== void 0 ? parseFloat(opts.range) : void 0,
|
|
8992
|
-
pool: poolAddr
|
|
9568
|
+
pool: poolAddr,
|
|
9569
|
+
slippage: opts.slippage !== void 0 ? { bps: parseInt(opts.slippage, 10) } : void 0,
|
|
9570
|
+
amount_a_min: opts.amountAMin !== void 0 ? BigInt(opts.amountAMin) : void 0,
|
|
9571
|
+
amount_b_min: opts.amountBMin !== void 0 ? BigInt(opts.amountBMin) : void 0
|
|
8993
9572
|
});
|
|
8994
9573
|
process.stderr.write("Step 1/2: Adding liquidity...\n");
|
|
8995
9574
|
const addResult = await executor.execute(addTx);
|
|
@@ -9229,7 +9808,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9229
9808
|
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."
|
|
9230
9809
|
}, getOpts());
|
|
9231
9810
|
});
|
|
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) => {
|
|
9811
|
+
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("--amount-a-min <wei>", "Explicit minimum of token_a accepted on remove (REQUIRED for V3/Algebra/Thena CL \u2014 caller must compute from positions(tokenId) + pool state and apply tolerance).").option("--amount-b-min <wei>", "Explicit minimum of token_b accepted on remove (REQUIRED for V3/Algebra/Thena CL).").option("--amounts <wei>", "Merchant Moe LB: comma-separated bin amounts (parallel to --bins, default: full balance)").action(async (opts) => {
|
|
9233
9812
|
const executor = makeExecutor2();
|
|
9234
9813
|
const chainName = parent.opts().chain;
|
|
9235
9814
|
if (!chainName) {
|
|
@@ -9251,14 +9830,28 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9251
9830
|
const lbAdapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
9252
9831
|
const tokenA2 = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
|
|
9253
9832
|
const tokenB2 = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
|
|
9254
|
-
const [tokenX, tokenY] = tokenA2.toLowerCase() < tokenB2.toLowerCase() ? [tokenA2, tokenB2] : [tokenB2, tokenA2];
|
|
9255
9833
|
const binIds = opts.bins.split(",").map((s) => parseInt(s.trim()));
|
|
9256
9834
|
const client = createPublicClient23({ transport: http23(rpcUrl) });
|
|
9257
|
-
const
|
|
9258
|
-
address
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9835
|
+
const lbPairOrderAbi = parseAbi30([
|
|
9836
|
+
"function getTokenX() view returns (address)",
|
|
9837
|
+
"function getTokenY() view returns (address)",
|
|
9838
|
+
"function getBinStep() view returns (uint16)"
|
|
9839
|
+
]);
|
|
9840
|
+
const [poolTokenX, poolTokenY, binStep] = await Promise.all([
|
|
9841
|
+
client.readContract({ address: opts.pool, abi: lbPairOrderAbi, functionName: "getTokenX" }),
|
|
9842
|
+
client.readContract({ address: opts.pool, abi: lbPairOrderAbi, functionName: "getTokenY" }),
|
|
9843
|
+
client.readContract({ address: opts.pool, abi: lbPairOrderAbi, functionName: "getBinStep" })
|
|
9844
|
+
]);
|
|
9845
|
+
const aIsX = tokenA2.toLowerCase() === poolTokenX.toLowerCase();
|
|
9846
|
+
const bIsX = tokenB2.toLowerCase() === poolTokenX.toLowerCase();
|
|
9847
|
+
const aIsY = tokenA2.toLowerCase() === poolTokenY.toLowerCase();
|
|
9848
|
+
const bIsY = tokenB2.toLowerCase() === poolTokenY.toLowerCase();
|
|
9849
|
+
if (!(aIsX && bIsY || bIsX && aIsY)) {
|
|
9850
|
+
throw new Error(
|
|
9851
|
+
`[${protocol.name}] --token-a/--token-b (${tokenA2}/${tokenB2}) do not match pool tokens (X=${poolTokenX}, Y=${poolTokenY})`
|
|
9852
|
+
);
|
|
9853
|
+
}
|
|
9854
|
+
const [tokenX, tokenY] = aIsX ? [tokenA2, tokenB2] : [tokenB2, tokenA2];
|
|
9262
9855
|
let amounts;
|
|
9263
9856
|
if (opts.amounts) {
|
|
9264
9857
|
amounts = opts.amounts.split(",").map((s) => BigInt(s.trim()));
|
|
@@ -9278,6 +9871,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9278
9871
|
gas_estimate: 8e4
|
|
9279
9872
|
};
|
|
9280
9873
|
const tx = await lbAdapter.buildRemoveLiquidity({
|
|
9874
|
+
pool: opts.pool,
|
|
9281
9875
|
tokenX,
|
|
9282
9876
|
tokenY,
|
|
9283
9877
|
binStep,
|
|
@@ -9390,13 +9984,17 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9390
9984
|
return;
|
|
9391
9985
|
}
|
|
9392
9986
|
const ZERO = "0x0000000000000000000000000000000000000000";
|
|
9987
|
+
const removePoolAddr = opts.pool ? resolvePoolAddress(registry, opts.protocol, opts.pool) : void 0;
|
|
9393
9988
|
const removeTx = await dexAdapter.buildRemoveLiquidity({
|
|
9394
9989
|
protocol: protocol.name,
|
|
9395
9990
|
token_a: tokenA ?? ZERO,
|
|
9396
9991
|
token_b: tokenB ?? ZERO,
|
|
9397
9992
|
liquidity: removeLiquidity,
|
|
9398
9993
|
recipient,
|
|
9399
|
-
token_id: opts.tokenId ? BigInt(opts.tokenId) : void 0
|
|
9994
|
+
token_id: opts.tokenId ? BigInt(opts.tokenId) : void 0,
|
|
9995
|
+
amount_a_min: opts.amountAMin !== void 0 ? BigInt(opts.amountAMin) : void 0,
|
|
9996
|
+
amount_b_min: opts.amountBMin !== void 0 ? BigInt(opts.amountBMin) : void 0,
|
|
9997
|
+
pool: removePoolAddr
|
|
9400
9998
|
});
|
|
9401
9999
|
const removeResult = await executor.execute(removeTx);
|
|
9402
10000
|
printOutput({ step: "lp_remove", ...removeResult }, getOpts());
|
|
@@ -9749,6 +10347,7 @@ function registerLP(parent, getOpts, makeExecutor2) {
|
|
|
9749
10347
|
// src/commands/lending.ts
|
|
9750
10348
|
init_dist();
|
|
9751
10349
|
init_dist2();
|
|
10350
|
+
import { maxUint256 } from "viem";
|
|
9752
10351
|
|
|
9753
10352
|
// src/utils.ts
|
|
9754
10353
|
init_dist();
|
|
@@ -9798,6 +10397,26 @@ function parseBigIntValue(v) {
|
|
|
9798
10397
|
}
|
|
9799
10398
|
|
|
9800
10399
|
// src/commands/lending.ts
|
|
10400
|
+
function parseAmount(s) {
|
|
10401
|
+
const lower = s.toLowerCase();
|
|
10402
|
+
if (lower === "max" || lower === "all") return maxUint256;
|
|
10403
|
+
return BigInt(s);
|
|
10404
|
+
}
|
|
10405
|
+
function resolveMarketInput(adapter, raw) {
|
|
10406
|
+
if (!raw) return void 0;
|
|
10407
|
+
if (/^0x[0-9a-fA-F]{64}$/.test(raw)) return raw;
|
|
10408
|
+
if (typeof adapter.resolveMarketIdByName !== "function") {
|
|
10409
|
+
throw new Error(
|
|
10410
|
+
`--market must be a 32-byte hex value; got '${raw}'. This adapter does not support named-market lookup.`
|
|
10411
|
+
);
|
|
10412
|
+
}
|
|
10413
|
+
const id = adapter.resolveMarketIdByName(raw);
|
|
10414
|
+
if (id) return id;
|
|
10415
|
+
const known = adapter.listNamedMarkets?.()?.map((m) => m.name).join(", ") ?? "(none)";
|
|
10416
|
+
throw new Error(
|
|
10417
|
+
`--market '${raw}' is not a 32-byte hex and is not registered on this protocol. Known markets: ${known}.`
|
|
10418
|
+
);
|
|
10419
|
+
}
|
|
9801
10420
|
function registerLending(parent, getOpts, makeExecutor2) {
|
|
9802
10421
|
const lending = parent.command("lending").description("Lending operations: supply, borrow, repay, withdraw, rates, position");
|
|
9803
10422
|
lending.command("rates").description("Show current lending rates").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").action(async (opts) => {
|
|
@@ -9820,18 +10439,24 @@ function registerLending(parent, getOpts, makeExecutor2) {
|
|
|
9820
10439
|
const position = await adapter.getUserPosition(address);
|
|
9821
10440
|
printOutput(position, getOpts());
|
|
9822
10441
|
});
|
|
9823
|
-
lending.command("supply").description("Supply an asset to a lending protocol").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount to supply in wei").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
|
|
10442
|
+
lending.command("supply").description("Supply an asset to a lending protocol").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount to supply in wei (or 'max')").option("--market <marketId>", "Morpho Blue marketId (32-byte hex) \u2014 required for direct Morpho markets, ignored elsewhere").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
|
|
9824
10443
|
const executor = makeExecutor2();
|
|
9825
10444
|
const ctx = resolveContext(parent, getOpts, opts.protocol);
|
|
9826
10445
|
if (!ctx) return;
|
|
9827
10446
|
const adapter = createLending(ctx.protocol, ctx.rpcUrl);
|
|
9828
10447
|
const asset = resolveTokenAddress(ctx.registry, ctx.chainName, opts.asset);
|
|
9829
10448
|
const onBehalfOf = resolveWallet(opts.onBehalfOf);
|
|
9830
|
-
const tx = await adapter.buildSupply({
|
|
10449
|
+
const tx = await adapter.buildSupply({
|
|
10450
|
+
protocol: ctx.protocol.name,
|
|
10451
|
+
asset,
|
|
10452
|
+
amount: parseAmount(opts.amount),
|
|
10453
|
+
on_behalf_of: onBehalfOf,
|
|
10454
|
+
market_id: resolveMarketInput(adapter, opts.market)
|
|
10455
|
+
});
|
|
9831
10456
|
const result = await executor.execute(tx);
|
|
9832
10457
|
printOutput(result, getOpts());
|
|
9833
10458
|
});
|
|
9834
|
-
lending.command("borrow").description("Borrow an asset").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount in wei").option("--rate-mode <mode>", "variable or stable", "variable").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
|
|
10459
|
+
lending.command("borrow").description("Borrow an asset").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount in wei (or 'max')").option("--rate-mode <mode>", "variable or stable", "variable").option("--market <marketId>", "Morpho Blue marketId (32-byte hex) \u2014 required for direct Morpho markets, ignored elsewhere").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
|
|
9835
10460
|
const executor = makeExecutor2();
|
|
9836
10461
|
const ctx = resolveContext(parent, getOpts, opts.protocol);
|
|
9837
10462
|
if (!ctx) return;
|
|
@@ -9841,14 +10466,15 @@ function registerLending(parent, getOpts, makeExecutor2) {
|
|
|
9841
10466
|
const tx = await adapter.buildBorrow({
|
|
9842
10467
|
protocol: ctx.protocol.name,
|
|
9843
10468
|
asset,
|
|
9844
|
-
amount:
|
|
10469
|
+
amount: parseAmount(opts.amount),
|
|
9845
10470
|
interest_rate_mode: opts.rateMode === "stable" ? InterestRateMode.Stable : InterestRateMode.Variable,
|
|
9846
|
-
on_behalf_of: onBehalfOf
|
|
10471
|
+
on_behalf_of: onBehalfOf,
|
|
10472
|
+
market_id: resolveMarketInput(adapter, opts.market)
|
|
9847
10473
|
});
|
|
9848
10474
|
const result = await executor.execute(tx);
|
|
9849
10475
|
printOutput(result, getOpts());
|
|
9850
10476
|
});
|
|
9851
|
-
lending.command("repay").description("Repay a borrowed asset").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount in wei").option("--rate-mode <mode>", "variable or stable", "variable").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
|
|
10477
|
+
lending.command("repay").description("Repay a borrowed asset").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount in wei (or 'max')").option("--rate-mode <mode>", "variable or stable", "variable").option("--market <marketId>", "Morpho Blue marketId (32-byte hex) \u2014 required for direct Morpho markets, ignored elsewhere").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
|
|
9852
10478
|
const executor = makeExecutor2();
|
|
9853
10479
|
const ctx = resolveContext(parent, getOpts, opts.protocol);
|
|
9854
10480
|
if (!ctx) return;
|
|
@@ -9858,21 +10484,150 @@ function registerLending(parent, getOpts, makeExecutor2) {
|
|
|
9858
10484
|
const tx = await adapter.buildRepay({
|
|
9859
10485
|
protocol: ctx.protocol.name,
|
|
9860
10486
|
asset,
|
|
9861
|
-
amount:
|
|
10487
|
+
amount: parseAmount(opts.amount),
|
|
9862
10488
|
interest_rate_mode: opts.rateMode === "stable" ? InterestRateMode.Stable : InterestRateMode.Variable,
|
|
9863
|
-
on_behalf_of: onBehalfOf
|
|
10489
|
+
on_behalf_of: onBehalfOf,
|
|
10490
|
+
market_id: resolveMarketInput(adapter, opts.market)
|
|
9864
10491
|
});
|
|
9865
10492
|
const result = await executor.execute(tx);
|
|
9866
10493
|
printOutput(result, getOpts());
|
|
9867
10494
|
});
|
|
9868
|
-
lending.command("withdraw").description("Withdraw a supplied asset").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount in wei").option("--to <address>", "Recipient address").action(async (opts) => {
|
|
10495
|
+
lending.command("withdraw").description("Withdraw a supplied asset").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount in wei (or 'max')").option("--market <marketId>", "Morpho Blue marketId (32-byte hex) \u2014 required for direct Morpho markets, ignored elsewhere").option("--to <address>", "Recipient address").action(async (opts) => {
|
|
9869
10496
|
const executor = makeExecutor2();
|
|
9870
10497
|
const ctx = resolveContext(parent, getOpts, opts.protocol);
|
|
9871
10498
|
if (!ctx) return;
|
|
9872
10499
|
const adapter = createLending(ctx.protocol, ctx.rpcUrl);
|
|
9873
10500
|
const asset = resolveTokenAddress(ctx.registry, ctx.chainName, opts.asset);
|
|
9874
10501
|
const to = resolveWallet(opts.to);
|
|
9875
|
-
const tx = await adapter.buildWithdraw({
|
|
10502
|
+
const tx = await adapter.buildWithdraw({
|
|
10503
|
+
protocol: ctx.protocol.name,
|
|
10504
|
+
asset,
|
|
10505
|
+
amount: parseAmount(opts.amount),
|
|
10506
|
+
to,
|
|
10507
|
+
market_id: resolveMarketInput(adapter, opts.market)
|
|
10508
|
+
});
|
|
10509
|
+
const result = await executor.execute(tx);
|
|
10510
|
+
printOutput(result, getOpts());
|
|
10511
|
+
});
|
|
10512
|
+
lending.command("toggle-collateral").description("Enable or disable a supplied reserve as collateral (Aave V3 family)").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").option("--enable", "Enable as collateral").option("--disable", "Disable as collateral").action(async (opts) => {
|
|
10513
|
+
const executor = makeExecutor2();
|
|
10514
|
+
const ctx = resolveContext(parent, getOpts, opts.protocol);
|
|
10515
|
+
if (!ctx) return;
|
|
10516
|
+
if (!opts.enable && !opts.disable) {
|
|
10517
|
+
printOutput({ error: "must pass either --enable or --disable" }, getOpts());
|
|
10518
|
+
return;
|
|
10519
|
+
}
|
|
10520
|
+
if (opts.enable && opts.disable) {
|
|
10521
|
+
printOutput({ error: "--enable and --disable are mutually exclusive" }, getOpts());
|
|
10522
|
+
return;
|
|
10523
|
+
}
|
|
10524
|
+
const adapter = createLending(ctx.protocol, ctx.rpcUrl);
|
|
10525
|
+
if (typeof adapter.buildSetUseReserveAsCollateral !== "function") {
|
|
10526
|
+
printOutput({
|
|
10527
|
+
error: `[${ctx.protocol.name}] adapter does not implement buildSetUseReserveAsCollateral. Aave V3 forks support this; Compound V2/Morpho Blue use different flows.`
|
|
10528
|
+
}, getOpts());
|
|
10529
|
+
return;
|
|
10530
|
+
}
|
|
10531
|
+
const asset = resolveTokenAddress(ctx.registry, ctx.chainName, opts.asset);
|
|
10532
|
+
const tx = await adapter.buildSetUseReserveAsCollateral(asset, !!opts.enable);
|
|
10533
|
+
const result = await executor.execute(tx);
|
|
10534
|
+
printOutput(result, getOpts());
|
|
10535
|
+
});
|
|
10536
|
+
lending.command("set-emode").description("Enroll the user in an Aave V3 efficiency-mode category (0 to opt out)").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--category-id <id>", "eMode category id (0 = opt out)").action(async (opts) => {
|
|
10537
|
+
const executor = makeExecutor2();
|
|
10538
|
+
const ctx = resolveContext(parent, getOpts, opts.protocol);
|
|
10539
|
+
if (!ctx) return;
|
|
10540
|
+
const adapter = createLending(ctx.protocol, ctx.rpcUrl);
|
|
10541
|
+
if (typeof adapter.buildSetEMode !== "function") {
|
|
10542
|
+
printOutput({
|
|
10543
|
+
error: `[${ctx.protocol.name}] adapter does not implement buildSetEMode (Aave V3 only)`
|
|
10544
|
+
}, getOpts());
|
|
10545
|
+
return;
|
|
10546
|
+
}
|
|
10547
|
+
const id = parseInt(opts.categoryId, 10);
|
|
10548
|
+
if (!Number.isInteger(id) || id < 0 || id > 255) {
|
|
10549
|
+
printOutput({ error: `--category-id must be an integer in [0, 255], got '${opts.categoryId}'` }, getOpts());
|
|
10550
|
+
return;
|
|
10551
|
+
}
|
|
10552
|
+
const tx = await adapter.buildSetEMode(id);
|
|
10553
|
+
const result = await executor.execute(tx);
|
|
10554
|
+
printOutput(result, getOpts());
|
|
10555
|
+
});
|
|
10556
|
+
lending.command("enter-markets").description("Compound V2 (Venus): enter supplied assets as collateral via Comptroller.enterMarkets").requiredOption("--protocol <protocol>", "Protocol slug (must be a Compound V2 fork)").requiredOption("--asset <token>", "Underlying asset symbol or address (resolved to its cToken)").action(async (opts) => {
|
|
10557
|
+
const executor = makeExecutor2();
|
|
10558
|
+
const ctx = resolveContext(parent, getOpts, opts.protocol);
|
|
10559
|
+
if (!ctx) return;
|
|
10560
|
+
const adapter = createLending(ctx.protocol, ctx.rpcUrl);
|
|
10561
|
+
if (typeof adapter.buildEnterMarkets !== "function") {
|
|
10562
|
+
printOutput({
|
|
10563
|
+
error: `[${ctx.protocol.name}] adapter does not implement buildEnterMarkets. This is a Compound V2 family operation; Aave V3 uses toggle-collateral instead.`
|
|
10564
|
+
}, getOpts());
|
|
10565
|
+
return;
|
|
10566
|
+
}
|
|
10567
|
+
const asset = resolveTokenAddress(ctx.registry, ctx.chainName, opts.asset);
|
|
10568
|
+
const contracts = ctx.protocol.contracts ?? {};
|
|
10569
|
+
const vTokenEntries = Object.entries(contracts).filter(([k]) => /^v[a-z][a-z0-9]*$/i.test(k));
|
|
10570
|
+
if (vTokenEntries.length === 0) {
|
|
10571
|
+
printOutput({ error: `[${ctx.protocol.name}] no vTokens registered in TOML` }, getOpts());
|
|
10572
|
+
return;
|
|
10573
|
+
}
|
|
10574
|
+
const symbol = opts.asset.toLowerCase();
|
|
10575
|
+
const matchedKey = vTokenEntries.find(([k]) => k.toLowerCase() === `v${symbol}`);
|
|
10576
|
+
const vToken = matchedKey ? matchedKey[1] : void 0;
|
|
10577
|
+
if (!vToken) {
|
|
10578
|
+
printOutput({
|
|
10579
|
+
error: `[${ctx.protocol.name}] could not resolve a vToken for '${opts.asset}'. Registered vTokens: ${vTokenEntries.map(([k]) => k).join(", ")}. Pass --asset matching the symbol after the 'v' prefix (e.g. USDT for vusdt).`
|
|
10580
|
+
}, getOpts());
|
|
10581
|
+
return;
|
|
10582
|
+
}
|
|
10583
|
+
void asset;
|
|
10584
|
+
const tx = await adapter.buildEnterMarkets([vToken]);
|
|
10585
|
+
const result = await executor.execute(tx);
|
|
10586
|
+
printOutput(result, getOpts());
|
|
10587
|
+
});
|
|
10588
|
+
lending.command("supply-collateral").description("Supply the collateral side of a Morpho Blue market (different selector from supply)").requiredOption("--protocol <protocol>", "Protocol slug (must be a Morpho Blue adapter)").requiredOption("--asset <token>", "Collateral token symbol or address").requiredOption("--amount <amount>", "Amount in wei (or 'max')").requiredOption("--market <marketId>", "32-byte Morpho marketId (find via Morpho API)").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
|
|
10589
|
+
const executor = makeExecutor2();
|
|
10590
|
+
const ctx = resolveContext(parent, getOpts, opts.protocol);
|
|
10591
|
+
if (!ctx) return;
|
|
10592
|
+
const adapter = createLending(ctx.protocol, ctx.rpcUrl);
|
|
10593
|
+
if (typeof adapter.buildSupplyCollateral !== "function") {
|
|
10594
|
+
printOutput({
|
|
10595
|
+
error: `[${ctx.protocol.name}] adapter does not implement buildSupplyCollateral. Only Morpho Blue forks expose this; Aave V3 / Compound use plain supply.`
|
|
10596
|
+
}, getOpts());
|
|
10597
|
+
return;
|
|
10598
|
+
}
|
|
10599
|
+
const asset = resolveTokenAddress(ctx.registry, ctx.chainName, opts.asset);
|
|
10600
|
+
const onBehalfOf = resolveWallet(opts.onBehalfOf);
|
|
10601
|
+
const tx = await adapter.buildSupplyCollateral({
|
|
10602
|
+
protocol: ctx.protocol.name,
|
|
10603
|
+
asset,
|
|
10604
|
+
amount: parseAmount(opts.amount),
|
|
10605
|
+
on_behalf_of: onBehalfOf,
|
|
10606
|
+
market_id: resolveMarketInput(adapter, opts.market)
|
|
10607
|
+
});
|
|
10608
|
+
const result = await executor.execute(tx);
|
|
10609
|
+
printOutput(result, getOpts());
|
|
10610
|
+
});
|
|
10611
|
+
lending.command("withdraw-collateral").description("Withdraw the collateral side of a Morpho Blue market").requiredOption("--protocol <protocol>", "Protocol slug (must be a Morpho Blue adapter)").requiredOption("--asset <token>", "Collateral token symbol or address").requiredOption("--amount <amount>", "Amount in wei (or 'max')").requiredOption("--market <marketId>", "32-byte Morpho marketId").option("--to <address>", "Recipient address").action(async (opts) => {
|
|
10612
|
+
const executor = makeExecutor2();
|
|
10613
|
+
const ctx = resolveContext(parent, getOpts, opts.protocol);
|
|
10614
|
+
if (!ctx) return;
|
|
10615
|
+
const adapter = createLending(ctx.protocol, ctx.rpcUrl);
|
|
10616
|
+
if (typeof adapter.buildWithdrawCollateral !== "function") {
|
|
10617
|
+
printOutput({
|
|
10618
|
+
error: `[${ctx.protocol.name}] adapter does not implement buildWithdrawCollateral.`
|
|
10619
|
+
}, getOpts());
|
|
10620
|
+
return;
|
|
10621
|
+
}
|
|
10622
|
+
const asset = resolveTokenAddress(ctx.registry, ctx.chainName, opts.asset);
|
|
10623
|
+
const to = resolveWallet(opts.to);
|
|
10624
|
+
const tx = await adapter.buildWithdrawCollateral({
|
|
10625
|
+
protocol: ctx.protocol.name,
|
|
10626
|
+
asset,
|
|
10627
|
+
amount: parseAmount(opts.amount),
|
|
10628
|
+
to,
|
|
10629
|
+
market_id: resolveMarketInput(adapter, opts.market)
|
|
10630
|
+
});
|
|
9876
10631
|
const result = await executor.execute(tx);
|
|
9877
10632
|
printOutput(result, getOpts());
|
|
9878
10633
|
});
|
|
@@ -11242,7 +11997,7 @@ function registerWallet(parent, getOpts) {
|
|
|
11242
11997
|
|
|
11243
11998
|
// src/commands/token.ts
|
|
11244
11999
|
init_dist();
|
|
11245
|
-
import { createPublicClient as createPublicClient27, http as http27, maxUint256 } from "viem";
|
|
12000
|
+
import { createPublicClient as createPublicClient27, encodeFunctionData as encodeFunctionData30, http as http27, maxUint256 as maxUint2562, parseAbi as parseAbi34 } from "viem";
|
|
11246
12001
|
function registerToken(parent, getOpts, makeExecutor2) {
|
|
11247
12002
|
const token = parent.command("token").description("Token operations: approve, allowance, transfer, balance");
|
|
11248
12003
|
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) => {
|
|
@@ -11276,7 +12031,7 @@ function registerToken(parent, getOpts, makeExecutor2) {
|
|
|
11276
12031
|
if (!chainName) return;
|
|
11277
12032
|
const registry = Registry.loadEmbedded();
|
|
11278
12033
|
const tokenAddr = resolveTokenAddress(registry, chainName, opts.token);
|
|
11279
|
-
const amount = opts.amount === "max" ?
|
|
12034
|
+
const amount = opts.amount === "max" ? maxUint2562 : BigInt(opts.amount);
|
|
11280
12035
|
const tx = buildApprove(tokenAddr, opts.spender, amount);
|
|
11281
12036
|
const result = await executor.execute(tx);
|
|
11282
12037
|
printOutput(result, getOpts());
|
|
@@ -11301,6 +12056,52 @@ function registerToken(parent, getOpts, makeExecutor2) {
|
|
|
11301
12056
|
});
|
|
11302
12057
|
printOutput({ token: tokenAddr, owner, spender: opts.spender, allowance }, getOpts());
|
|
11303
12058
|
});
|
|
12059
|
+
const WETH9_ABI = parseAbi34([
|
|
12060
|
+
"function deposit() payable",
|
|
12061
|
+
"function withdraw(uint256 amount)"
|
|
12062
|
+
]);
|
|
12063
|
+
token.command("wrap").description("Wrap native gas token into its ERC-20 wrapped form (WrappedNative.deposit())").requiredOption("--amount <amount>", "Amount of native token to wrap (in wei)").action(async (opts) => {
|
|
12064
|
+
const executor = makeExecutor2();
|
|
12065
|
+
const chainName = requireChain(parent, getOpts);
|
|
12066
|
+
if (!chainName) return;
|
|
12067
|
+
const registry = Registry.loadEmbedded();
|
|
12068
|
+
const chain = registry.getChain(chainName);
|
|
12069
|
+
if (!chain.wrapped_native) {
|
|
12070
|
+
printOutput({ error: `[${chainName}] no wrapped_native registered in chains.toml` }, getOpts());
|
|
12071
|
+
return;
|
|
12072
|
+
}
|
|
12073
|
+
const amount = BigInt(opts.amount);
|
|
12074
|
+
const data = encodeFunctionData30({ abi: WETH9_ABI, functionName: "deposit" });
|
|
12075
|
+
const result = await executor.execute({
|
|
12076
|
+
description: `[${chainName}] Wrap ${opts.amount} ${chain.native_token} \u2192 W${chain.native_token}`,
|
|
12077
|
+
to: chain.wrapped_native,
|
|
12078
|
+
data,
|
|
12079
|
+
value: amount,
|
|
12080
|
+
gas_estimate: 8e4
|
|
12081
|
+
});
|
|
12082
|
+
printOutput(result, getOpts());
|
|
12083
|
+
});
|
|
12084
|
+
token.command("unwrap").description("Unwrap wrapped-native ERC-20 back into native gas token (WrappedNative.withdraw(amount))").requiredOption("--amount <amount>", "Amount of wrapped token to unwrap (in wei)").action(async (opts) => {
|
|
12085
|
+
const executor = makeExecutor2();
|
|
12086
|
+
const chainName = requireChain(parent, getOpts);
|
|
12087
|
+
if (!chainName) return;
|
|
12088
|
+
const registry = Registry.loadEmbedded();
|
|
12089
|
+
const chain = registry.getChain(chainName);
|
|
12090
|
+
if (!chain.wrapped_native) {
|
|
12091
|
+
printOutput({ error: `[${chainName}] no wrapped_native registered in chains.toml` }, getOpts());
|
|
12092
|
+
return;
|
|
12093
|
+
}
|
|
12094
|
+
const amount = BigInt(opts.amount);
|
|
12095
|
+
const data = encodeFunctionData30({ abi: WETH9_ABI, functionName: "withdraw", args: [amount] });
|
|
12096
|
+
const result = await executor.execute({
|
|
12097
|
+
description: `[${chainName}] Unwrap ${opts.amount} W${chain.native_token} \u2192 ${chain.native_token}`,
|
|
12098
|
+
to: chain.wrapped_native,
|
|
12099
|
+
data,
|
|
12100
|
+
value: 0n,
|
|
12101
|
+
gas_estimate: 8e4
|
|
12102
|
+
});
|
|
12103
|
+
printOutput(result, getOpts());
|
|
12104
|
+
});
|
|
11304
12105
|
token.command("transfer").description("Transfer tokens to an address").requiredOption("--token <token>", "Token symbol or address").requiredOption("--to <address>", "Recipient address").requiredOption("--amount <amount>", "Amount to transfer (in wei)").action(async (opts) => {
|
|
11305
12106
|
const executor = makeExecutor2();
|
|
11306
12107
|
const chainName = requireChain(parent, getOpts);
|
|
@@ -11315,9 +12116,50 @@ function registerToken(parent, getOpts, makeExecutor2) {
|
|
|
11315
12116
|
|
|
11316
12117
|
// src/commands/bridge.ts
|
|
11317
12118
|
init_dist();
|
|
12119
|
+
function resolveDestExecutor(slug, broadcast) {
|
|
12120
|
+
const registry = Registry.loadEmbedded();
|
|
12121
|
+
try {
|
|
12122
|
+
const c = registry.getChain(slug);
|
|
12123
|
+
return new Executor(broadcast, c.effectiveRpcUrl(), c.explorer_url, c.viemChain());
|
|
12124
|
+
} catch {
|
|
12125
|
+
const envKey = `${slug.toUpperCase()}_RPC_URL`;
|
|
12126
|
+
const envVal = process.env[envKey];
|
|
12127
|
+
const meta = DEST_CHAIN_META[slug];
|
|
12128
|
+
if (!meta) throw new Error(`Cannot resolve destination chain: ${slug}`);
|
|
12129
|
+
const rpc = envVal ?? DEST_RPC_FALLBACKS[slug];
|
|
12130
|
+
if (!rpc) throw new Error(`No RPC URL for ${slug}. Set ${envKey} or use a registered chain.`);
|
|
12131
|
+
const minimal = {
|
|
12132
|
+
id: meta.chain_id,
|
|
12133
|
+
name: meta.name,
|
|
12134
|
+
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
|
|
12135
|
+
rpcUrls: { default: { http: [rpc] } }
|
|
12136
|
+
};
|
|
12137
|
+
return new Executor(broadcast, rpc, void 0, minimal);
|
|
12138
|
+
}
|
|
12139
|
+
}
|
|
12140
|
+
async function pollCctpAttestation(srcDomain, burnTxHash, maxSeconds) {
|
|
12141
|
+
const intervalMs = 5e3;
|
|
12142
|
+
const deadline = Date.now() + maxSeconds * 1e3;
|
|
12143
|
+
while (Date.now() < deadline) {
|
|
12144
|
+
try {
|
|
12145
|
+
const res = await fetch(`https://iris-api.circle.com/v2/messages/${srcDomain}?transactionHash=${burnTxHash}`);
|
|
12146
|
+
if (res.ok) {
|
|
12147
|
+
const data = await res.json();
|
|
12148
|
+
const m = data.messages?.[0];
|
|
12149
|
+
if (m && m.status === "complete" && m.attestation && m.attestation !== "PENDING") {
|
|
12150
|
+
return { message: m.message, attestation: m.attestation };
|
|
12151
|
+
}
|
|
12152
|
+
}
|
|
12153
|
+
} catch {
|
|
12154
|
+
}
|
|
12155
|
+
await new Promise((resolve5) => setTimeout(resolve5, intervalMs));
|
|
12156
|
+
}
|
|
12157
|
+
throw new Error(`CCTP attestation timeout after ${maxSeconds}s for tx ${burnTxHash}`);
|
|
12158
|
+
}
|
|
11318
12159
|
var LIFI_API = "https://li.quest/v1";
|
|
11319
12160
|
var DLN_API = "https://dln.debridge.finance/v1.0/dln/order";
|
|
11320
12161
|
var CCTP_FEE_API = "https://iris-api.circle.com/v2/burn/USDC/fees";
|
|
12162
|
+
var RELAY_API = "https://api.relay.link";
|
|
11321
12163
|
var DEST_CHAIN_META = {
|
|
11322
12164
|
ethereum: { chain_id: 1, name: "Ethereum" },
|
|
11323
12165
|
optimism: { chain_id: 10, name: "Optimism" },
|
|
@@ -11389,6 +12231,63 @@ async function getDebridgeQuote(srcChainId, dstChainId, srcToken, dstToken, amou
|
|
|
11389
12231
|
raw: createData
|
|
11390
12232
|
};
|
|
11391
12233
|
}
|
|
12234
|
+
async function getRelayBridgeQuote(srcChainId, dstChainId, srcToken, dstToken, amountRaw, user, recipient) {
|
|
12235
|
+
const body = {
|
|
12236
|
+
user,
|
|
12237
|
+
recipient,
|
|
12238
|
+
originChainId: srcChainId,
|
|
12239
|
+
destinationChainId: dstChainId,
|
|
12240
|
+
originCurrency: srcToken,
|
|
12241
|
+
destinationCurrency: dstToken,
|
|
12242
|
+
tradeType: "EXACT_INPUT",
|
|
12243
|
+
amount: amountRaw
|
|
12244
|
+
};
|
|
12245
|
+
const res = await fetch(`${RELAY_API}/quote`, {
|
|
12246
|
+
method: "POST",
|
|
12247
|
+
headers: { "content-type": "application/json" },
|
|
12248
|
+
body: JSON.stringify(body)
|
|
12249
|
+
});
|
|
12250
|
+
if (!res.ok) {
|
|
12251
|
+
const text = await res.text();
|
|
12252
|
+
let code = "";
|
|
12253
|
+
let msg = text;
|
|
12254
|
+
try {
|
|
12255
|
+
const j = JSON.parse(text);
|
|
12256
|
+
code = String(j.errorCode ?? j.code ?? "");
|
|
12257
|
+
msg = String(j.message ?? text);
|
|
12258
|
+
} catch {
|
|
12259
|
+
}
|
|
12260
|
+
const hints = {
|
|
12261
|
+
AMOUNT_TOO_LOW: "Try a larger amount or use --provider lifi (Relay enforces a per-route fee floor)",
|
|
12262
|
+
INVALID_INPUT_CURRENCY: "Source token unsupported by Relay on this chain \u2014 try --provider lifi",
|
|
12263
|
+
INVALID_OUTPUT_CURRENCY: "Destination token unsupported by Relay on this chain \u2014 try --provider lifi or change --token",
|
|
12264
|
+
NO_QUOTES: "No Relay liquidity for this route right now \u2014 try --provider lifi or retry later",
|
|
12265
|
+
AMOUNT_TOO_HIGH: "Amount exceeds Relay per-tx cap \u2014 split into smaller amounts",
|
|
12266
|
+
UNSUPPORTED_CHAIN: "Relay does not support this origin/destination chain \u2014 use --provider lifi"
|
|
12267
|
+
};
|
|
12268
|
+
const hint = hints[code];
|
|
12269
|
+
const detail = code ? `${code}: ${msg}` : msg;
|
|
12270
|
+
throw new Error(`Relay quote failed (${res.status}): ${detail}${hint ? ` \u2014 ${hint}` : ""}`);
|
|
12271
|
+
}
|
|
12272
|
+
const json = await res.json();
|
|
12273
|
+
const steps = json.steps;
|
|
12274
|
+
const swapStep = steps?.find((s) => s.id !== "approve") ?? steps?.[steps?.length ?? 1 - 1];
|
|
12275
|
+
const items = swapStep?.items;
|
|
12276
|
+
const txData = items?.[0]?.data;
|
|
12277
|
+
if (!txData) throw new Error("Relay: no executable step in cross-chain quote");
|
|
12278
|
+
const details = json.details;
|
|
12279
|
+
const currencyOut = details?.currencyOut;
|
|
12280
|
+
const timeEst = details?.timeEstimate ?? 30;
|
|
12281
|
+
return {
|
|
12282
|
+
to: String(txData.to),
|
|
12283
|
+
data: String(txData.data),
|
|
12284
|
+
value: String(txData.value ?? "0x0"),
|
|
12285
|
+
amountOut: String(currencyOut?.amount ?? "0"),
|
|
12286
|
+
estimatedTime: timeEst,
|
|
12287
|
+
tool: String(swapStep?.id ?? "relay"),
|
|
12288
|
+
raw: json
|
|
12289
|
+
};
|
|
12290
|
+
}
|
|
11392
12291
|
var CCTP_DOMAINS = {
|
|
11393
12292
|
ethereum: 0,
|
|
11394
12293
|
avalanche: 1,
|
|
@@ -11401,6 +12300,16 @@ var CCTP_DOMAINS = {
|
|
|
11401
12300
|
aptos: 9
|
|
11402
12301
|
};
|
|
11403
12302
|
var CCTP_TOKEN_MESSENGER_V2 = "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d";
|
|
12303
|
+
var CCTP_MESSAGE_TRANSMITTER_V2 = "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64";
|
|
12304
|
+
var DEST_RPC_FALLBACKS = {
|
|
12305
|
+
ethereum: "https://eth.merkle.io",
|
|
12306
|
+
arbitrum: "https://arbitrum.drpc.org",
|
|
12307
|
+
optimism: "https://optimism.drpc.org",
|
|
12308
|
+
polygon: "https://polygon.drpc.org",
|
|
12309
|
+
avalanche: "https://avalanche.drpc.org",
|
|
12310
|
+
linea: "https://linea.drpc.org",
|
|
12311
|
+
zksync: "https://zksync.drpc.org"
|
|
12312
|
+
};
|
|
11404
12313
|
var CCTP_USDC_ADDRESSES = {
|
|
11405
12314
|
ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
11406
12315
|
avalanche: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
@@ -11444,8 +12353,8 @@ async function getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc) {
|
|
|
11444
12353
|
}
|
|
11445
12354
|
return { fee: 0.25, maxFeeSubunits: 250000n };
|
|
11446
12355
|
}
|
|
11447
|
-
function registerBridge(parent, getOpts) {
|
|
11448
|
-
parent.command("bridge").description("Cross-chain bridge: move assets between chains").requiredOption("--token <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount in wei").requiredOption("--to-chain <chain>", "Destination chain name").option("--recipient <address>", "Recipient address on destination chain").option("--slippage <bps>", "Slippage in bps (LI.FI only)", "50").option("--provider <name>", "Bridge provider: lifi, debridge, cctp", "lifi").action(async (opts) => {
|
|
12356
|
+
function registerBridge(parent, getOpts, makeExecutor2) {
|
|
12357
|
+
parent.command("bridge").description("Cross-chain bridge: move assets between chains").requiredOption("--token <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount in wei").requiredOption("--to-chain <chain>", "Destination chain name").option("--recipient <address>", "Recipient address on destination chain").option("--slippage <bps>", "Slippage in bps (LI.FI only)", "50").option("--provider <name>", "Bridge provider: lifi, relay, debridge, cctp", "lifi").option("--auto-receive", "(CCTP only) After burn, poll Circle attestation and auto-call MessageTransmitter.receiveMessage on destination", false).option("--receive-timeout <seconds>", "(CCTP --auto-receive) max seconds to wait for attestation", "1200").action(async (opts) => {
|
|
11449
12358
|
const chainName = requireChain(parent, getOpts);
|
|
11450
12359
|
if (!chainName) return;
|
|
11451
12360
|
const registry = Registry.loadEmbedded();
|
|
@@ -11458,8 +12367,55 @@ function registerBridge(parent, getOpts) {
|
|
|
11458
12367
|
return;
|
|
11459
12368
|
}
|
|
11460
12369
|
const tokenAddr = opts.token.startsWith("0x") ? opts.token : registry.resolveToken(chainName, opts.token).address;
|
|
12370
|
+
let dstTokenAddr = tokenAddr;
|
|
12371
|
+
if (!opts.token.startsWith("0x")) {
|
|
12372
|
+
try {
|
|
12373
|
+
dstTokenAddr = registry.resolveToken(opts.toChain, opts.token).address;
|
|
12374
|
+
} catch {
|
|
12375
|
+
}
|
|
12376
|
+
}
|
|
11461
12377
|
const recipient = resolveWallet(opts.recipient);
|
|
11462
12378
|
const provider = opts.provider.toLowerCase();
|
|
12379
|
+
if (provider === "relay") {
|
|
12380
|
+
try {
|
|
12381
|
+
const result = await getRelayBridgeQuote(
|
|
12382
|
+
fromChain.chain_id,
|
|
12383
|
+
toChain.chain_id,
|
|
12384
|
+
tokenAddr,
|
|
12385
|
+
dstTokenAddr,
|
|
12386
|
+
opts.amount,
|
|
12387
|
+
recipient,
|
|
12388
|
+
recipient
|
|
12389
|
+
);
|
|
12390
|
+
const isNative = tokenAddr.toLowerCase() === "0x0000000000000000000000000000000000000000";
|
|
12391
|
+
const executor = makeExecutor2();
|
|
12392
|
+
const approvals = isNative ? [] : [{
|
|
12393
|
+
token: tokenAddr,
|
|
12394
|
+
spender: result.to,
|
|
12395
|
+
amount: BigInt(opts.amount)
|
|
12396
|
+
}];
|
|
12397
|
+
const action = await executor.execute({
|
|
12398
|
+
to: result.to,
|
|
12399
|
+
data: result.data,
|
|
12400
|
+
value: BigInt(result.value || "0"),
|
|
12401
|
+
description: `Relay bridge ${fromChain.name} \u2192 ${toChain.name}`,
|
|
12402
|
+
approvals
|
|
12403
|
+
});
|
|
12404
|
+
printOutput({
|
|
12405
|
+
from_chain: fromChain.name,
|
|
12406
|
+
to_chain: toChain.name,
|
|
12407
|
+
token: tokenAddr,
|
|
12408
|
+
amount: opts.amount,
|
|
12409
|
+
bridge: `Relay (${result.tool})`,
|
|
12410
|
+
estimated_output: result.amountOut,
|
|
12411
|
+
estimated_time_seconds: result.estimatedTime,
|
|
12412
|
+
action
|
|
12413
|
+
}, getOpts());
|
|
12414
|
+
} catch (e) {
|
|
12415
|
+
printOutput({ error: `Relay API error: ${errMsg(e)}` }, getOpts());
|
|
12416
|
+
}
|
|
12417
|
+
return;
|
|
12418
|
+
}
|
|
11463
12419
|
if (provider === "debridge") {
|
|
11464
12420
|
try {
|
|
11465
12421
|
const srcId = DLN_CHAIN_IDS[chainName] ?? fromChain.chain_id;
|
|
@@ -11468,11 +12424,29 @@ function registerBridge(parent, getOpts) {
|
|
|
11468
12424
|
srcId,
|
|
11469
12425
|
dstId,
|
|
11470
12426
|
tokenAddr,
|
|
11471
|
-
|
|
12427
|
+
dstTokenAddr,
|
|
11472
12428
|
opts.amount,
|
|
11473
12429
|
recipient
|
|
11474
12430
|
);
|
|
11475
12431
|
const tx = result.raw.tx;
|
|
12432
|
+
if (!tx?.to || !tx?.data) {
|
|
12433
|
+
printOutput({ error: "deBridge: API did not return a tx envelope" }, getOpts());
|
|
12434
|
+
return;
|
|
12435
|
+
}
|
|
12436
|
+
const isNative = tokenAddr.toLowerCase() === "0x0000000000000000000000000000000000000000";
|
|
12437
|
+
const executor = makeExecutor2();
|
|
12438
|
+
const approvals = isNative ? [] : [{
|
|
12439
|
+
token: tokenAddr,
|
|
12440
|
+
spender: tx.to,
|
|
12441
|
+
amount: BigInt(opts.amount)
|
|
12442
|
+
}];
|
|
12443
|
+
const action = await executor.execute({
|
|
12444
|
+
to: tx.to,
|
|
12445
|
+
data: tx.data,
|
|
12446
|
+
value: BigInt(tx.value ?? "0"),
|
|
12447
|
+
description: `deBridge DLN ${fromChain.name} \u2192 ${toChain.name}`,
|
|
12448
|
+
approvals
|
|
12449
|
+
});
|
|
11476
12450
|
printOutput({
|
|
11477
12451
|
from_chain: fromChain.name,
|
|
11478
12452
|
to_chain: toChain.name,
|
|
@@ -11481,7 +12455,7 @@ function registerBridge(parent, getOpts) {
|
|
|
11481
12455
|
bridge: "deBridge DLN",
|
|
11482
12456
|
estimated_output: result.amountOut,
|
|
11483
12457
|
estimated_time_seconds: result.estimatedTime,
|
|
11484
|
-
|
|
12458
|
+
action
|
|
11485
12459
|
}, getOpts());
|
|
11486
12460
|
} catch (e) {
|
|
11487
12461
|
printOutput({ error: `deBridge API error: ${errMsg(e)}` }, getOpts());
|
|
@@ -11514,11 +12488,11 @@ function registerBridge(parent, getOpts) {
|
|
|
11514
12488
|
return;
|
|
11515
12489
|
}
|
|
11516
12490
|
const recipientPadded = `0x${"0".repeat(24)}${recipient.replace("0x", "").toLowerCase()}`;
|
|
11517
|
-
const { encodeFunctionData:
|
|
11518
|
-
const tokenMessengerAbi =
|
|
12491
|
+
const { encodeFunctionData: encodeFunctionData31, parseAbi: parseAbi35 } = await import("viem");
|
|
12492
|
+
const tokenMessengerAbi = parseAbi35([
|
|
11519
12493
|
"function depositForBurn(uint256 amount, uint32 destinationDomain, bytes32 mintRecipient, address burnToken, bytes32 destinationCaller, uint256 maxFee, uint32 minFinalityThreshold) external returns (uint64 nonce)"
|
|
11520
12494
|
]);
|
|
11521
|
-
const data =
|
|
12495
|
+
const data = encodeFunctionData31({
|
|
11522
12496
|
abi: tokenMessengerAbi,
|
|
11523
12497
|
functionName: "depositForBurn",
|
|
11524
12498
|
args: [
|
|
@@ -11533,6 +12507,48 @@ function registerBridge(parent, getOpts) {
|
|
|
11533
12507
|
// standard finality
|
|
11534
12508
|
]
|
|
11535
12509
|
});
|
|
12510
|
+
const executor = makeExecutor2();
|
|
12511
|
+
const burnAction = await executor.execute({
|
|
12512
|
+
to: CCTP_TOKEN_MESSENGER_V2,
|
|
12513
|
+
data,
|
|
12514
|
+
value: 0n,
|
|
12515
|
+
description: `CCTP burn ${fromChain.name} \u2192 ${toChain.name}`,
|
|
12516
|
+
approvals: [{
|
|
12517
|
+
token: usdcSrc,
|
|
12518
|
+
spender: CCTP_TOKEN_MESSENGER_V2,
|
|
12519
|
+
amount: BigInt(opts.amount)
|
|
12520
|
+
}]
|
|
12521
|
+
});
|
|
12522
|
+
let receiveAction = void 0;
|
|
12523
|
+
if (opts.autoReceive && burnAction.tx_hash && burnAction.status === "confirmed") {
|
|
12524
|
+
try {
|
|
12525
|
+
const timeoutSec = parseInt(opts.receiveTimeout) || 1200;
|
|
12526
|
+
process.stderr.write(`Polling Circle attestation for ${burnAction.tx_hash} (max ${timeoutSec}s)...
|
|
12527
|
+
`);
|
|
12528
|
+
const { message, attestation } = await pollCctpAttestation(srcDomain, burnAction.tx_hash, timeoutSec);
|
|
12529
|
+
process.stderr.write(`Attestation ready. Calling receiveMessage on ${opts.toChain}...
|
|
12530
|
+
`);
|
|
12531
|
+
const { encodeFunctionData: encReceive, parseAbi: parseAbiReceive } = await import("viem");
|
|
12532
|
+
const receiveAbi = parseAbiReceive([
|
|
12533
|
+
"function receiveMessage(bytes message, bytes attestation) external"
|
|
12534
|
+
]);
|
|
12535
|
+
const receiveData = encReceive({
|
|
12536
|
+
abi: receiveAbi,
|
|
12537
|
+
functionName: "receiveMessage",
|
|
12538
|
+
args: [message, attestation]
|
|
12539
|
+
});
|
|
12540
|
+
const destExecutor = resolveDestExecutor(opts.toChain, !!parent.opts().broadcast);
|
|
12541
|
+
receiveAction = await destExecutor.execute({
|
|
12542
|
+
to: CCTP_MESSAGE_TRANSMITTER_V2,
|
|
12543
|
+
data: receiveData,
|
|
12544
|
+
value: 0n,
|
|
12545
|
+
description: `CCTP receive on ${toChain.name}`,
|
|
12546
|
+
approvals: []
|
|
12547
|
+
});
|
|
12548
|
+
} catch (e) {
|
|
12549
|
+
receiveAction = { error: `auto-receive failed: ${errMsg(e)}` };
|
|
12550
|
+
}
|
|
12551
|
+
}
|
|
11536
12552
|
printOutput({
|
|
11537
12553
|
from_chain: fromChain.name,
|
|
11538
12554
|
to_chain: toChain.name,
|
|
@@ -11542,12 +12558,9 @@ function registerBridge(parent, getOpts) {
|
|
|
11542
12558
|
bridge: "Circle CCTP V2",
|
|
11543
12559
|
estimated_fee_usdc: fee,
|
|
11544
12560
|
estimated_output: String(BigInt(opts.amount) - maxFeeSubunits),
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
data,
|
|
11549
|
-
value: "0x0"
|
|
11550
|
-
}
|
|
12561
|
+
burn: burnAction,
|
|
12562
|
+
receive: receiveAction,
|
|
12563
|
+
note: opts.autoReceive ? void 0 : "Pass --auto-receive to poll Circle attestation and auto-call MessageTransmitter.receiveMessage on the destination chain."
|
|
11551
12564
|
}, getOpts());
|
|
11552
12565
|
} catch (e) {
|
|
11553
12566
|
printOutput({ error: `CCTP error: ${errMsg(e)}` }, getOpts());
|
|
@@ -11559,26 +12572,41 @@ function registerBridge(parent, getOpts) {
|
|
|
11559
12572
|
fromChain: String(fromChain.chain_id),
|
|
11560
12573
|
toChain: String(toChain.chain_id),
|
|
11561
12574
|
fromToken: tokenAddr,
|
|
11562
|
-
toToken:
|
|
12575
|
+
toToken: dstTokenAddr,
|
|
11563
12576
|
fromAmount: opts.amount,
|
|
11564
12577
|
fromAddress: recipient,
|
|
11565
12578
|
slippage: String(parseInt(opts.slippage) / 1e4)
|
|
11566
12579
|
});
|
|
11567
12580
|
const res = await fetch(`${LIFI_API}/quote?${params}`);
|
|
11568
12581
|
const quote = await res.json();
|
|
11569
|
-
if (quote.transactionRequest) {
|
|
11570
|
-
printOutput({
|
|
11571
|
-
from_chain: fromChain.name,
|
|
11572
|
-
to_chain: toChain.name,
|
|
11573
|
-
token: tokenAddr,
|
|
11574
|
-
amount: opts.amount,
|
|
11575
|
-
bridge: quote.toolDetails?.name ?? "LI.FI",
|
|
11576
|
-
estimated_output: quote.estimate?.toAmount,
|
|
11577
|
-
tx: { to: quote.transactionRequest.to, data: quote.transactionRequest.data, value: quote.transactionRequest.value }
|
|
11578
|
-
}, getOpts());
|
|
11579
|
-
} else {
|
|
12582
|
+
if (!quote.transactionRequest) {
|
|
11580
12583
|
printOutput({ error: "No LI.FI route found", details: quote }, getOpts());
|
|
12584
|
+
return;
|
|
11581
12585
|
}
|
|
12586
|
+
const isNative = tokenAddr.toLowerCase() === "0x0000000000000000000000000000000000000000";
|
|
12587
|
+
const spender = quote.estimate?.approvalAddress ?? quote.transactionRequest.to;
|
|
12588
|
+
const executor = makeExecutor2();
|
|
12589
|
+
const approvals = isNative ? [] : [{
|
|
12590
|
+
token: tokenAddr,
|
|
12591
|
+
spender,
|
|
12592
|
+
amount: BigInt(opts.amount)
|
|
12593
|
+
}];
|
|
12594
|
+
const action = await executor.execute({
|
|
12595
|
+
to: quote.transactionRequest.to,
|
|
12596
|
+
data: quote.transactionRequest.data,
|
|
12597
|
+
value: BigInt(quote.transactionRequest.value ?? "0"),
|
|
12598
|
+
description: `LI.FI ${fromChain.name} \u2192 ${toChain.name}`,
|
|
12599
|
+
approvals
|
|
12600
|
+
});
|
|
12601
|
+
printOutput({
|
|
12602
|
+
from_chain: fromChain.name,
|
|
12603
|
+
to_chain: toChain.name,
|
|
12604
|
+
token: tokenAddr,
|
|
12605
|
+
amount: opts.amount,
|
|
12606
|
+
bridge: quote.toolDetails?.name ?? "LI.FI",
|
|
12607
|
+
estimated_output: quote.estimate?.toAmount,
|
|
12608
|
+
action
|
|
12609
|
+
}, getOpts());
|
|
11582
12610
|
} catch (e) {
|
|
11583
12611
|
printOutput({ error: `LI.FI API error: ${errMsg(e)}` }, getOpts());
|
|
11584
12612
|
}
|
|
@@ -11666,7 +12694,7 @@ async function lifiQuote(chainId, fromToken, toToken, fromAmount, fromAddress, s
|
|
|
11666
12694
|
outAmount: String(estimate?.toAmount ?? "0")
|
|
11667
12695
|
};
|
|
11668
12696
|
}
|
|
11669
|
-
var
|
|
12697
|
+
var RELAY_API2 = "https://api.relay.link";
|
|
11670
12698
|
async function relayQuote(chainId, fromToken, toToken, amount, user) {
|
|
11671
12699
|
const body = {
|
|
11672
12700
|
user,
|
|
@@ -11678,7 +12706,7 @@ async function relayQuote(chainId, fromToken, toToken, amount, user) {
|
|
|
11678
12706
|
tradeType: "EXACT_INPUT",
|
|
11679
12707
|
amount
|
|
11680
12708
|
};
|
|
11681
|
-
const res = await fetch(`${
|
|
12709
|
+
const res = await fetch(`${RELAY_API2}/quote`, {
|
|
11682
12710
|
method: "POST",
|
|
11683
12711
|
headers: { "content-type": "application/json" },
|
|
11684
12712
|
body: JSON.stringify(body)
|
|
@@ -11718,7 +12746,7 @@ async function liquidSwapRoute(tokenIn, tokenOut, amountIn, slippagePct) {
|
|
|
11718
12746
|
};
|
|
11719
12747
|
}
|
|
11720
12748
|
function registerSwap(parent, getOpts, makeExecutor2) {
|
|
11721
|
-
parent.command("swap").description("Swap tokens via DEX aggregator (KyberSwap, OpenOcean, LiquidSwap, LI.FI, Relay)").requiredOption("--from <token>", "Input token symbol or address").requiredOption("--to <token>", "Output token symbol or address").requiredOption("--amount <amount>", "Amount of input token in wei").option("--provider <name>", "Aggregator: kyber, openocean, liquid, lifi, relay", "kyber").option("--slippage <bps>", "Slippage tolerance in bps", "
|
|
12749
|
+
parent.command("swap").description("Swap tokens via DEX aggregator (KyberSwap, OpenOcean, LiquidSwap, LI.FI, Relay)").requiredOption("--from <token>", "Input token symbol or address").requiredOption("--to <token>", "Output token symbol or address").requiredOption("--amount <amount>", "Amount of input token in wei").option("--provider <name>", "Aggregator: kyber, openocean, liquid, lifi, relay", "kyber").option("--slippage <bps>", "Slippage tolerance in bps", "100").action(async (opts) => {
|
|
11722
12750
|
const executor = makeExecutor2();
|
|
11723
12751
|
const chainName = requireChain(parent, getOpts);
|
|
11724
12752
|
if (!chainName) return;
|
|
@@ -11747,12 +12775,14 @@ function registerSwap(parent, getOpts, makeExecutor2) {
|
|
|
11747
12775
|
wallet,
|
|
11748
12776
|
slippageBps
|
|
11749
12777
|
);
|
|
12778
|
+
const fromLowerKyber = fromAddr.toLowerCase();
|
|
12779
|
+
const isNativeInputKyber = fromLowerKyber === "0x0000000000000000000000000000000000000000" || fromLowerKyber === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
|
11750
12780
|
const tx = {
|
|
11751
12781
|
description: `KyberSwap: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
|
|
11752
12782
|
to: txData.to,
|
|
11753
12783
|
data: txData.data,
|
|
11754
|
-
value: parseBigIntValue(txData.value),
|
|
11755
|
-
approvals: [{ token: fromAddr, spender: txData.to, amount: BigInt(opts.amount) }]
|
|
12784
|
+
value: isNativeInputKyber ? BigInt(opts.amount) : parseBigIntValue(txData.value),
|
|
12785
|
+
...isNativeInputKyber ? {} : { approvals: [{ token: fromAddr, spender: txData.to, amount: BigInt(opts.amount) }] }
|
|
11756
12786
|
};
|
|
11757
12787
|
const result = await executor.execute(tx);
|
|
11758
12788
|
printOutput({
|
|
@@ -12319,7 +13349,8 @@ function makeExecutor() {
|
|
|
12319
13349
|
process.exit(1);
|
|
12320
13350
|
}
|
|
12321
13351
|
const chain = registry.getChain(opts.chain);
|
|
12322
|
-
|
|
13352
|
+
const viemChain = chain.viemChain();
|
|
13353
|
+
return new Executor(!!opts.broadcast, chain.effectiveRpcUrl(), chain.explorer_url, viemChain);
|
|
12323
13354
|
}
|
|
12324
13355
|
registerStatus(program, getOutputMode);
|
|
12325
13356
|
registerSchema(program, getOutputMode);
|
|
@@ -12330,7 +13361,7 @@ registerPortfolio(program, getOutputMode);
|
|
|
12330
13361
|
registerPrice(program, getOutputMode);
|
|
12331
13362
|
registerWallet(program, getOutputMode);
|
|
12332
13363
|
registerToken(program, getOutputMode, makeExecutor);
|
|
12333
|
-
registerBridge(program, getOutputMode);
|
|
13364
|
+
registerBridge(program, getOutputMode, makeExecutor);
|
|
12334
13365
|
registerSwap(program, getOutputMode, makeExecutor);
|
|
12335
13366
|
registerSetup(program);
|
|
12336
13367
|
registerOws(program, getOutputMode);
|