@hypurrquant/defi-cli 1.0.11 → 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 +1590 -309
- package/dist/index.js.map +1 -1
- package/dist/main.js +1595 -314
- package/dist/main.js.map +1 -1
- package/dist/mcp-server.js +810 -196
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/skills/defi-cli/SKILL.md +54 -5
- package/skills/defi-cli/references/commands.md +41 -8
- package/skills/defi-cli/references/protocols.md +2 -2
- package/skills/defi-cli/scripts/bridge-quote.sh +34 -0
- package/skills/defi-cli/scripts/lending-supply-flow.sh +28 -0
- package/skills/defi-cli/scripts/lp-claim-all.sh +25 -0
- package/skills/defi-cli/scripts/lp-emission-discover.sh +20 -0
- package/skills/defi-cli/scripts/portfolio-snapshot.sh +0 -0
- package/skills/defi-cli/scripts/preflight.sh +0 -0
- package/skills/defi-cli/scripts/swap-quote.sh +37 -0
- package/skills/defi-cli/scripts/wallet-status.sh +30 -0
- package/skills/defi-cli/scripts/yield-scan.sh +0 -0
- package/skills/defi-cli/scripts/exploit-scan.sh +0 -10
package/dist/mcp-server.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
#!/usr/bin/env node
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
4
|
var __esm = (fn, res) => function __init() {
|
|
@@ -111,11 +110,15 @@ function buildTransfer(token, to, amount) {
|
|
|
111
110
|
gas_estimate: 65e3
|
|
112
111
|
};
|
|
113
112
|
}
|
|
114
|
-
function getProvider(rpcUrl) {
|
|
115
|
-
const
|
|
113
|
+
function getProvider(rpcUrl, chain) {
|
|
114
|
+
const key = chain ? `${rpcUrl}@${chain.id}` : rpcUrl;
|
|
115
|
+
const cached = providerCache.get(key);
|
|
116
116
|
if (cached) return cached;
|
|
117
|
-
const client = createPublicClient({
|
|
118
|
-
|
|
117
|
+
const client = createPublicClient({
|
|
118
|
+
transport: http(rpcUrl),
|
|
119
|
+
...chain ? { chain } : {}
|
|
120
|
+
});
|
|
121
|
+
providerCache.set(key, client);
|
|
119
122
|
return client;
|
|
120
123
|
}
|
|
121
124
|
function clearProviderCache() {
|
|
@@ -341,6 +344,28 @@ var init_dist = __esm({
|
|
|
341
344
|
const chainEnv = this.name.toUpperCase().replace(/ /g, "_") + "_RPC_URL";
|
|
342
345
|
return process.env[chainEnv] ?? this.rpc_url;
|
|
343
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* Build a viem Chain object pinned to this config so wallet/public clients
|
|
349
|
+
* can sign with an explicit chainId rather than auto-fetching it from the
|
|
350
|
+
* RPC. SSOT 7.4: anchoring chainId at client-construction time defends
|
|
351
|
+
* against an MITM RPC that returns the wrong eth_chainId, and keeps
|
|
352
|
+
* offline signing safe against RPC drift.
|
|
353
|
+
*/
|
|
354
|
+
viemChain() {
|
|
355
|
+
const rpcUrl = this.effectiveRpcUrl();
|
|
356
|
+
return {
|
|
357
|
+
id: this.chain_id,
|
|
358
|
+
name: this.name,
|
|
359
|
+
nativeCurrency: {
|
|
360
|
+
name: this.native_token,
|
|
361
|
+
symbol: this.native_token,
|
|
362
|
+
decimals: 18
|
|
363
|
+
},
|
|
364
|
+
rpcUrls: { default: { http: [rpcUrl] } },
|
|
365
|
+
...this.explorer_url ? { blockExplorers: { default: { name: this.name, url: this.explorer_url } } } : {},
|
|
366
|
+
...this.multicall3 ? { contracts: { multicall3: { address: this.multicall3 } } } : {}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
344
369
|
};
|
|
345
370
|
ProtocolCategory = /* @__PURE__ */ ((ProtocolCategory2) => {
|
|
346
371
|
ProtocolCategory2["Dex"] = "dex";
|
|
@@ -532,13 +557,13 @@ __export(dist_exports2, {
|
|
|
532
557
|
import { encodeFunctionData as encodeFunctionData3, parseAbi as parseAbi3, createPublicClient as createPublicClient2, http as http2, decodeAbiParameters } from "viem";
|
|
533
558
|
import { encodeFunctionData as encodeFunctionData22, parseAbi as parseAbi22, createPublicClient as createPublicClient22, http as http22, decodeFunctionResult as decodeFunctionResult2, decodeAbiParameters as decodeAbiParameters2 } from "viem";
|
|
534
559
|
import { encodeFunctionData as encodeFunctionData32, parseAbi as parseAbi32, createPublicClient as createPublicClient3, http as http3, decodeAbiParameters as decodeAbiParameters3, concatHex, zeroAddress } from "viem";
|
|
535
|
-
import { encodeFunctionData as encodeFunctionData4, parseAbi as parseAbi4
|
|
560
|
+
import { encodeFunctionData as encodeFunctionData4, parseAbi as parseAbi4 } from "viem";
|
|
536
561
|
import { encodeFunctionData as encodeFunctionData5, parseAbi as parseAbi5 } from "viem";
|
|
537
562
|
import { encodeFunctionData as encodeFunctionData6, parseAbi as parseAbi6, decodeAbiParameters as decodeAbiParameters4 } from "viem";
|
|
538
|
-
import { encodeFunctionData as encodeFunctionData7, parseAbi as parseAbi7, createPublicClient as createPublicClient4, http as http4, zeroAddress as
|
|
539
|
-
import { createPublicClient as createPublicClient5, decodeFunctionResult as decodeFunctionResult22, encodeFunctionData as encodeFunctionData8, http as http5, parseAbi as parseAbi8, zeroAddress as
|
|
540
|
-
import { encodeFunctionData as encodeFunctionData9, parseAbi as parseAbi9, zeroAddress as
|
|
541
|
-
import { createPublicClient as createPublicClient6, decodeFunctionResult as decodeFunctionResult3, encodeFunctionData as encodeFunctionData10, http as http6, parseAbi as parseAbi10, zeroAddress as
|
|
563
|
+
import { encodeFunctionData as encodeFunctionData7, parseAbi as parseAbi7, createPublicClient as createPublicClient4, http as http4, zeroAddress as zeroAddress2 } from "viem";
|
|
564
|
+
import { createPublicClient as createPublicClient5, decodeFunctionResult as decodeFunctionResult22, encodeFunctionData as encodeFunctionData8, http as http5, parseAbi as parseAbi8, zeroAddress as zeroAddress3 } from "viem";
|
|
565
|
+
import { encodeFunctionData as encodeFunctionData9, parseAbi as parseAbi9, zeroAddress as zeroAddress4 } from "viem";
|
|
566
|
+
import { createPublicClient as createPublicClient6, decodeFunctionResult as decodeFunctionResult3, encodeFunctionData as encodeFunctionData10, http as http6, parseAbi as parseAbi10, zeroAddress as zeroAddress5 } from "viem";
|
|
542
567
|
import { encodeFunctionData as encodeFunctionData11, parseAbi as parseAbi11, createPublicClient as createPublicClient7, http as http7 } from "viem";
|
|
543
568
|
import {
|
|
544
569
|
encodeFunctionData as encodeFunctionData12,
|
|
@@ -556,24 +581,24 @@ import {
|
|
|
556
581
|
keccak256,
|
|
557
582
|
parseAbi as parseAbi13,
|
|
558
583
|
decodeFunctionResult as decodeFunctionResult5,
|
|
559
|
-
zeroAddress as
|
|
584
|
+
zeroAddress as zeroAddress6
|
|
560
585
|
} from "viem";
|
|
561
|
-
import { createPublicClient as createPublicClient10, http as http10, parseAbi as parseAbi14, encodeFunctionData as encodeFunctionData14, decodeFunctionResult as decodeFunctionResult6, zeroAddress as
|
|
562
|
-
import { createPublicClient as createPublicClient11, http as http11, parseAbi as parseAbi15, encodeFunctionData as encodeFunctionData15, zeroAddress as
|
|
586
|
+
import { createPublicClient as createPublicClient10, http as http10, parseAbi as parseAbi14, encodeFunctionData as encodeFunctionData14, decodeFunctionResult as decodeFunctionResult6, zeroAddress as zeroAddress7 } from "viem";
|
|
587
|
+
import { createPublicClient as createPublicClient11, http as http11, parseAbi as parseAbi15, encodeFunctionData as encodeFunctionData15, zeroAddress as zeroAddress8 } from "viem";
|
|
563
588
|
import { createPublicClient as createPublicClient12, http as http12, parseAbi as parseAbi16 } from "viem";
|
|
564
589
|
import { createPublicClient as createPublicClient13, http as http13, parseAbi as parseAbi17, encodeFunctionData as encodeFunctionData16 } from "viem";
|
|
565
590
|
import { createPublicClient as createPublicClient14, http as http14, parseAbi as parseAbi18, encodeFunctionData as encodeFunctionData17 } from "viem";
|
|
566
591
|
import { createPublicClient as createPublicClient15, http as http15, parseAbi as parseAbi19, encodeFunctionData as encodeFunctionData18 } from "viem";
|
|
567
|
-
import { parseAbi as parseAbi20, encodeFunctionData as encodeFunctionData19, decodeFunctionResult as decodeFunctionResult7, zeroAddress as
|
|
568
|
-
import { createPublicClient as
|
|
569
|
-
import { createPublicClient as
|
|
570
|
-
import { createPublicClient as
|
|
592
|
+
import { parseAbi as parseAbi20, encodeFunctionData as encodeFunctionData19, decodeFunctionResult as decodeFunctionResult7, zeroAddress as zeroAddress9, createPublicClient as createPublicClient16, http as http16 } from "viem";
|
|
593
|
+
import { createPublicClient as createPublicClient17, http as http17, parseAbi as parseAbi21, encodeFunctionData as encodeFunctionData20, zeroAddress as zeroAddress10 } from "viem";
|
|
594
|
+
import { createPublicClient as createPublicClient18, http as http18, parseAbi as parseAbi222 } from "viem";
|
|
595
|
+
import { createPublicClient as createPublicClient19, http as http19, parseAbi as parseAbi23, encodeFunctionData as encodeFunctionData21 } from "viem";
|
|
571
596
|
import { parseAbi as parseAbi24, encodeFunctionData as encodeFunctionData222 } from "viem";
|
|
572
|
-
import { createPublicClient as
|
|
573
|
-
import { createPublicClient as
|
|
597
|
+
import { createPublicClient as createPublicClient20, http as http20, parseAbi as parseAbi25, encodeFunctionData as encodeFunctionData23, zeroAddress as zeroAddress11 } from "viem";
|
|
598
|
+
import { createPublicClient as createPublicClient21, http as http21, parseAbi as parseAbi26, encodeFunctionData as encodeFunctionData24, zeroAddress as zeroAddress12 } from "viem";
|
|
574
599
|
import { parseAbi as parseAbi27, encodeFunctionData as encodeFunctionData25 } from "viem";
|
|
575
600
|
import { parseAbi as parseAbi28, encodeFunctionData as encodeFunctionData26 } from "viem";
|
|
576
|
-
import { createPublicClient as
|
|
601
|
+
import { createPublicClient as createPublicClient222, http as http222, parseAbi as parseAbi29 } from "viem";
|
|
577
602
|
function pctToTickDelta(pct) {
|
|
578
603
|
return Math.round(Math.log(1 + pct / 100) / Math.log(1.0001));
|
|
579
604
|
}
|
|
@@ -827,15 +852,6 @@ function u256ToF642(v) {
|
|
|
827
852
|
if (v > MAX_U128) return Infinity;
|
|
828
853
|
return Number(v);
|
|
829
854
|
}
|
|
830
|
-
function defaultMarketParams(loanToken = zeroAddress10) {
|
|
831
|
-
return {
|
|
832
|
-
loanToken,
|
|
833
|
-
collateralToken: zeroAddress10,
|
|
834
|
-
oracle: zeroAddress10,
|
|
835
|
-
irm: zeroAddress10,
|
|
836
|
-
lltv: 0n
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
855
|
function decodeMarket(data) {
|
|
840
856
|
if (!data) return null;
|
|
841
857
|
try {
|
|
@@ -1068,7 +1084,7 @@ function createKittenSwapFarming(entry, rpcUrl) {
|
|
|
1068
1084
|
const bonusRewardToken = entry.contracts?.["bonus_reward_token"];
|
|
1069
1085
|
return new KittenSwapFarmingAdapter(entry.name, farmingCenter, eternalFarming, positionManager, rpcUrl, factory, rewardToken, bonusRewardToken);
|
|
1070
1086
|
}
|
|
1071
|
-
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_ABI, INCENTIVES_ABI, REWARDS_CONTROLLER_ABI, POOL_PROVIDER_ABI, ADDRESSES_PROVIDER_ABI, ORACLE_ABI, ERC20_DECIMALS_ABI, AaveV3Adapter, POOL_ABI2, ERC20_ABI2, 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;
|
|
1087
|
+
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_ABI, INCENTIVES_ABI, REWARDS_CONTROLLER_ABI, POOL_PROVIDER_ABI, ADDRESSES_PROVIDER_ABI, ORACLE_ABI, ERC20_DECIMALS_ABI, AaveV3Adapter, POOL_ABI2, ERC20_ABI2, 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;
|
|
1072
1088
|
var init_dist2 = __esm({
|
|
1073
1089
|
"../defi-protocols/dist/index.js"() {
|
|
1074
1090
|
"use strict";
|
|
@@ -1136,6 +1152,10 @@ var init_dist2 = __esm({
|
|
|
1136
1152
|
"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; }",
|
|
1137
1153
|
"function mint(SlipstreamMintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
|
|
1138
1154
|
]);
|
|
1155
|
+
ramsesMintAbi = parseAbi3([
|
|
1156
|
+
"struct RamsesMintParams { address token0; address token1; int24 tickSpacing; int24 tickLower; int24 tickUpper; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; uint256 deadline; }",
|
|
1157
|
+
"function mint(RamsesMintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
|
|
1158
|
+
]);
|
|
1139
1159
|
UniswapV3Adapter = class {
|
|
1140
1160
|
protocolName;
|
|
1141
1161
|
router;
|
|
@@ -1145,6 +1165,7 @@ var init_dist2 = __esm({
|
|
|
1145
1165
|
fee;
|
|
1146
1166
|
rpcUrl;
|
|
1147
1167
|
useTickSpacingQuoter;
|
|
1168
|
+
clStyle;
|
|
1148
1169
|
constructor(entry, rpcUrl) {
|
|
1149
1170
|
this.protocolName = entry.name;
|
|
1150
1171
|
const router = entry.contracts?.["router"];
|
|
@@ -1157,6 +1178,7 @@ var init_dist2 = __esm({
|
|
|
1157
1178
|
this.factory = entry.contracts?.["factory"];
|
|
1158
1179
|
this.fee = DEFAULT_FEE;
|
|
1159
1180
|
this.rpcUrl = rpcUrl;
|
|
1181
|
+
this.clStyle = entry.cl_style;
|
|
1160
1182
|
this.useTickSpacingQuoter = entry.cl_style === "slipstream" || entry.cl_style === "ramses" || entry.contracts?.["pool_deployer"] !== void 0 || entry.contracts?.["gauge_factory"] !== void 0;
|
|
1161
1183
|
}
|
|
1162
1184
|
name() {
|
|
@@ -1164,7 +1186,15 @@ var init_dist2 = __esm({
|
|
|
1164
1186
|
}
|
|
1165
1187
|
async buildSwap(params) {
|
|
1166
1188
|
const deadline = BigInt(params.deadline ?? 18446744073709551615n);
|
|
1167
|
-
const amountOutMinimum =
|
|
1189
|
+
const amountOutMinimum = params.amount_out_min ?? applyMinSlippage(
|
|
1190
|
+
params.slippage,
|
|
1191
|
+
(await this.quote({
|
|
1192
|
+
protocol: this.protocolName,
|
|
1193
|
+
token_in: params.token_in,
|
|
1194
|
+
token_out: params.token_out,
|
|
1195
|
+
amount_in: params.amount_in
|
|
1196
|
+
})).amount_out
|
|
1197
|
+
);
|
|
1168
1198
|
const data = encodeFunctionData3({
|
|
1169
1199
|
abi: swapRouterAbi,
|
|
1170
1200
|
functionName: "exactInputSingle",
|
|
@@ -1330,31 +1360,37 @@ var init_dist2 = __esm({
|
|
|
1330
1360
|
if (!pm) {
|
|
1331
1361
|
throw new DefiError("CONTRACT_ERROR", "Position manager address not configured");
|
|
1332
1362
|
}
|
|
1333
|
-
const
|
|
1363
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
1364
|
+
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];
|
|
1334
1365
|
const amount0 = rawAmount0 === 0n && rawAmount1 > 0n ? 1n : rawAmount0;
|
|
1335
1366
|
const amount1 = rawAmount1 === 0n && rawAmount0 > 0n ? 1n : rawAmount1;
|
|
1367
|
+
const slippage = params.slippage ?? defaultSwapSlippage();
|
|
1368
|
+
const minA = params.amount_a_min ?? applyMinSlippage(slippage, params.amount_a);
|
|
1369
|
+
const minB = params.amount_b_min ?? applyMinSlippage(slippage, params.amount_b);
|
|
1370
|
+
const [amount0Min, amount1Min] = isAFirst ? [minA, minB] : [minB, minA];
|
|
1336
1371
|
let thirdField = this.fee;
|
|
1337
1372
|
let tickLower = -887220;
|
|
1338
1373
|
let tickUpper = 887220;
|
|
1339
1374
|
if (params.pool && this.rpcUrl) {
|
|
1340
1375
|
const poolAbi2 = parseAbi3([
|
|
1341
1376
|
"function fee() view returns (uint24)",
|
|
1342
|
-
"function tickSpacing() view returns (int24)"
|
|
1343
|
-
"function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16, uint16, uint16, bool)"
|
|
1377
|
+
"function tickSpacing() view returns (int24)"
|
|
1344
1378
|
]);
|
|
1345
1379
|
const client = createPublicClient2({ transport: http2(this.rpcUrl) });
|
|
1346
|
-
const [poolFee, poolTs,
|
|
1380
|
+
const [poolFee, poolTs, slot0Raw] = await Promise.all([
|
|
1347
1381
|
client.readContract({ address: params.pool, abi: poolAbi2, functionName: "fee" }).catch(() => null),
|
|
1348
1382
|
client.readContract({ address: params.pool, abi: poolAbi2, functionName: "tickSpacing" }).catch(() => null),
|
|
1349
|
-
client.
|
|
1383
|
+
client.call({ to: params.pool, data: "0x3850c7bd" }).then((r) => r.data ?? null).catch(() => null)
|
|
1350
1384
|
]);
|
|
1351
1385
|
if (this.useTickSpacingQuoter && poolTs !== null) {
|
|
1352
1386
|
thirdField = poolTs;
|
|
1353
1387
|
} else if (poolFee !== null) {
|
|
1354
1388
|
thirdField = poolFee;
|
|
1355
1389
|
}
|
|
1356
|
-
if (params.range_pct !== void 0 &&
|
|
1357
|
-
const
|
|
1390
|
+
if (params.range_pct !== void 0 && slot0Raw && poolTs !== null) {
|
|
1391
|
+
const tickWord = slot0Raw.slice(2 + 64, 2 + 128);
|
|
1392
|
+
const tickU = BigInt("0x" + tickWord);
|
|
1393
|
+
const currentTick = tickU >= 1n << 255n ? Number(tickU - (1n << 256n)) : Number(tickU);
|
|
1358
1394
|
const rangeTicks = Math.floor(params.range_pct * 100);
|
|
1359
1395
|
tickLower = Math.floor((currentTick - rangeTicks) / poolTs) * poolTs;
|
|
1360
1396
|
tickUpper = Math.ceil((currentTick + rangeTicks) / poolTs) * poolTs;
|
|
@@ -1362,7 +1398,25 @@ var init_dist2 = __esm({
|
|
|
1362
1398
|
}
|
|
1363
1399
|
if (params.tick_lower !== void 0) tickLower = params.tick_lower;
|
|
1364
1400
|
if (params.tick_upper !== void 0) tickUpper = params.tick_upper;
|
|
1365
|
-
const data = this.
|
|
1401
|
+
const data = this.clStyle === "ramses" ? encodeFunctionData3({
|
|
1402
|
+
abi: ramsesMintAbi,
|
|
1403
|
+
functionName: "mint",
|
|
1404
|
+
args: [
|
|
1405
|
+
{
|
|
1406
|
+
token0,
|
|
1407
|
+
token1,
|
|
1408
|
+
tickSpacing: thirdField,
|
|
1409
|
+
tickLower,
|
|
1410
|
+
tickUpper,
|
|
1411
|
+
amount0Desired: amount0,
|
|
1412
|
+
amount1Desired: amount1,
|
|
1413
|
+
amount0Min,
|
|
1414
|
+
amount1Min,
|
|
1415
|
+
recipient: params.recipient,
|
|
1416
|
+
deadline: BigInt("18446744073709551615")
|
|
1417
|
+
}
|
|
1418
|
+
]
|
|
1419
|
+
}) : this.useTickSpacingQuoter ? encodeFunctionData3({
|
|
1366
1420
|
abi: slipstreamMintAbi,
|
|
1367
1421
|
functionName: "mint",
|
|
1368
1422
|
args: [
|
|
@@ -1374,8 +1428,8 @@ var init_dist2 = __esm({
|
|
|
1374
1428
|
tickUpper,
|
|
1375
1429
|
amount0Desired: amount0,
|
|
1376
1430
|
amount1Desired: amount1,
|
|
1377
|
-
amount0Min
|
|
1378
|
-
amount1Min
|
|
1431
|
+
amount0Min,
|
|
1432
|
+
amount1Min,
|
|
1379
1433
|
recipient: params.recipient,
|
|
1380
1434
|
deadline: BigInt("18446744073709551615"),
|
|
1381
1435
|
sqrtPriceX96: 0n
|
|
@@ -1393,8 +1447,8 @@ var init_dist2 = __esm({
|
|
|
1393
1447
|
tickUpper,
|
|
1394
1448
|
amount0Desired: amount0,
|
|
1395
1449
|
amount1Desired: amount1,
|
|
1396
|
-
amount0Min
|
|
1397
|
-
amount1Min
|
|
1450
|
+
amount0Min,
|
|
1451
|
+
amount1Min,
|
|
1398
1452
|
recipient: params.recipient,
|
|
1399
1453
|
deadline: BigInt("18446744073709551615")
|
|
1400
1454
|
}
|
|
@@ -1428,10 +1482,17 @@ var init_dist2 = __esm({
|
|
|
1428
1482
|
const liquidity = params.liquidity;
|
|
1429
1483
|
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
1430
1484
|
const deadline = BigInt("18446744073709551615");
|
|
1485
|
+
if (params.amount_a_min === void 0 || params.amount_b_min === void 0) {
|
|
1486
|
+
throw DefiError.invalidParam(
|
|
1487
|
+
`[${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.`
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
1491
|
+
const [amount0Min, amount1Min] = isAFirst ? [params.amount_a_min, params.amount_b_min] : [params.amount_b_min, params.amount_a_min];
|
|
1431
1492
|
const decreaseData = encodeFunctionData3({
|
|
1432
1493
|
abi: positionManagerAbi,
|
|
1433
1494
|
functionName: "decreaseLiquidity",
|
|
1434
|
-
args: [{ tokenId, liquidity, amount0Min
|
|
1495
|
+
args: [{ tokenId, liquidity, amount0Min, amount1Min, deadline }]
|
|
1435
1496
|
});
|
|
1436
1497
|
const collectData = encodeFunctionData3({
|
|
1437
1498
|
abi: positionManagerAbi,
|
|
@@ -1729,7 +1790,13 @@ var init_dist2 = __esm({
|
|
|
1729
1790
|
to: this.router,
|
|
1730
1791
|
data,
|
|
1731
1792
|
value: 0n,
|
|
1732
|
-
gas_estimate: 25e4
|
|
1793
|
+
gas_estimate: 25e4,
|
|
1794
|
+
// Same LP-approval requirement as Solidly forks: the V2 router pulls the
|
|
1795
|
+
// LP pair token via transferFrom and reverts at gas ~42k otherwise.
|
|
1796
|
+
// Caller passes --pool so we can target the pair contract for approval.
|
|
1797
|
+
// Discovered live on Uniswap V2 Monad WMON/AUSD 2026-05-08
|
|
1798
|
+
// (failed tx 0x2268659a…09ae → recovered with this fix).
|
|
1799
|
+
...params.pool ? { approvals: [{ token: params.pool, spender: this.router, amount: params.liquidity }] } : {}
|
|
1733
1800
|
};
|
|
1734
1801
|
}
|
|
1735
1802
|
};
|
|
@@ -1784,7 +1851,15 @@ var init_dist2 = __esm({
|
|
|
1784
1851
|
}
|
|
1785
1852
|
async buildSwap(params) {
|
|
1786
1853
|
const deadline = BigInt(params.deadline ?? 18446744073709551615n);
|
|
1787
|
-
const amountOutMinimum =
|
|
1854
|
+
const amountOutMinimum = params.amount_out_min ?? applyMinSlippage(
|
|
1855
|
+
params.slippage,
|
|
1856
|
+
(await this.quote({
|
|
1857
|
+
protocol: this.protocolName,
|
|
1858
|
+
token_in: params.token_in,
|
|
1859
|
+
token_out: params.token_out,
|
|
1860
|
+
amount_in: params.amount_in
|
|
1861
|
+
})).amount_out
|
|
1862
|
+
);
|
|
1788
1863
|
const data = encodeFunctionData32({
|
|
1789
1864
|
abi: abi2,
|
|
1790
1865
|
functionName: "exactInputSingle",
|
|
@@ -1898,7 +1973,12 @@ var init_dist2 = __esm({
|
|
|
1898
1973
|
if (!pm) {
|
|
1899
1974
|
throw new DefiError("CONTRACT_ERROR", "Position manager address not configured");
|
|
1900
1975
|
}
|
|
1901
|
-
const
|
|
1976
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
1977
|
+
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];
|
|
1978
|
+
const slippage = params.slippage ?? defaultSwapSlippage();
|
|
1979
|
+
const minA = params.amount_a_min ?? applyMinSlippage(slippage, params.amount_a);
|
|
1980
|
+
const minB = params.amount_b_min ?? applyMinSlippage(slippage, params.amount_b);
|
|
1981
|
+
const [amount0Min, amount1Min] = isAFirst ? [minA, minB] : [minB, minA];
|
|
1902
1982
|
let tickLower = params.tick_lower ?? -887220;
|
|
1903
1983
|
let tickUpper = params.tick_upper ?? 887220;
|
|
1904
1984
|
const isSingleSide = rawAmount0 === 0n || rawAmount1 === 0n;
|
|
@@ -1935,11 +2015,11 @@ var init_dist2 = __esm({
|
|
|
1935
2015
|
const data = this.useSingleQuoter ? encodeFunctionData32({
|
|
1936
2016
|
abi: algebraV2PmAbi,
|
|
1937
2017
|
functionName: "mint",
|
|
1938
|
-
args: [{ token0, token1, tickLower, tickUpper, amount0Desired: amount0, amount1Desired: amount1, amount0Min
|
|
2018
|
+
args: [{ token0, token1, tickLower, tickUpper, amount0Desired: amount0, amount1Desired: amount1, amount0Min, amount1Min, recipient: params.recipient, deadline: BigInt("18446744073709551615") }]
|
|
1939
2019
|
}) : encodeFunctionData32({
|
|
1940
2020
|
abi: algebraIntegralPmAbi,
|
|
1941
2021
|
functionName: "mint",
|
|
1942
|
-
args: [{ token0, token1, deployer: zeroAddress, tickLower, tickUpper, amount0Desired: amount0, amount1Desired: amount1, amount0Min
|
|
2022
|
+
args: [{ token0, token1, deployer: zeroAddress, tickLower, tickUpper, amount0Desired: amount0, amount1Desired: amount1, amount0Min, amount1Min, recipient: params.recipient, deadline: BigInt("18446744073709551615") }]
|
|
1943
2023
|
});
|
|
1944
2024
|
const approvals = [];
|
|
1945
2025
|
if (amount0 > 0n) approvals.push({ token: token0, spender: pm, amount: amount0 });
|
|
@@ -1957,12 +2037,19 @@ var init_dist2 = __esm({
|
|
|
1957
2037
|
const pm = this.positionManager;
|
|
1958
2038
|
if (!pm) throw DefiError.contractError(`[${this.protocolName}] Missing 'position_manager'`);
|
|
1959
2039
|
if (!params.token_id) throw DefiError.invalidParam(`[${this.protocolName}] V3 remove_liquidity requires --token-id`);
|
|
2040
|
+
if (params.amount_a_min === void 0 || params.amount_b_min === void 0) {
|
|
2041
|
+
throw DefiError.invalidParam(
|
|
2042
|
+
`[${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.`
|
|
2043
|
+
);
|
|
2044
|
+
}
|
|
2045
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
2046
|
+
const [amount0Min, amount1Min] = isAFirst ? [params.amount_a_min, params.amount_b_min] : [params.amount_b_min, params.amount_a_min];
|
|
1960
2047
|
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
1961
2048
|
const deadline = BigInt("18446744073709551615");
|
|
1962
2049
|
const decreaseData = encodeFunctionData32({
|
|
1963
2050
|
abi: algebraSharedPmAbi,
|
|
1964
2051
|
functionName: "decreaseLiquidity",
|
|
1965
|
-
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min
|
|
2052
|
+
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min, amount1Min, deadline }]
|
|
1966
2053
|
});
|
|
1967
2054
|
const collectData = encodeFunctionData32({
|
|
1968
2055
|
abi: algebraSharedPmAbi,
|
|
@@ -1989,6 +2076,7 @@ var init_dist2 = __esm({
|
|
|
1989
2076
|
BalancerV3Adapter = class {
|
|
1990
2077
|
protocolName;
|
|
1991
2078
|
router;
|
|
2079
|
+
pool;
|
|
1992
2080
|
constructor(entry, _rpcUrl) {
|
|
1993
2081
|
this.protocolName = entry.name;
|
|
1994
2082
|
const router = entry.contracts?.["router"];
|
|
@@ -1996,19 +2084,29 @@ var init_dist2 = __esm({
|
|
|
1996
2084
|
throw new DefiError("CONTRACT_ERROR", "Missing 'router' contract");
|
|
1997
2085
|
}
|
|
1998
2086
|
this.router = router;
|
|
2087
|
+
this.pool = entry.contracts?.["pool"];
|
|
1999
2088
|
}
|
|
2000
2089
|
name() {
|
|
2001
2090
|
return this.protocolName;
|
|
2002
2091
|
}
|
|
2003
2092
|
async buildSwap(params) {
|
|
2004
|
-
|
|
2093
|
+
if (params.amount_out_min === void 0) {
|
|
2094
|
+
throw DefiError.invalidParam(
|
|
2095
|
+
`[${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.`
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
if (!this.pool) {
|
|
2099
|
+
throw DefiError.invalidParam(
|
|
2100
|
+
`[${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.`
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2103
|
+
const minAmountOut = params.amount_out_min;
|
|
2005
2104
|
const deadline = BigInt(params.deadline ?? 18446744073709551615n);
|
|
2006
2105
|
const data = encodeFunctionData4({
|
|
2007
2106
|
abi: abi3,
|
|
2008
2107
|
functionName: "swapSingleTokenExactIn",
|
|
2009
2108
|
args: [
|
|
2010
|
-
|
|
2011
|
-
// TODO: resolve pool from registry
|
|
2109
|
+
this.pool,
|
|
2012
2110
|
params.token_in,
|
|
2013
2111
|
params.token_out,
|
|
2014
2112
|
params.amount_in,
|
|
@@ -2019,7 +2117,7 @@ var init_dist2 = __esm({
|
|
|
2019
2117
|
]
|
|
2020
2118
|
});
|
|
2021
2119
|
return {
|
|
2022
|
-
description: `[${this.protocolName}] Swap ${params.amount_in} via Balancer V3`,
|
|
2120
|
+
description: `[${this.protocolName}] Swap ${params.amount_in} via Balancer V3 pool ${this.pool}`,
|
|
2023
2121
|
to: this.router,
|
|
2024
2122
|
data,
|
|
2025
2123
|
value: 0n,
|
|
@@ -2039,8 +2137,8 @@ var init_dist2 = __esm({
|
|
|
2039
2137
|
poolAbi = parseAbi5([
|
|
2040
2138
|
"function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256)",
|
|
2041
2139
|
"function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256)",
|
|
2042
|
-
"function add_liquidity(uint256[
|
|
2043
|
-
"function remove_liquidity(uint256 amount, uint256[
|
|
2140
|
+
"function add_liquidity(uint256[] amounts, uint256 min_mint_amount) external returns (uint256)",
|
|
2141
|
+
"function remove_liquidity(uint256 amount, uint256[] min_amounts) external returns (uint256[])"
|
|
2044
2142
|
]);
|
|
2045
2143
|
CurveStableSwapAdapter = class {
|
|
2046
2144
|
protocolName;
|
|
@@ -2075,28 +2173,42 @@ var init_dist2 = __esm({
|
|
|
2075
2173
|
throw DefiError.unsupported(`[${this.protocolName}] quote requires RPC connection`);
|
|
2076
2174
|
}
|
|
2077
2175
|
async buildAddLiquidity(params) {
|
|
2176
|
+
if (!params.pool) {
|
|
2177
|
+
throw DefiError.invalidParam(
|
|
2178
|
+
`[${this.protocolName}] Curve add_liquidity needs --pool <address>. The router does not proxy this call; it lives on the pool itself.`
|
|
2179
|
+
);
|
|
2180
|
+
}
|
|
2078
2181
|
const data = encodeFunctionData5({
|
|
2079
2182
|
abi: poolAbi,
|
|
2080
2183
|
functionName: "add_liquidity",
|
|
2081
2184
|
args: [[params.amount_a, params.amount_b], 0n]
|
|
2082
2185
|
});
|
|
2186
|
+
const approvals = [];
|
|
2187
|
+
if (params.amount_a > 0n) approvals.push({ token: params.token_a, spender: params.pool, amount: params.amount_a });
|
|
2188
|
+
if (params.amount_b > 0n) approvals.push({ token: params.token_b, spender: params.pool, amount: params.amount_b });
|
|
2083
2189
|
return {
|
|
2084
|
-
description: `[${this.protocolName}] Curve add liquidity`,
|
|
2085
|
-
to:
|
|
2190
|
+
description: `[${this.protocolName}] Curve add liquidity to ${params.pool}`,
|
|
2191
|
+
to: params.pool,
|
|
2086
2192
|
data,
|
|
2087
2193
|
value: 0n,
|
|
2088
|
-
gas_estimate: 4e5
|
|
2194
|
+
gas_estimate: 4e5,
|
|
2195
|
+
approvals
|
|
2089
2196
|
};
|
|
2090
2197
|
}
|
|
2091
2198
|
async buildRemoveLiquidity(params) {
|
|
2199
|
+
if (!params.pool) {
|
|
2200
|
+
throw DefiError.invalidParam(
|
|
2201
|
+
`[${this.protocolName}] Curve remove_liquidity needs --pool <address>. The router does not proxy this call.`
|
|
2202
|
+
);
|
|
2203
|
+
}
|
|
2092
2204
|
const data = encodeFunctionData5({
|
|
2093
2205
|
abi: poolAbi,
|
|
2094
2206
|
functionName: "remove_liquidity",
|
|
2095
2207
|
args: [params.liquidity, [0n, 0n]]
|
|
2096
2208
|
});
|
|
2097
2209
|
return {
|
|
2098
|
-
description: `[${this.protocolName}] Curve remove liquidity`,
|
|
2099
|
-
to:
|
|
2210
|
+
description: `[${this.protocolName}] Curve remove liquidity from ${params.pool}`,
|
|
2211
|
+
to: params.pool,
|
|
2100
2212
|
data,
|
|
2101
2213
|
value: 0n,
|
|
2102
2214
|
gas_estimate: 35e4
|
|
@@ -2262,7 +2374,12 @@ var init_dist2 = __esm({
|
|
|
2262
2374
|
to: this.router,
|
|
2263
2375
|
data,
|
|
2264
2376
|
value: 0n,
|
|
2265
|
-
gas_estimate: 3e5
|
|
2377
|
+
gas_estimate: 3e5,
|
|
2378
|
+
// The router pulls the LP token via transferFrom; without this approval
|
|
2379
|
+
// the tx reverts at gas ~42k. Caller must pass --pool so we know which
|
|
2380
|
+
// LP pair to approve. Discovered live on Aerodrome USDC/USDT 2026-05-08
|
|
2381
|
+
// (failed tx 0x6d052e0a…3298 → recovered with manual approve 0xa126fc3a).
|
|
2382
|
+
...params.pool ? { approvals: [{ token: params.pool, spender: this.router, amount: params.liquidity }] } : {}
|
|
2266
2383
|
};
|
|
2267
2384
|
}
|
|
2268
2385
|
};
|
|
@@ -2309,6 +2426,11 @@ var init_dist2 = __esm({
|
|
|
2309
2426
|
return this.protocolName;
|
|
2310
2427
|
}
|
|
2311
2428
|
async buildSwap(params) {
|
|
2429
|
+
if (params.amount_out_min === void 0) {
|
|
2430
|
+
throw DefiError.invalidParam(
|
|
2431
|
+
`[${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.`
|
|
2432
|
+
);
|
|
2433
|
+
}
|
|
2312
2434
|
const data = encodeFunctionData7({
|
|
2313
2435
|
abi: thenaRouterAbi,
|
|
2314
2436
|
functionName: "exactInputSingle",
|
|
@@ -2319,7 +2441,7 @@ var init_dist2 = __esm({
|
|
|
2319
2441
|
recipient: params.recipient,
|
|
2320
2442
|
deadline: BigInt(params.deadline ?? 18446744073709551615n),
|
|
2321
2443
|
amountIn: params.amount_in,
|
|
2322
|
-
amountOutMinimum:
|
|
2444
|
+
amountOutMinimum: params.amount_out_min,
|
|
2323
2445
|
sqrtPriceLimitX96: 0n
|
|
2324
2446
|
}]
|
|
2325
2447
|
});
|
|
@@ -2339,7 +2461,12 @@ var init_dist2 = __esm({
|
|
|
2339
2461
|
const pm = this.positionManager;
|
|
2340
2462
|
if (!pm) throw new DefiError("CONTRACT_ERROR", "Position manager not configured");
|
|
2341
2463
|
if (!this.rpcUrl) throw DefiError.rpcError("RPC URL required");
|
|
2342
|
-
const
|
|
2464
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
2465
|
+
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];
|
|
2466
|
+
const slippage = params.slippage ?? defaultSwapSlippage();
|
|
2467
|
+
const minA = params.amount_a_min ?? applyMinSlippage(slippage, params.amount_a);
|
|
2468
|
+
const minB = params.amount_b_min ?? applyMinSlippage(slippage, params.amount_b);
|
|
2469
|
+
const [amount0Min, amount1Min] = isAFirst ? [minA, minB] : [minB, minA];
|
|
2343
2470
|
const client = createPublicClient4({ transport: http4(this.rpcUrl) });
|
|
2344
2471
|
const poolAddr = params.pool;
|
|
2345
2472
|
let tickSpacing = this.defaultTickSpacing;
|
|
@@ -2354,7 +2481,7 @@ var init_dist2 = __esm({
|
|
|
2354
2481
|
functionName: "getPool",
|
|
2355
2482
|
args: [token0, token1, tickSpacing]
|
|
2356
2483
|
});
|
|
2357
|
-
if (pool ===
|
|
2484
|
+
if (pool === zeroAddress2) throw new DefiError("CONTRACT_ERROR", "Pool not found");
|
|
2358
2485
|
}
|
|
2359
2486
|
if (pool) {
|
|
2360
2487
|
const [slot0, ts] = await Promise.all([
|
|
@@ -2397,8 +2524,8 @@ var init_dist2 = __esm({
|
|
|
2397
2524
|
tickUpper,
|
|
2398
2525
|
amount0Desired: rawAmount0,
|
|
2399
2526
|
amount1Desired: rawAmount1,
|
|
2400
|
-
amount0Min
|
|
2401
|
-
amount1Min
|
|
2527
|
+
amount0Min,
|
|
2528
|
+
amount1Min,
|
|
2402
2529
|
recipient: params.recipient,
|
|
2403
2530
|
deadline: BigInt("18446744073709551615"),
|
|
2404
2531
|
sqrtPriceX96: 0n
|
|
@@ -2420,12 +2547,19 @@ var init_dist2 = __esm({
|
|
|
2420
2547
|
const pm = this.positionManager;
|
|
2421
2548
|
if (!pm) throw DefiError.contractError(`[${this.protocolName}] Missing 'position_manager'`);
|
|
2422
2549
|
if (!params.token_id) throw DefiError.invalidParam(`[${this.protocolName}] V3 remove_liquidity requires --token-id`);
|
|
2550
|
+
if (params.amount_a_min === void 0 || params.amount_b_min === void 0) {
|
|
2551
|
+
throw DefiError.invalidParam(
|
|
2552
|
+
`[${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.`
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2555
|
+
const isAFirst = params.token_a.toLowerCase() < params.token_b.toLowerCase();
|
|
2556
|
+
const [amount0Min, amount1Min] = isAFirst ? [params.amount_a_min, params.amount_b_min] : [params.amount_b_min, params.amount_a_min];
|
|
2423
2557
|
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
2424
2558
|
const deadline = BigInt("18446744073709551615");
|
|
2425
2559
|
const decreaseData = encodeFunctionData7({
|
|
2426
2560
|
abi: thenaPmAbi,
|
|
2427
2561
|
functionName: "decreaseLiquidity",
|
|
2428
|
-
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min
|
|
2562
|
+
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min, amount1Min, deadline }]
|
|
2429
2563
|
});
|
|
2430
2564
|
const collectData = encodeFunctionData7({
|
|
2431
2565
|
abi: thenaPmAbi,
|
|
@@ -2492,8 +2626,8 @@ var init_dist2 = __esm({
|
|
|
2492
2626
|
const ve = entry.contracts?.["ve_token"];
|
|
2493
2627
|
if (!ve) throw new DefiError("CONTRACT_ERROR", "Missing 've_token' contract");
|
|
2494
2628
|
this.veToken = ve;
|
|
2495
|
-
this.voter = entry.contracts?.["voter"] ??
|
|
2496
|
-
this.positionManager = entry.contracts?.["position_manager"] ??
|
|
2629
|
+
this.voter = entry.contracts?.["voter"] ?? zeroAddress3;
|
|
2630
|
+
this.positionManager = entry.contracts?.["position_manager"] ?? zeroAddress3;
|
|
2497
2631
|
this.poolFactory = entry.contracts?.["pool_factory"];
|
|
2498
2632
|
this.rpcUrl = rpcUrl;
|
|
2499
2633
|
}
|
|
@@ -2535,7 +2669,7 @@ var init_dist2 = __esm({
|
|
|
2535
2669
|
]);
|
|
2536
2670
|
}
|
|
2537
2671
|
const poolAddressResults = await multicallRead(this.rpcUrl, poolAddressCalls);
|
|
2538
|
-
const pools = poolAddressResults.map((r) => decodeAddress(r)).filter((a) => a !== null && a !==
|
|
2672
|
+
const pools = poolAddressResults.map((r) => decodeAddress(r)).filter((a) => a !== null && a !== zeroAddress3);
|
|
2539
2673
|
if (pools.length === 0) return [];
|
|
2540
2674
|
const gaugeCalls = pools.map((pool) => [
|
|
2541
2675
|
this.gaugeManager,
|
|
@@ -2545,7 +2679,7 @@ var init_dist2 = __esm({
|
|
|
2545
2679
|
const gaugedPools = [];
|
|
2546
2680
|
for (let i = 0; i < pools.length; i++) {
|
|
2547
2681
|
const gauge = decodeAddress(gaugeResults[i] ?? null);
|
|
2548
|
-
if (gauge && gauge !==
|
|
2682
|
+
if (gauge && gauge !== zeroAddress3) {
|
|
2549
2683
|
gaugedPools.push({ pool: pools[i], gauge });
|
|
2550
2684
|
}
|
|
2551
2685
|
}
|
|
@@ -2560,8 +2694,8 @@ var init_dist2 = __esm({
|
|
|
2560
2694
|
for (let i = 0; i < gaugedPools.length; i++) {
|
|
2561
2695
|
const t0 = decodeAddress(tokenResults[i * 2] ?? null);
|
|
2562
2696
|
const t1 = decodeAddress(tokenResults[i * 2 + 1] ?? null);
|
|
2563
|
-
if (t0 && t0 !==
|
|
2564
|
-
if (t1 && t1 !==
|
|
2697
|
+
if (t0 && t0 !== zeroAddress3) tokenAddrs.add(t0);
|
|
2698
|
+
if (t1 && t1 !== zeroAddress3) tokenAddrs.add(t1);
|
|
2565
2699
|
}
|
|
2566
2700
|
const uniqueTokens = Array.from(tokenAddrs);
|
|
2567
2701
|
const symbolCalls = uniqueTokens.map((t) => [
|
|
@@ -2600,7 +2734,7 @@ var init_dist2 = __esm({
|
|
|
2600
2734
|
functionName: "gauges",
|
|
2601
2735
|
args: [pool]
|
|
2602
2736
|
});
|
|
2603
|
-
if (gauge ===
|
|
2737
|
+
if (gauge === zeroAddress3) throw new DefiError("CONTRACT_ERROR", `No gauge for pool ${pool}`);
|
|
2604
2738
|
return gauge;
|
|
2605
2739
|
}
|
|
2606
2740
|
// ─── CL Gauge: NFT Deposit/Withdraw ──────────────────────────
|
|
@@ -2762,7 +2896,7 @@ var init_dist2 = __esm({
|
|
|
2762
2896
|
params.amount_in,
|
|
2763
2897
|
minToAmount,
|
|
2764
2898
|
params.recipient,
|
|
2765
|
-
|
|
2899
|
+
zeroAddress4
|
|
2766
2900
|
]
|
|
2767
2901
|
});
|
|
2768
2902
|
return {
|
|
@@ -2987,7 +3121,7 @@ var init_dist2 = __esm({
|
|
|
2987
3121
|
poolCalls.push([this.voter, encodeFunctionData10({ abi: voterPoolsAbi, functionName: "pools", args: [BigInt(i)] })]);
|
|
2988
3122
|
}
|
|
2989
3123
|
const poolResults = await multicallRead(this.rpcUrl, poolCalls);
|
|
2990
|
-
pairs = poolResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !==
|
|
3124
|
+
pairs = poolResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !== zeroAddress5);
|
|
2991
3125
|
} else {
|
|
2992
3126
|
const v2FactoryAbi = parseAbi10(["function allPairsLength() view returns (uint256)", "function allPairs(uint256) view returns (address)"]);
|
|
2993
3127
|
const solidlyFactoryAbi = parseAbi10(["function allPoolsLength() view returns (uint256)", "function allPools(uint256) view returns (address)"]);
|
|
@@ -3012,17 +3146,17 @@ var init_dist2 = __esm({
|
|
|
3012
3146
|
const pairCalls = [];
|
|
3013
3147
|
for (let i = startIdx; i < count; i++) pairCalls.push([this.v2Factory, encodeFunctionData10({ abi: fetchAbi, functionName: fetchFn, args: [BigInt(i)] })]);
|
|
3014
3148
|
const pairResults = await multicallRead(this.rpcUrl, pairCalls);
|
|
3015
|
-
pairs = pairResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !==
|
|
3149
|
+
pairs = pairResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !== zeroAddress5);
|
|
3016
3150
|
}
|
|
3017
3151
|
if (pairs.length === 0) return;
|
|
3018
3152
|
const gaugeCalls = pairs.map((p) => [this.voter, encodeFunctionData10({ abi: gaugeForPoolAbi, functionName: "gaugeForPool", args: [p] })]);
|
|
3019
3153
|
let gaugeResults = await multicallRead(this.rpcUrl, gaugeCalls);
|
|
3020
|
-
const allNull = gaugeResults.every((r) => !r || decodeAddress2(r) ===
|
|
3154
|
+
const allNull = gaugeResults.every((r) => !r || decodeAddress2(r) === zeroAddress5 || decodeAddress2(r) === null);
|
|
3021
3155
|
if (allNull) {
|
|
3022
3156
|
const fb1 = pairs.map((p) => [this.voter, encodeFunctionData10({ abi: poolToGaugeAbi, functionName: "poolToGauge", args: [p] })]);
|
|
3023
3157
|
gaugeResults = await multicallRead(this.rpcUrl, fb1);
|
|
3024
3158
|
}
|
|
3025
|
-
const allNull2 = gaugeResults.every((r) => !r || decodeAddress2(r) ===
|
|
3159
|
+
const allNull2 = gaugeResults.every((r) => !r || decodeAddress2(r) === zeroAddress5 || decodeAddress2(r) === null);
|
|
3026
3160
|
if (allNull2) {
|
|
3027
3161
|
const fb2 = pairs.map((p) => [this.voter, encodeFunctionData10({ abi: gaugesAbi, functionName: "gauges", args: [p] })]);
|
|
3028
3162
|
gaugeResults = await multicallRead(this.rpcUrl, fb2);
|
|
@@ -3030,7 +3164,7 @@ var init_dist2 = __esm({
|
|
|
3030
3164
|
const gaugedPairs = [];
|
|
3031
3165
|
for (let i = 0; i < pairs.length; i++) {
|
|
3032
3166
|
const gauge = decodeAddress2(gaugeResults[i] ?? null);
|
|
3033
|
-
if (gauge && gauge !==
|
|
3167
|
+
if (gauge && gauge !== zeroAddress5) {
|
|
3034
3168
|
gaugedPairs.push({ pair: pairs[i], gauge });
|
|
3035
3169
|
}
|
|
3036
3170
|
}
|
|
@@ -3046,8 +3180,8 @@ var init_dist2 = __esm({
|
|
|
3046
3180
|
for (let i = 0; i < gaugedPairs.length; i++) {
|
|
3047
3181
|
const t0 = decodeAddress2(metaResults[i * 3] ?? null);
|
|
3048
3182
|
const t1 = decodeAddress2(metaResults[i * 3 + 1] ?? null);
|
|
3049
|
-
if (t0 && t0 !==
|
|
3050
|
-
if (t1 && t1 !==
|
|
3183
|
+
if (t0 && t0 !== zeroAddress5) tokenAddrs.add(t0);
|
|
3184
|
+
if (t1 && t1 !== zeroAddress5) tokenAddrs.add(t1);
|
|
3051
3185
|
}
|
|
3052
3186
|
const uniqueTokens = Array.from(tokenAddrs);
|
|
3053
3187
|
const symbolCalls = uniqueTokens.map((t) => [
|
|
@@ -3137,7 +3271,7 @@ var init_dist2 = __esm({
|
|
|
3137
3271
|
const candidatePools = [];
|
|
3138
3272
|
for (let i = 0; i < getPoolCalls.length; i++) {
|
|
3139
3273
|
const pool = decodeAddress2(getPoolResults[i] ?? null);
|
|
3140
|
-
if (pool && pool !==
|
|
3274
|
+
if (pool && pool !== zeroAddress5) {
|
|
3141
3275
|
const { pairIdx, tickSpacing } = callMeta[i];
|
|
3142
3276
|
const [tokenA, tokenB] = pairs[pairIdx];
|
|
3143
3277
|
candidatePools.push({ pool, tokenA, tokenB, tickSpacing });
|
|
@@ -3149,7 +3283,7 @@ var init_dist2 = __esm({
|
|
|
3149
3283
|
encodeFunctionData10({ abi: gaugeForPoolAbi, functionName: "gaugeForPool", args: [pool] })
|
|
3150
3284
|
]);
|
|
3151
3285
|
let gaugeResults = await multicallRead(this.rpcUrl, gaugeCalls);
|
|
3152
|
-
const allNull = gaugeResults.every((r) => !r || decodeAddress2(r) ===
|
|
3286
|
+
const allNull = gaugeResults.every((r) => !r || decodeAddress2(r) === zeroAddress5 || decodeAddress2(r) === null);
|
|
3153
3287
|
if (allNull) {
|
|
3154
3288
|
const fallbackCalls = candidatePools.map(({ pool }) => [
|
|
3155
3289
|
this.voter,
|
|
@@ -3160,7 +3294,7 @@ var init_dist2 = __esm({
|
|
|
3160
3294
|
const gaugedCL = [];
|
|
3161
3295
|
for (let i = 0; i < candidatePools.length; i++) {
|
|
3162
3296
|
const gauge = decodeAddress2(gaugeResults[i] ?? null);
|
|
3163
|
-
if (gauge && gauge !==
|
|
3297
|
+
if (gauge && gauge !== zeroAddress5) {
|
|
3164
3298
|
gaugedCL.push({ ...candidatePools[i], gauge });
|
|
3165
3299
|
}
|
|
3166
3300
|
}
|
|
@@ -3190,8 +3324,8 @@ var init_dist2 = __esm({
|
|
|
3190
3324
|
const { pool, gauge, tokenA, tokenB, tickSpacing } = gaugedCL[i];
|
|
3191
3325
|
const rawT0 = decodeAddress2(poolTokenResults[i * 2] ?? null);
|
|
3192
3326
|
const rawT1 = decodeAddress2(poolTokenResults[i * 2 + 1] ?? null);
|
|
3193
|
-
const t0 = rawT0 && rawT0 !==
|
|
3194
|
-
const t1 = rawT1 && rawT1 !==
|
|
3327
|
+
const t0 = rawT0 && rawT0 !== zeroAddress5 ? rawT0 : tokenA;
|
|
3328
|
+
const t1 = rawT1 && rawT1 !== zeroAddress5 ? rawT1 : tokenB;
|
|
3195
3329
|
out.push({
|
|
3196
3330
|
pool,
|
|
3197
3331
|
gauge,
|
|
@@ -3293,7 +3427,7 @@ var init_dist2 = __esm({
|
|
|
3293
3427
|
functionName: fn,
|
|
3294
3428
|
args: [pool]
|
|
3295
3429
|
});
|
|
3296
|
-
if (gauge !==
|
|
3430
|
+
if (gauge !== zeroAddress5) return gauge;
|
|
3297
3431
|
} catch {
|
|
3298
3432
|
}
|
|
3299
3433
|
}
|
|
@@ -3347,7 +3481,7 @@ var init_dist2 = __esm({
|
|
|
3347
3481
|
abi: gaugeAbi,
|
|
3348
3482
|
functionName: "rewardToken"
|
|
3349
3483
|
});
|
|
3350
|
-
if (rt !==
|
|
3484
|
+
if (rt !== zeroAddress5) return { tokens: [rt], multiToken: false };
|
|
3351
3485
|
} catch {
|
|
3352
3486
|
}
|
|
3353
3487
|
return { tokens: [], multiToken: false };
|
|
@@ -3357,7 +3491,7 @@ var init_dist2 = __esm({
|
|
|
3357
3491
|
const data = encodeFunctionData10({
|
|
3358
3492
|
abi: gaugeAbi,
|
|
3359
3493
|
functionName: "getReward",
|
|
3360
|
-
args: [account ??
|
|
3494
|
+
args: [account ?? zeroAddress5]
|
|
3361
3495
|
});
|
|
3362
3496
|
return { description: `[${this.protocolName}] Claim gauge rewards`, to: gauge, data, value: 0n, gas_estimate: 2e5 };
|
|
3363
3497
|
}
|
|
@@ -3581,7 +3715,7 @@ var init_dist2 = __esm({
|
|
|
3581
3715
|
functionName: "earned",
|
|
3582
3716
|
args: [user]
|
|
3583
3717
|
});
|
|
3584
|
-
results.push({ token:
|
|
3718
|
+
results.push({ token: zeroAddress5, symbol: "unknown", amount: earned });
|
|
3585
3719
|
} catch {
|
|
3586
3720
|
}
|
|
3587
3721
|
}
|
|
@@ -3943,20 +4077,47 @@ var init_dist2 = __esm({
|
|
|
3943
4077
|
/**
|
|
3944
4078
|
* Build an addLiquidity transaction for a Liquidity Book pair.
|
|
3945
4079
|
* Distributes tokenX/tokenY uniformly across active bin ± numBins.
|
|
4080
|
+
*
|
|
4081
|
+
* The LB pair stores tokenX/tokenY in the order set at factory creation,
|
|
4082
|
+
* which is **not** address-sorted. Callers may pass tokens in either order;
|
|
4083
|
+
* we always re-align with the pool's `getTokenX/getTokenY` (and swap amounts
|
|
4084
|
+
* accordingly) so the router's `if (tokenX != pair.tokenX) revert` path is
|
|
4085
|
+
* never hit.
|
|
3946
4086
|
*/
|
|
3947
4087
|
async buildAddLiquidity(params) {
|
|
3948
4088
|
const numBins = params.numBins ?? 5;
|
|
3949
4089
|
const deadline = params.deadline ?? BigInt("18446744073709551615");
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
4090
|
+
const rpcUrl = this.requireRpc();
|
|
4091
|
+
const client = createPublicClient8({ transport: http8(rpcUrl) });
|
|
4092
|
+
const [poolTokenX, poolTokenY, onChainActiveId] = await Promise.all([
|
|
4093
|
+
client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getTokenX" }),
|
|
4094
|
+
client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getTokenY" }),
|
|
4095
|
+
params.activeIdDesired === void 0 ? client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getActiveId" }) : Promise.resolve(params.activeIdDesired)
|
|
4096
|
+
]);
|
|
4097
|
+
const activeIdDesired = onChainActiveId;
|
|
4098
|
+
const inX = params.tokenX.toLowerCase();
|
|
4099
|
+
const inY = params.tokenY.toLowerCase();
|
|
4100
|
+
const poolX = poolTokenX.toLowerCase();
|
|
4101
|
+
const poolY = poolTokenY.toLowerCase();
|
|
4102
|
+
let tokenX;
|
|
4103
|
+
let tokenY;
|
|
4104
|
+
let amountX;
|
|
4105
|
+
let amountY;
|
|
4106
|
+
if (inX === poolX && inY === poolY) {
|
|
4107
|
+
tokenX = params.tokenX;
|
|
4108
|
+
tokenY = params.tokenY;
|
|
4109
|
+
amountX = params.amountX;
|
|
4110
|
+
amountY = params.amountY;
|
|
4111
|
+
} else if (inX === poolY && inY === poolX) {
|
|
4112
|
+
tokenX = poolTokenX;
|
|
4113
|
+
tokenY = poolTokenY;
|
|
4114
|
+
amountX = params.amountY;
|
|
4115
|
+
amountY = params.amountX;
|
|
4116
|
+
} else {
|
|
4117
|
+
throw new DefiError(
|
|
4118
|
+
"CONTRACT_ERROR",
|
|
4119
|
+
`[${this.protocolName}] tokenX/tokenY ${params.tokenX}/${params.tokenY} do not match pool ${params.pool} (${poolTokenX}/${poolTokenY})`
|
|
4120
|
+
);
|
|
3960
4121
|
}
|
|
3961
4122
|
const deltaIds = [];
|
|
3962
4123
|
for (let d = -numBins; d <= numBins; d++) {
|
|
@@ -3968,11 +4129,11 @@ var init_dist2 = __esm({
|
|
|
3968
4129
|
functionName: "addLiquidity",
|
|
3969
4130
|
args: [
|
|
3970
4131
|
{
|
|
3971
|
-
tokenX
|
|
3972
|
-
tokenY
|
|
4132
|
+
tokenX,
|
|
4133
|
+
tokenY,
|
|
3973
4134
|
binStep: BigInt(params.binStep),
|
|
3974
|
-
amountX
|
|
3975
|
-
amountY
|
|
4135
|
+
amountX,
|
|
4136
|
+
amountY,
|
|
3976
4137
|
amountXMin: 0n,
|
|
3977
4138
|
amountYMin: 0n,
|
|
3978
4139
|
activeIdDesired: BigInt(activeIdDesired),
|
|
@@ -3987,31 +4148,63 @@ var init_dist2 = __esm({
|
|
|
3987
4148
|
]
|
|
3988
4149
|
});
|
|
3989
4150
|
return {
|
|
3990
|
-
description: `[${this.protocolName}] LB addLiquidity ${
|
|
4151
|
+
description: `[${this.protocolName}] LB addLiquidity ${amountX} tokenX + ${amountY} tokenY across ${deltaIds.length} bins`,
|
|
3991
4152
|
to: this.lbRouter,
|
|
3992
4153
|
data,
|
|
3993
4154
|
value: 0n,
|
|
3994
4155
|
gas_estimate: 8e5,
|
|
3995
4156
|
approvals: [
|
|
3996
|
-
{ token:
|
|
3997
|
-
{ token:
|
|
4157
|
+
{ token: tokenX, spender: this.lbRouter, amount: amountX },
|
|
4158
|
+
{ token: tokenY, spender: this.lbRouter, amount: amountY }
|
|
3998
4159
|
]
|
|
3999
4160
|
};
|
|
4000
4161
|
}
|
|
4001
4162
|
/**
|
|
4002
4163
|
* Build a removeLiquidity transaction for specific LB bins.
|
|
4164
|
+
*
|
|
4165
|
+
* When `pool` is supplied, the adapter realigns tokenX/tokenY (and
|
|
4166
|
+
* amountXMin/amountYMin) to the pool's actual ordering. Otherwise it trusts
|
|
4167
|
+
* the caller — the router will revert if the order is wrong.
|
|
4003
4168
|
*/
|
|
4004
4169
|
async buildRemoveLiquidity(params) {
|
|
4005
4170
|
const deadline = params.deadline ?? BigInt("18446744073709551615");
|
|
4171
|
+
let tokenX = params.tokenX;
|
|
4172
|
+
let tokenY = params.tokenY;
|
|
4173
|
+
let amountXMin = params.amountXMin ?? 0n;
|
|
4174
|
+
let amountYMin = params.amountYMin ?? 0n;
|
|
4175
|
+
if (params.pool) {
|
|
4176
|
+
const rpcUrl = this.requireRpc();
|
|
4177
|
+
const client = createPublicClient8({ transport: http8(rpcUrl) });
|
|
4178
|
+
const [poolTokenX, poolTokenY] = await Promise.all([
|
|
4179
|
+
client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getTokenX" }),
|
|
4180
|
+
client.readContract({ address: params.pool, abi: lbPairAbi, functionName: "getTokenY" })
|
|
4181
|
+
]);
|
|
4182
|
+
const inX = params.tokenX.toLowerCase();
|
|
4183
|
+
const inY = params.tokenY.toLowerCase();
|
|
4184
|
+
const poolX = poolTokenX.toLowerCase();
|
|
4185
|
+
const poolY = poolTokenY.toLowerCase();
|
|
4186
|
+
if (inX === poolY && inY === poolX) {
|
|
4187
|
+
tokenX = poolTokenX;
|
|
4188
|
+
tokenY = poolTokenY;
|
|
4189
|
+
const x = amountXMin;
|
|
4190
|
+
amountXMin = amountYMin;
|
|
4191
|
+
amountYMin = x;
|
|
4192
|
+
} else if (!(inX === poolX && inY === poolY)) {
|
|
4193
|
+
throw new DefiError(
|
|
4194
|
+
"CONTRACT_ERROR",
|
|
4195
|
+
`[${this.protocolName}] tokenX/tokenY ${params.tokenX}/${params.tokenY} do not match pool ${params.pool} (${poolTokenX}/${poolTokenY})`
|
|
4196
|
+
);
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4006
4199
|
const data = encodeFunctionData12({
|
|
4007
4200
|
abi: lbRouterAbi,
|
|
4008
4201
|
functionName: "removeLiquidity",
|
|
4009
4202
|
args: [
|
|
4010
|
-
|
|
4011
|
-
|
|
4203
|
+
tokenX,
|
|
4204
|
+
tokenY,
|
|
4012
4205
|
params.binStep,
|
|
4013
|
-
|
|
4014
|
-
|
|
4206
|
+
amountXMin,
|
|
4207
|
+
amountYMin,
|
|
4015
4208
|
params.binIds.map(BigInt),
|
|
4016
4209
|
params.amounts,
|
|
4017
4210
|
params.recipient,
|
|
@@ -4921,7 +5114,7 @@ var init_dist2 = __esm({
|
|
|
4921
5114
|
const poolSet = /* @__PURE__ */ new Set();
|
|
4922
5115
|
for (const data of poolResults) {
|
|
4923
5116
|
const addr = decodeAddress3(data);
|
|
4924
|
-
if (addr && addr !==
|
|
5117
|
+
if (addr && addr !== zeroAddress6) {
|
|
4925
5118
|
poolSet.add(addr.toLowerCase());
|
|
4926
5119
|
}
|
|
4927
5120
|
}
|
|
@@ -5086,6 +5279,34 @@ var init_dist2 = __esm({
|
|
|
5086
5279
|
}
|
|
5087
5280
|
return apr;
|
|
5088
5281
|
}
|
|
5282
|
+
/**
|
|
5283
|
+
* Snapshot of all NEST gauged pools from the off-chain blaze API. The
|
|
5284
|
+
* on-chain ve(3,3) gauges return `rewardRate=0` (emissions are credited
|
|
5285
|
+
* off-chain via signed claim tickets), so the on-chain Solidly gauge
|
|
5286
|
+
* reader cannot surface real emission APR. This API is the canonical
|
|
5287
|
+
* read source — used by `lp discover` to expose non-zero NEST yields.
|
|
5288
|
+
*/
|
|
5289
|
+
async getLiquidityPools() {
|
|
5290
|
+
const raw = await this.fetchJson("/liquidity-pools");
|
|
5291
|
+
const out = [];
|
|
5292
|
+
for (const p of raw) {
|
|
5293
|
+
const t0 = p.token0?.basetoken;
|
|
5294
|
+
const t1 = p.token1?.basetoken;
|
|
5295
|
+
if (!t0?.address || !t1?.address) continue;
|
|
5296
|
+
out.push({
|
|
5297
|
+
pool: p.id,
|
|
5298
|
+
gauge: p.gauge ?? null,
|
|
5299
|
+
token0: { address: t0.address, symbol: t0.symbol ?? "?" },
|
|
5300
|
+
token1: { address: t1.address, symbol: t1.symbol ?? "?" },
|
|
5301
|
+
tvlUSD: Number(p.tvlUSD ?? 0),
|
|
5302
|
+
aprPercent: Number(p.apr ?? 0),
|
|
5303
|
+
curEpochEmissionRewardsUSD: Number(p.curEpochEmissionRewardsUSD ?? 0),
|
|
5304
|
+
poolType: p.poolType,
|
|
5305
|
+
isStable: p.isStable
|
|
5306
|
+
});
|
|
5307
|
+
}
|
|
5308
|
+
return out;
|
|
5309
|
+
}
|
|
5089
5310
|
/** Pending NEST emissions as IGauge-compatible RewardInfo[] */
|
|
5090
5311
|
async getPendingRewards(user) {
|
|
5091
5312
|
const status = await this.getClaimStatus(user);
|
|
@@ -5241,6 +5462,16 @@ var init_dist2 = __esm({
|
|
|
5241
5462
|
"function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf) external",
|
|
5242
5463
|
"function repay(address asset, uint256 amount, uint256 interestRateMode, address onBehalfOf) external returns (uint256)",
|
|
5243
5464
|
"function withdraw(address asset, uint256 amount, address to) external returns (uint256)",
|
|
5465
|
+
// Toggles required to actually borrow against an isolation-mode reserve
|
|
5466
|
+
// (https://aave.com/docs/aave-v3/smart-contracts/pool):
|
|
5467
|
+
// - setUserUseReserveAsCollateral: an isolation reserve can be enabled
|
|
5468
|
+
// only if no other asset is enabled; LTV=0 reserves can never be
|
|
5469
|
+
// enabled; disable reverts if the resulting HF would drop below the
|
|
5470
|
+
// liquidation threshold.
|
|
5471
|
+
// - setUserEMode: reverts if the user is borrowing a non-eMode-
|
|
5472
|
+
// compatible asset, or if the change would push HF below threshold.
|
|
5473
|
+
"function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external",
|
|
5474
|
+
"function setUserEMode(uint8 categoryId) external",
|
|
5244
5475
|
"function getUserAccountData(address user) external view returns (uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor)",
|
|
5245
5476
|
"function getReservesList() external view returns (address[])",
|
|
5246
5477
|
"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)"
|
|
@@ -5366,6 +5597,34 @@ var init_dist2 = __esm({
|
|
|
5366
5597
|
gas_estimate: 25e4
|
|
5367
5598
|
};
|
|
5368
5599
|
}
|
|
5600
|
+
async buildSetUseReserveAsCollateral(asset, useAsCollateral) {
|
|
5601
|
+
const data = encodeFunctionData14({
|
|
5602
|
+
abi: POOL_ABI,
|
|
5603
|
+
functionName: "setUserUseReserveAsCollateral",
|
|
5604
|
+
args: [asset, useAsCollateral]
|
|
5605
|
+
});
|
|
5606
|
+
return {
|
|
5607
|
+
description: `[${this.protocolName}] ${useAsCollateral ? "Enable" : "Disable"} ${asset} as collateral`,
|
|
5608
|
+
to: this.pool,
|
|
5609
|
+
data,
|
|
5610
|
+
value: 0n,
|
|
5611
|
+
gas_estimate: 1e5
|
|
5612
|
+
};
|
|
5613
|
+
}
|
|
5614
|
+
async buildSetEMode(categoryId) {
|
|
5615
|
+
const data = encodeFunctionData14({
|
|
5616
|
+
abi: POOL_ABI,
|
|
5617
|
+
functionName: "setUserEMode",
|
|
5618
|
+
args: [categoryId]
|
|
5619
|
+
});
|
|
5620
|
+
return {
|
|
5621
|
+
description: `[${this.protocolName}] Set eMode category to ${categoryId}${categoryId === 0 ? " (opt out)" : ""}`,
|
|
5622
|
+
to: this.pool,
|
|
5623
|
+
data,
|
|
5624
|
+
value: 0n,
|
|
5625
|
+
gas_estimate: 15e4
|
|
5626
|
+
};
|
|
5627
|
+
}
|
|
5369
5628
|
async getRates(asset) {
|
|
5370
5629
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
5371
5630
|
const reserveCallData = encodeFunctionData14({
|
|
@@ -5410,7 +5669,7 @@ var init_dist2 = __esm({
|
|
|
5410
5669
|
[aTokenAddress, encodeFunctionData14({ abi: INCENTIVES_ABI, functionName: "getIncentivesController" })]
|
|
5411
5670
|
]);
|
|
5412
5671
|
const controllerAddr = decodeAddress4(controllerRaw ?? null);
|
|
5413
|
-
if (controllerAddr && controllerAddr !==
|
|
5672
|
+
if (controllerAddr && controllerAddr !== zeroAddress7) {
|
|
5414
5673
|
const [supplyRewardsRaw, borrowRewardsRaw] = await multicallRead(this.rpcUrl, [
|
|
5415
5674
|
[controllerAddr, encodeFunctionData14({ abi: REWARDS_CONTROLLER_ABI, functionName: "getRewardsByAsset", args: [aTokenAddress] })],
|
|
5416
5675
|
[controllerAddr, encodeFunctionData14({ abi: REWARDS_CONTROLLER_ABI, functionName: "getRewardsByAsset", args: [variableDebtTokenAddress] })]
|
|
@@ -5606,8 +5865,8 @@ var init_dist2 = __esm({
|
|
|
5606
5865
|
if (p.value.borrow > 0n) borrows.push({ asset: p.value.asset, symbol: p.value.symbol, amount: p.value.borrow });
|
|
5607
5866
|
}
|
|
5608
5867
|
} catch {
|
|
5609
|
-
if (collateralUsd > 0) supplies.push({ asset:
|
|
5610
|
-
if (debtUsd > 0) borrows.push({ asset:
|
|
5868
|
+
if (collateralUsd > 0) supplies.push({ asset: zeroAddress7, symbol: "Total Collateral (per-asset breakdown unavailable)", amount: totalCollateralBase });
|
|
5869
|
+
if (debtUsd > 0) borrows.push({ asset: zeroAddress7, symbol: "Total Debt (per-asset breakdown unavailable)", amount: totalDebtBase });
|
|
5611
5870
|
}
|
|
5612
5871
|
return {
|
|
5613
5872
|
protocol: this.protocolName,
|
|
@@ -5774,8 +6033,8 @@ var init_dist2 = __esm({
|
|
|
5774
6033
|
const collateralUsd = u256ToF642(totalCollateralBase) / 1e18;
|
|
5775
6034
|
const debtUsd = u256ToF642(totalDebtBase) / 1e18;
|
|
5776
6035
|
const ltvBps = u256ToF642(ltv);
|
|
5777
|
-
const supplies = collateralUsd > 0 ? [{ asset:
|
|
5778
|
-
const borrows = debtUsd > 0 ? [{ asset:
|
|
6036
|
+
const supplies = collateralUsd > 0 ? [{ asset: zeroAddress8, symbol: "Total Collateral", amount: totalCollateralBase, value_usd: collateralUsd }] : [];
|
|
6037
|
+
const borrows = debtUsd > 0 ? [{ asset: zeroAddress8, symbol: "Total Debt", amount: totalDebtBase, value_usd: debtUsd }] : [];
|
|
5779
6038
|
return {
|
|
5780
6039
|
protocol: this.protocolName,
|
|
5781
6040
|
user,
|
|
@@ -5878,14 +6137,31 @@ var init_dist2 = __esm({
|
|
|
5878
6137
|
"function borrow(uint256 borrowAmount) external returns (uint256)",
|
|
5879
6138
|
"function repayBorrow(uint256 repayAmount) external returns (uint256)"
|
|
5880
6139
|
]);
|
|
6140
|
+
NATIVE_CTOKEN_ABI = parseAbi17([
|
|
6141
|
+
"function mint() external payable",
|
|
6142
|
+
"function repayBorrow() external payable"
|
|
6143
|
+
]);
|
|
6144
|
+
COMPTROLLER_ABI = parseAbi17([
|
|
6145
|
+
"function enterMarkets(address[] cTokens) external returns (uint256[])",
|
|
6146
|
+
"function exitMarket(address cToken) external returns (uint256)"
|
|
6147
|
+
]);
|
|
6148
|
+
NATIVE_SENTINEL = "0x0000000000000000000000000000000000000000";
|
|
5881
6149
|
BSC_BLOCKS_PER_YEAR = 10512e3;
|
|
5882
6150
|
CompoundV2Adapter = class {
|
|
5883
6151
|
protocolName;
|
|
5884
6152
|
defaultVtoken;
|
|
5885
6153
|
vTokenCandidates;
|
|
6154
|
+
comptroller;
|
|
5886
6155
|
rpcUrl;
|
|
5887
|
-
// Lazy cache: underlying asset address (lowercased) → vToken address
|
|
6156
|
+
// Lazy cache: underlying asset address (lowercased) → vToken address.
|
|
6157
|
+
// The native sentinel (0x0…) is mapped to the cETH/vBNB-style vToken
|
|
6158
|
+
// when one is detected during resolveVtoken().
|
|
5888
6159
|
vTokenByAsset = null;
|
|
6160
|
+
// The cETH/vBNB-style vToken whose underlying() reverts (it has no
|
|
6161
|
+
// ERC20 underlying — the underlying is the chain's native gas token).
|
|
6162
|
+
// Set lazily by resolveVtoken() and consulted by buildSupply/buildRepay
|
|
6163
|
+
// to switch to the payable mint() / repayBorrow() variants.
|
|
6164
|
+
nativeVtoken = null;
|
|
5889
6165
|
constructor(entry, rpcUrl) {
|
|
5890
6166
|
this.protocolName = entry.name;
|
|
5891
6167
|
this.rpcUrl = rpcUrl;
|
|
@@ -5893,6 +6169,7 @@ var init_dist2 = __esm({
|
|
|
5893
6169
|
const vtoken = contracts["vusdt"] ?? contracts["vusdc"] ?? contracts["vbnb"] ?? contracts["comptroller"];
|
|
5894
6170
|
if (!vtoken) throw DefiError.contractError("Missing vToken or comptroller address");
|
|
5895
6171
|
this.defaultVtoken = vtoken;
|
|
6172
|
+
this.comptroller = contracts["comptroller"];
|
|
5896
6173
|
this.vTokenCandidates = Object.entries(contracts).filter(([k]) => /^v[a-z][a-z0-9]*$/i.test(k)).map(([, v]) => v);
|
|
5897
6174
|
if (this.vTokenCandidates.length === 0) this.vTokenCandidates = [vtoken];
|
|
5898
6175
|
}
|
|
@@ -5901,19 +6178,38 @@ var init_dist2 = __esm({
|
|
|
5901
6178
|
if (!this.vTokenByAsset) {
|
|
5902
6179
|
const client = createPublicClient13({ transport: http13(this.rpcUrl) });
|
|
5903
6180
|
const map = /* @__PURE__ */ new Map();
|
|
6181
|
+
let nativeVtoken = null;
|
|
5904
6182
|
const lookups = await Promise.allSettled(
|
|
5905
6183
|
this.vTokenCandidates.map(async (v) => {
|
|
5906
|
-
|
|
5907
|
-
|
|
6184
|
+
try {
|
|
6185
|
+
const u = await client.readContract({ address: v, abi: CTOKEN_ABI, functionName: "underlying" });
|
|
6186
|
+
return { vtoken: v, underlying: u };
|
|
6187
|
+
} catch {
|
|
6188
|
+
return { vtoken: v, underlying: null };
|
|
6189
|
+
}
|
|
5908
6190
|
})
|
|
5909
6191
|
);
|
|
5910
6192
|
for (const r of lookups) {
|
|
5911
|
-
if (r.status
|
|
6193
|
+
if (r.status !== "fulfilled") continue;
|
|
6194
|
+
const { vtoken, underlying } = r.value;
|
|
6195
|
+
if (underlying) {
|
|
6196
|
+
map.set(underlying.toLowerCase(), vtoken);
|
|
6197
|
+
} else if (!nativeVtoken) {
|
|
6198
|
+
nativeVtoken = vtoken;
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
6201
|
+
if (nativeVtoken) {
|
|
6202
|
+
map.set(NATIVE_SENTINEL, nativeVtoken);
|
|
5912
6203
|
}
|
|
5913
6204
|
this.vTokenByAsset = map;
|
|
6205
|
+
this.nativeVtoken = nativeVtoken;
|
|
5914
6206
|
}
|
|
5915
6207
|
return this.vTokenByAsset.get(asset.toLowerCase()) ?? null;
|
|
5916
6208
|
}
|
|
6209
|
+
/** True iff `vtoken` is the cETH/vBNB-style native cToken for this protocol. */
|
|
6210
|
+
isNativeVtoken(vtoken) {
|
|
6211
|
+
return this.nativeVtoken !== null && vtoken.toLowerCase() === this.nativeVtoken.toLowerCase();
|
|
6212
|
+
}
|
|
5917
6213
|
name() {
|
|
5918
6214
|
return this.protocolName;
|
|
5919
6215
|
}
|
|
@@ -5927,6 +6223,16 @@ var init_dist2 = __esm({
|
|
|
5927
6223
|
}
|
|
5928
6224
|
async buildSupply(params) {
|
|
5929
6225
|
const vtoken = await this.vtokenFor(params.asset);
|
|
6226
|
+
if (this.isNativeVtoken(vtoken)) {
|
|
6227
|
+
const data2 = encodeFunctionData16({ abi: NATIVE_CTOKEN_ABI, functionName: "mint" });
|
|
6228
|
+
return {
|
|
6229
|
+
description: `[${this.protocolName}] Supply ${params.amount} (native) to Venus`,
|
|
6230
|
+
to: vtoken,
|
|
6231
|
+
data: data2,
|
|
6232
|
+
value: params.amount,
|
|
6233
|
+
gas_estimate: 3e5
|
|
6234
|
+
};
|
|
6235
|
+
}
|
|
5930
6236
|
const data = encodeFunctionData16({ abi: CTOKEN_ABI, functionName: "mint", args: [params.amount] });
|
|
5931
6237
|
return {
|
|
5932
6238
|
description: `[${this.protocolName}] Supply ${params.amount} of ${params.asset} to Venus`,
|
|
@@ -5950,6 +6256,16 @@ var init_dist2 = __esm({
|
|
|
5950
6256
|
}
|
|
5951
6257
|
async buildRepay(params) {
|
|
5952
6258
|
const vtoken = await this.vtokenFor(params.asset);
|
|
6259
|
+
if (this.isNativeVtoken(vtoken)) {
|
|
6260
|
+
const data2 = encodeFunctionData16({ abi: NATIVE_CTOKEN_ABI, functionName: "repayBorrow" });
|
|
6261
|
+
return {
|
|
6262
|
+
description: `[${this.protocolName}] Repay ${params.amount} (native) to Venus`,
|
|
6263
|
+
to: vtoken,
|
|
6264
|
+
data: data2,
|
|
6265
|
+
value: params.amount,
|
|
6266
|
+
gas_estimate: 3e5
|
|
6267
|
+
};
|
|
6268
|
+
}
|
|
5953
6269
|
const data = encodeFunctionData16({ abi: CTOKEN_ABI, functionName: "repayBorrow", args: [params.amount] });
|
|
5954
6270
|
return {
|
|
5955
6271
|
description: `[${this.protocolName}] Repay ${params.amount} of ${params.asset} to Venus`,
|
|
@@ -5962,6 +6278,37 @@ var init_dist2 = __esm({
|
|
|
5962
6278
|
}
|
|
5963
6279
|
async buildWithdraw(params) {
|
|
5964
6280
|
const vtoken = await this.vtokenFor(params.asset);
|
|
6281
|
+
const MAX_UINT2562 = (1n << 256n) - 1n;
|
|
6282
|
+
if (params.amount === MAX_UINT2562 && this.rpcUrl) {
|
|
6283
|
+
const client = createPublicClient13({ transport: http13(this.rpcUrl) });
|
|
6284
|
+
const [vtokenBalance, borrowBalance] = await Promise.all([
|
|
6285
|
+
client.readContract({
|
|
6286
|
+
address: vtoken,
|
|
6287
|
+
abi: CTOKEN_ABI,
|
|
6288
|
+
functionName: "balanceOf",
|
|
6289
|
+
args: [params.to]
|
|
6290
|
+
}),
|
|
6291
|
+
client.readContract({
|
|
6292
|
+
address: vtoken,
|
|
6293
|
+
abi: CTOKEN_ABI,
|
|
6294
|
+
functionName: "borrowBalanceStored",
|
|
6295
|
+
args: [params.to]
|
|
6296
|
+
}).catch(() => 0n)
|
|
6297
|
+
]);
|
|
6298
|
+
if (borrowBalance > 0n) {
|
|
6299
|
+
throw DefiError.contractError(
|
|
6300
|
+
`[${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.`
|
|
6301
|
+
);
|
|
6302
|
+
}
|
|
6303
|
+
const redeemData = encodeFunctionData16({ abi: CTOKEN_ABI, functionName: "redeem", args: [vtokenBalance] });
|
|
6304
|
+
return {
|
|
6305
|
+
description: `[${this.protocolName}] Withdraw all (auto-max, ${vtokenBalance} vTokens) of ${params.asset} from Venus`,
|
|
6306
|
+
to: vtoken,
|
|
6307
|
+
data: redeemData,
|
|
6308
|
+
value: 0n,
|
|
6309
|
+
gas_estimate: 35e4
|
|
6310
|
+
};
|
|
6311
|
+
}
|
|
5965
6312
|
const data = encodeFunctionData16({ abi: CTOKEN_ABI, functionName: "redeemUnderlying", args: [params.amount] });
|
|
5966
6313
|
return {
|
|
5967
6314
|
description: `[${this.protocolName}] Withdraw ${params.amount} of ${params.asset} from Venus`,
|
|
@@ -5971,6 +6318,37 @@ var init_dist2 = __esm({
|
|
|
5971
6318
|
gas_estimate: 25e4
|
|
5972
6319
|
};
|
|
5973
6320
|
}
|
|
6321
|
+
/**
|
|
6322
|
+
* Compound V2 family: enter cTokens as collateral via Comptroller.
|
|
6323
|
+
* Without this call, supplied assets sit dormant in the Comptroller's
|
|
6324
|
+
* accountAssets[] and `getAccountLiquidity` reports zero collateral —
|
|
6325
|
+
* any borrow then reverts. Mirrors the role of Aave V3's
|
|
6326
|
+
* setUserUseReserveAsCollateral, but the API is batch-by-cToken.
|
|
6327
|
+
*/
|
|
6328
|
+
async buildEnterMarkets(cTokens) {
|
|
6329
|
+
if (!this.comptroller) {
|
|
6330
|
+
throw DefiError.contractError(
|
|
6331
|
+
`[${this.protocolName}] enterMarkets requires the Comptroller address to be registered under [protocol.contracts] as 'comptroller'.`
|
|
6332
|
+
);
|
|
6333
|
+
}
|
|
6334
|
+
if (cTokens.length === 0) {
|
|
6335
|
+
throw DefiError.invalidParam(
|
|
6336
|
+
`[${this.protocolName}] enterMarkets requires at least one cToken address.`
|
|
6337
|
+
);
|
|
6338
|
+
}
|
|
6339
|
+
const data = encodeFunctionData16({
|
|
6340
|
+
abi: COMPTROLLER_ABI,
|
|
6341
|
+
functionName: "enterMarkets",
|
|
6342
|
+
args: [cTokens]
|
|
6343
|
+
});
|
|
6344
|
+
return {
|
|
6345
|
+
description: `[${this.protocolName}] Enter ${cTokens.length} market(s) as collateral`,
|
|
6346
|
+
to: this.comptroller,
|
|
6347
|
+
data,
|
|
6348
|
+
value: 0n,
|
|
6349
|
+
gas_estimate: 2e5
|
|
6350
|
+
};
|
|
6351
|
+
}
|
|
5974
6352
|
async getRates(asset) {
|
|
5975
6353
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
5976
6354
|
const client = createPublicClient13({ transport: http13(this.rpcUrl) });
|
|
@@ -6338,9 +6716,11 @@ var init_dist2 = __esm({
|
|
|
6338
6716
|
"function market(bytes32 id) external view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee)",
|
|
6339
6717
|
"function idToMarketParams(bytes32 id) external view returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv)",
|
|
6340
6718
|
"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)",
|
|
6719
|
+
"function supplyCollateral((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, bytes data) external",
|
|
6341
6720
|
"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)",
|
|
6342
6721
|
"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)",
|
|
6343
|
-
"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)"
|
|
6722
|
+
"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)",
|
|
6723
|
+
"function withdrawCollateral((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, address receiver) external"
|
|
6344
6724
|
]);
|
|
6345
6725
|
META_MORPHO_ABI = parseAbi20([
|
|
6346
6726
|
"function supplyQueueLength() external view returns (uint256)",
|
|
@@ -6367,6 +6747,8 @@ var init_dist2 = __esm({
|
|
|
6367
6747
|
rpcUrl;
|
|
6368
6748
|
metaMorphoVaults;
|
|
6369
6749
|
metaMorphoVaultEntries;
|
|
6750
|
+
namedMarkets;
|
|
6751
|
+
namedMarketByName;
|
|
6370
6752
|
vaultAssetMap = null;
|
|
6371
6753
|
constructor(entry, rpcUrl) {
|
|
6372
6754
|
this.protocolName = entry.name;
|
|
@@ -6378,6 +6760,26 @@ var init_dist2 = __esm({
|
|
|
6378
6760
|
this.defaultVault = contracts["fehype"] ?? contracts["vault"] ?? contracts["feusdc"];
|
|
6379
6761
|
this.metaMorphoVaultEntries = Object.entries(contracts).filter(([key]) => /^fe[a-z0-9_]+$/i.test(key) || key === "vault").map(([key, addr]) => ({ key, addr }));
|
|
6380
6762
|
this.metaMorphoVaults = this.metaMorphoVaultEntries.map((e) => e.addr);
|
|
6763
|
+
this.namedMarkets = entry.markets ?? [];
|
|
6764
|
+
const byName = /* @__PURE__ */ new Map();
|
|
6765
|
+
for (const m of this.namedMarkets) byName.set(m.name.toLowerCase(), m.id);
|
|
6766
|
+
this.namedMarketByName = byName;
|
|
6767
|
+
}
|
|
6768
|
+
/**
|
|
6769
|
+
* Resolve a friendly market name (e.g. `WMON-AUSD`) to its 32-byte
|
|
6770
|
+
* marketId via the per-protocol TOML registry. Returns null when the
|
|
6771
|
+
* adapter has no markets[] block or the name doesn't match any entry —
|
|
6772
|
+
* callers fall back to treating the input as a raw hex marketId.
|
|
6773
|
+
*/
|
|
6774
|
+
resolveMarketIdByName(name) {
|
|
6775
|
+
return this.namedMarketByName.get(name.toLowerCase()) ?? null;
|
|
6776
|
+
}
|
|
6777
|
+
/**
|
|
6778
|
+
* Returns the registered named markets for diagnostics (e.g. CLI error
|
|
6779
|
+
* messages listing valid choices when the user passes an unknown name).
|
|
6780
|
+
*/
|
|
6781
|
+
listNamedMarkets() {
|
|
6782
|
+
return this.namedMarkets;
|
|
6381
6783
|
}
|
|
6382
6784
|
async resolveVault(asset, preferKey) {
|
|
6383
6785
|
if (this.metaMorphoVaultEntries.length === 0 || !this.rpcUrl) return null;
|
|
@@ -6411,10 +6813,61 @@ var init_dist2 = __esm({
|
|
|
6411
6813
|
name() {
|
|
6412
6814
|
return this.protocolName;
|
|
6413
6815
|
}
|
|
6816
|
+
/**
|
|
6817
|
+
* Resolve a Morpho Blue marketId into the full MarketParams tuple by
|
|
6818
|
+
* calling Morpho.idToMarketParams(id). Used by every direct-market
|
|
6819
|
+
* method (supply / borrow / repay / withdraw / supplyCollateral /
|
|
6820
|
+
* withdrawCollateral) so the caller only has to pass the 32-byte
|
|
6821
|
+
* marketId — same shape as the Morpho UI / API.
|
|
6822
|
+
*/
|
|
6823
|
+
async resolveMarketParams(marketId) {
|
|
6824
|
+
if (!this.rpcUrl) {
|
|
6825
|
+
throw DefiError.rpcError(
|
|
6826
|
+
`[${this.protocolName}] No RPC URL configured \u2014 cannot resolve marketId ${marketId}`
|
|
6827
|
+
);
|
|
6828
|
+
}
|
|
6829
|
+
const client = createPublicClient16({ transport: http16(this.rpcUrl) });
|
|
6830
|
+
let result;
|
|
6831
|
+
try {
|
|
6832
|
+
result = await client.readContract({
|
|
6833
|
+
address: this.morpho,
|
|
6834
|
+
abi: MORPHO_ABI,
|
|
6835
|
+
functionName: "idToMarketParams",
|
|
6836
|
+
args: [marketId]
|
|
6837
|
+
});
|
|
6838
|
+
} catch (e) {
|
|
6839
|
+
throw DefiError.rpcError(
|
|
6840
|
+
`[${this.protocolName}] idToMarketParams(${marketId}) failed: ${e}`
|
|
6841
|
+
);
|
|
6842
|
+
}
|
|
6843
|
+
const [loanToken, collateralToken, oracle, irm, lltv] = result;
|
|
6844
|
+
if (loanToken === zeroAddress9 || collateralToken === zeroAddress9 || lltv === 0n) {
|
|
6845
|
+
throw DefiError.invalidParam(
|
|
6846
|
+
`[${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.`
|
|
6847
|
+
);
|
|
6848
|
+
}
|
|
6849
|
+
return { loanToken, collateralToken, oracle, irm, lltv };
|
|
6850
|
+
}
|
|
6414
6851
|
async buildSupply(params) {
|
|
6852
|
+
if (params.market_id) {
|
|
6853
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6854
|
+
const data = encodeFunctionData19({
|
|
6855
|
+
abi: MORPHO_ABI,
|
|
6856
|
+
functionName: "supply",
|
|
6857
|
+
args: [market, params.amount, 0n, params.on_behalf_of, "0x"]
|
|
6858
|
+
});
|
|
6859
|
+
return {
|
|
6860
|
+
description: `[${this.protocolName}] Supply ${params.amount} of ${params.asset} to market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6861
|
+
to: this.morpho,
|
|
6862
|
+
data,
|
|
6863
|
+
value: 0n,
|
|
6864
|
+
gas_estimate: 35e4,
|
|
6865
|
+
approvals: [{ token: params.asset, spender: this.morpho, amount: params.amount }]
|
|
6866
|
+
};
|
|
6867
|
+
}
|
|
6415
6868
|
const vault = await this.resolveVault(params.asset);
|
|
6416
6869
|
if (vault) {
|
|
6417
|
-
const
|
|
6870
|
+
const data = encodeFunctionData19({
|
|
6418
6871
|
abi: ERC4626_ABI,
|
|
6419
6872
|
functionName: "deposit",
|
|
6420
6873
|
args: [params.amount, params.on_behalf_of]
|
|
@@ -6422,50 +6875,118 @@ var init_dist2 = __esm({
|
|
|
6422
6875
|
return {
|
|
6423
6876
|
description: `[${this.protocolName}] Deposit ${params.amount} into MetaMorpho vault`,
|
|
6424
6877
|
to: vault,
|
|
6425
|
-
data
|
|
6878
|
+
data,
|
|
6426
6879
|
value: 0n,
|
|
6427
6880
|
gas_estimate: 4e5,
|
|
6428
6881
|
approvals: [{ token: params.asset, spender: vault, amount: params.amount }]
|
|
6429
6882
|
};
|
|
6430
6883
|
}
|
|
6431
|
-
|
|
6884
|
+
throw DefiError.invalidParam(
|
|
6885
|
+
`[${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).`
|
|
6886
|
+
);
|
|
6887
|
+
}
|
|
6888
|
+
async buildBorrow(params) {
|
|
6889
|
+
if (!params.market_id) {
|
|
6890
|
+
throw DefiError.invalidParam(
|
|
6891
|
+
`[${this.protocolName}] Morpho Blue borrow requires --market <marketId>. Find one via the Morpho API (https://blue-api.morpho.org/graphql).`
|
|
6892
|
+
);
|
|
6893
|
+
}
|
|
6894
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6895
|
+
const data = encodeFunctionData19({
|
|
6896
|
+
abi: MORPHO_ABI,
|
|
6897
|
+
functionName: "borrow",
|
|
6898
|
+
args: [market, params.amount, 0n, params.on_behalf_of, params.on_behalf_of]
|
|
6899
|
+
});
|
|
6900
|
+
return {
|
|
6901
|
+
description: `[${this.protocolName}] Borrow ${params.amount} of ${params.asset} from market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6902
|
+
to: this.morpho,
|
|
6903
|
+
data,
|
|
6904
|
+
value: 0n,
|
|
6905
|
+
gas_estimate: 4e5
|
|
6906
|
+
};
|
|
6907
|
+
}
|
|
6908
|
+
async buildRepay(params) {
|
|
6909
|
+
if (!params.market_id) {
|
|
6910
|
+
throw DefiError.invalidParam(
|
|
6911
|
+
`[${this.protocolName}] Morpho Blue repay requires --market <marketId>.`
|
|
6912
|
+
);
|
|
6913
|
+
}
|
|
6914
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6915
|
+
if (params.amount === MAX_UINT256) {
|
|
6916
|
+
if (!this.rpcUrl) {
|
|
6917
|
+
throw DefiError.rpcError(
|
|
6918
|
+
`[${this.protocolName}] max-repay requires an RPC URL to read borrowShares.`
|
|
6919
|
+
);
|
|
6920
|
+
}
|
|
6921
|
+
const client = createPublicClient16({ transport: http16(this.rpcUrl) });
|
|
6922
|
+
const positionAbi = parseAbi20([
|
|
6923
|
+
"function position(bytes32 id, address user) external view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral)"
|
|
6924
|
+
]);
|
|
6925
|
+
const pos = await client.readContract({
|
|
6926
|
+
address: this.morpho,
|
|
6927
|
+
abi: positionAbi,
|
|
6928
|
+
functionName: "position",
|
|
6929
|
+
args: [params.market_id, params.on_behalf_of]
|
|
6930
|
+
});
|
|
6931
|
+
const [, borrowShares] = pos;
|
|
6932
|
+
if (borrowShares === 0n) {
|
|
6933
|
+
throw DefiError.invalidParam(
|
|
6934
|
+
`[${this.protocolName}] cannot repay max \u2014 user has no borrow position in market ${params.market_id}.`
|
|
6935
|
+
);
|
|
6936
|
+
}
|
|
6937
|
+
const data2 = encodeFunctionData19({
|
|
6938
|
+
abi: MORPHO_ABI,
|
|
6939
|
+
functionName: "repay",
|
|
6940
|
+
args: [market, 0n, borrowShares, params.on_behalf_of, "0x"]
|
|
6941
|
+
});
|
|
6942
|
+
return {
|
|
6943
|
+
description: `[${this.protocolName}] Repay max (${borrowShares} shares) to market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6944
|
+
to: this.morpho,
|
|
6945
|
+
data: data2,
|
|
6946
|
+
value: 0n,
|
|
6947
|
+
gas_estimate: 35e4,
|
|
6948
|
+
approvals: [{ token: params.asset, spender: this.morpho, amount: MAX_UINT256 }]
|
|
6949
|
+
};
|
|
6950
|
+
}
|
|
6432
6951
|
const data = encodeFunctionData19({
|
|
6433
6952
|
abi: MORPHO_ABI,
|
|
6434
|
-
functionName: "
|
|
6953
|
+
functionName: "repay",
|
|
6435
6954
|
args: [market, params.amount, 0n, params.on_behalf_of, "0x"]
|
|
6436
6955
|
});
|
|
6437
6956
|
return {
|
|
6438
|
-
description: `[${this.protocolName}]
|
|
6957
|
+
description: `[${this.protocolName}] Repay ${params.amount} of ${params.asset} to market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6439
6958
|
to: this.morpho,
|
|
6440
6959
|
data,
|
|
6441
6960
|
value: 0n,
|
|
6442
|
-
gas_estimate:
|
|
6961
|
+
gas_estimate: 35e4,
|
|
6962
|
+
approvals: [{ token: params.asset, spender: this.morpho, amount: params.amount }]
|
|
6443
6963
|
};
|
|
6444
6964
|
}
|
|
6445
|
-
async
|
|
6446
|
-
const market =
|
|
6965
|
+
async buildSupplyCollateral(params) {
|
|
6966
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6447
6967
|
const data = encodeFunctionData19({
|
|
6448
6968
|
abi: MORPHO_ABI,
|
|
6449
|
-
functionName: "
|
|
6450
|
-
args: [market, params.amount,
|
|
6969
|
+
functionName: "supplyCollateral",
|
|
6970
|
+
args: [market, params.amount, params.on_behalf_of, "0x"]
|
|
6451
6971
|
});
|
|
6452
6972
|
return {
|
|
6453
|
-
description: `[${this.protocolName}]
|
|
6973
|
+
description: `[${this.protocolName}] Supply collateral ${params.amount} of ${params.asset} to market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6454
6974
|
to: this.morpho,
|
|
6455
6975
|
data,
|
|
6456
6976
|
value: 0n,
|
|
6457
|
-
gas_estimate: 35e4
|
|
6977
|
+
gas_estimate: 35e4,
|
|
6978
|
+
approvals: [{ token: params.asset, spender: this.morpho, amount: params.amount }]
|
|
6458
6979
|
};
|
|
6459
6980
|
}
|
|
6460
|
-
async
|
|
6461
|
-
const market =
|
|
6981
|
+
async buildWithdrawCollateral(params) {
|
|
6982
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6462
6983
|
const data = encodeFunctionData19({
|
|
6463
6984
|
abi: MORPHO_ABI,
|
|
6464
|
-
functionName: "
|
|
6465
|
-
args: [market, params.amount,
|
|
6985
|
+
functionName: "withdrawCollateral",
|
|
6986
|
+
args: [market, params.amount, params.to, params.to]
|
|
6466
6987
|
});
|
|
6467
6988
|
return {
|
|
6468
|
-
description: `[${this.protocolName}]
|
|
6989
|
+
description: `[${this.protocolName}] Withdraw collateral ${params.amount} of ${params.asset} from market ${params.market_id.slice(0, 10)}\u2026`,
|
|
6469
6990
|
to: this.morpho,
|
|
6470
6991
|
data,
|
|
6471
6992
|
value: 0n,
|
|
@@ -6473,6 +6994,21 @@ var init_dist2 = __esm({
|
|
|
6473
6994
|
};
|
|
6474
6995
|
}
|
|
6475
6996
|
async buildWithdraw(params) {
|
|
6997
|
+
if (params.market_id) {
|
|
6998
|
+
const market = await this.resolveMarketParams(params.market_id);
|
|
6999
|
+
const data = encodeFunctionData19({
|
|
7000
|
+
abi: MORPHO_ABI,
|
|
7001
|
+
functionName: "withdraw",
|
|
7002
|
+
args: [market, params.amount, 0n, params.to, params.to]
|
|
7003
|
+
});
|
|
7004
|
+
return {
|
|
7005
|
+
description: `[${this.protocolName}] Withdraw ${params.amount} of ${params.asset} from market ${params.market_id.slice(0, 10)}\u2026`,
|
|
7006
|
+
to: this.morpho,
|
|
7007
|
+
data,
|
|
7008
|
+
value: 0n,
|
|
7009
|
+
gas_estimate: 3e5
|
|
7010
|
+
};
|
|
7011
|
+
}
|
|
6476
7012
|
const vault = await this.resolveVault(params.asset);
|
|
6477
7013
|
if (vault) {
|
|
6478
7014
|
if (params.amount === MAX_UINT256) {
|
|
@@ -6481,7 +7017,7 @@ var init_dist2 = __esm({
|
|
|
6481
7017
|
[vault, encodeFunctionData19({ abi: ERC4626_ABI, functionName: "balanceOf", args: [params.to] })]
|
|
6482
7018
|
]);
|
|
6483
7019
|
const shares = decodeU256(balRaw ?? null);
|
|
6484
|
-
const
|
|
7020
|
+
const data2 = encodeFunctionData19({
|
|
6485
7021
|
abi: ERC4626_ABI,
|
|
6486
7022
|
functionName: "redeem",
|
|
6487
7023
|
args: [shares, params.to, params.to]
|
|
@@ -6489,12 +7025,12 @@ var init_dist2 = __esm({
|
|
|
6489
7025
|
return {
|
|
6490
7026
|
description: `[${this.protocolName}] Redeem all shares (${shares}) from MetaMorpho vault`,
|
|
6491
7027
|
to: vault,
|
|
6492
|
-
data:
|
|
7028
|
+
data: data2,
|
|
6493
7029
|
value: 0n,
|
|
6494
7030
|
gas_estimate: 4e5
|
|
6495
7031
|
};
|
|
6496
7032
|
}
|
|
6497
|
-
const
|
|
7033
|
+
const data = encodeFunctionData19({
|
|
6498
7034
|
abi: ERC4626_ABI,
|
|
6499
7035
|
functionName: "withdraw",
|
|
6500
7036
|
args: [params.amount, params.to, params.to]
|
|
@@ -6502,24 +7038,14 @@ var init_dist2 = __esm({
|
|
|
6502
7038
|
return {
|
|
6503
7039
|
description: `[${this.protocolName}] Withdraw ${params.amount} assets from MetaMorpho vault`,
|
|
6504
7040
|
to: vault,
|
|
6505
|
-
data
|
|
7041
|
+
data,
|
|
6506
7042
|
value: 0n,
|
|
6507
7043
|
gas_estimate: 4e5
|
|
6508
7044
|
};
|
|
6509
7045
|
}
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
functionName: "withdraw",
|
|
6514
|
-
args: [market, params.amount, 0n, params.to, params.to]
|
|
6515
|
-
});
|
|
6516
|
-
return {
|
|
6517
|
-
description: `[${this.protocolName}] Withdraw ${params.amount} from Morpho market`,
|
|
6518
|
-
to: this.morpho,
|
|
6519
|
-
data,
|
|
6520
|
-
value: 0n,
|
|
6521
|
-
gas_estimate: 25e4
|
|
6522
|
-
};
|
|
7046
|
+
throw DefiError.invalidParam(
|
|
7047
|
+
`[${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).`
|
|
7048
|
+
);
|
|
6523
7049
|
}
|
|
6524
7050
|
async getRates(asset) {
|
|
6525
7051
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
@@ -6636,7 +7162,7 @@ var init_dist2 = __esm({
|
|
|
6636
7162
|
if (!this.hintHelpers || !this.sortedTroves || !this.rpcUrl) {
|
|
6637
7163
|
return [0n, 0n];
|
|
6638
7164
|
}
|
|
6639
|
-
const client =
|
|
7165
|
+
const client = createPublicClient17({ transport: http17(this.rpcUrl) });
|
|
6640
7166
|
const approxResult = await client.readContract({
|
|
6641
7167
|
address: this.hintHelpers,
|
|
6642
7168
|
abi: HINT_HELPERS_ABI,
|
|
@@ -6727,7 +7253,7 @@ var init_dist2 = __esm({
|
|
|
6727
7253
|
async getCdpInfo(cdpId) {
|
|
6728
7254
|
if (!this.rpcUrl) throw DefiError.rpcError(`[${this.protocolName}] getCdpInfo requires RPC \u2014 set HYPEREVM_RPC_URL`);
|
|
6729
7255
|
if (!this.troveManager) throw DefiError.contractError(`[${this.protocolName}] trove_manager contract not configured`);
|
|
6730
|
-
const client =
|
|
7256
|
+
const client = createPublicClient17({ transport: http17(this.rpcUrl) });
|
|
6731
7257
|
const data = await client.readContract({
|
|
6732
7258
|
address: this.troveManager,
|
|
6733
7259
|
abi: TROVE_MANAGER_ABI,
|
|
@@ -6745,13 +7271,13 @@ var init_dist2 = __esm({
|
|
|
6745
7271
|
protocol: this.protocolName,
|
|
6746
7272
|
cdp_id: cdpId,
|
|
6747
7273
|
collateral: {
|
|
6748
|
-
token:
|
|
7274
|
+
token: zeroAddress10,
|
|
6749
7275
|
symbol: "WHYPE",
|
|
6750
7276
|
amount: entireColl,
|
|
6751
7277
|
decimals: 18
|
|
6752
7278
|
},
|
|
6753
7279
|
debt: {
|
|
6754
|
-
token:
|
|
7280
|
+
token: zeroAddress10,
|
|
6755
7281
|
symbol: "feUSD",
|
|
6756
7282
|
amount: entireDebt,
|
|
6757
7283
|
decimals: 18
|
|
@@ -6786,7 +7312,7 @@ var init_dist2 = __esm({
|
|
|
6786
7312
|
if (asset !== this.asset && this.asset !== "0x0000000000000000000000000000000000000000") {
|
|
6787
7313
|
throw DefiError.unsupported(`[${this.protocolName}] Felix PriceFeed only supports asset ${this.asset}`);
|
|
6788
7314
|
}
|
|
6789
|
-
const client =
|
|
7315
|
+
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
6790
7316
|
let priceVal;
|
|
6791
7317
|
try {
|
|
6792
7318
|
const result = await client.readContract({
|
|
@@ -6878,7 +7404,7 @@ var init_dist2 = __esm({
|
|
|
6878
7404
|
}
|
|
6879
7405
|
async totalAssets() {
|
|
6880
7406
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6881
|
-
const client =
|
|
7407
|
+
const client = createPublicClient19({ transport: http19(this.rpcUrl) });
|
|
6882
7408
|
return client.readContract({
|
|
6883
7409
|
address: this.vaultAddress,
|
|
6884
7410
|
abi: ERC4626_ABI2,
|
|
@@ -6889,7 +7415,7 @@ var init_dist2 = __esm({
|
|
|
6889
7415
|
}
|
|
6890
7416
|
async convertToShares(assets) {
|
|
6891
7417
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6892
|
-
const client =
|
|
7418
|
+
const client = createPublicClient19({ transport: http19(this.rpcUrl) });
|
|
6893
7419
|
return client.readContract({
|
|
6894
7420
|
address: this.vaultAddress,
|
|
6895
7421
|
abi: ERC4626_ABI2,
|
|
@@ -6901,7 +7427,7 @@ var init_dist2 = __esm({
|
|
|
6901
7427
|
}
|
|
6902
7428
|
async convertToAssets(shares) {
|
|
6903
7429
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6904
|
-
const client =
|
|
7430
|
+
const client = createPublicClient19({ transport: http19(this.rpcUrl) });
|
|
6905
7431
|
return client.readContract({
|
|
6906
7432
|
address: this.vaultAddress,
|
|
6907
7433
|
abi: ERC4626_ABI2,
|
|
@@ -6913,7 +7439,7 @@ var init_dist2 = __esm({
|
|
|
6913
7439
|
}
|
|
6914
7440
|
async getVaultInfo() {
|
|
6915
7441
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6916
|
-
const client =
|
|
7442
|
+
const client = createPublicClient19({ transport: http19(this.rpcUrl) });
|
|
6917
7443
|
const [totalAssets, totalSupply, asset] = await Promise.all([
|
|
6918
7444
|
client.readContract({ address: this.vaultAddress, abi: ERC4626_ABI2, functionName: "totalAssets" }).catch((e) => {
|
|
6919
7445
|
throw DefiError.rpcError(`[${this.protocolName}] totalAssets failed: ${e}`);
|
|
@@ -7005,7 +7531,7 @@ var init_dist2 = __esm({
|
|
|
7005
7531
|
const data = encodeFunctionData23({
|
|
7006
7532
|
abi: STHYPE_ABI,
|
|
7007
7533
|
functionName: "submit",
|
|
7008
|
-
args: [
|
|
7534
|
+
args: [zeroAddress11]
|
|
7009
7535
|
});
|
|
7010
7536
|
return {
|
|
7011
7537
|
description: `[${this.protocolName}] Stake ${params.amount} HYPE for stHYPE`,
|
|
@@ -7031,7 +7557,7 @@ var init_dist2 = __esm({
|
|
|
7031
7557
|
}
|
|
7032
7558
|
async getInfo() {
|
|
7033
7559
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7034
|
-
const client =
|
|
7560
|
+
const client = createPublicClient20({ transport: http20(this.rpcUrl) });
|
|
7035
7561
|
const tokenAddr = this.sthypeToken ?? this.staking;
|
|
7036
7562
|
const totalSupply = await client.readContract({
|
|
7037
7563
|
address: tokenAddr,
|
|
@@ -7042,7 +7568,7 @@ var init_dist2 = __esm({
|
|
|
7042
7568
|
});
|
|
7043
7569
|
return {
|
|
7044
7570
|
protocol: this.protocolName,
|
|
7045
|
-
staked_token:
|
|
7571
|
+
staked_token: zeroAddress11,
|
|
7046
7572
|
liquid_token: tokenAddr,
|
|
7047
7573
|
exchange_rate: 1,
|
|
7048
7574
|
total_staked: totalSupply
|
|
@@ -7101,7 +7627,7 @@ var init_dist2 = __esm({
|
|
|
7101
7627
|
}
|
|
7102
7628
|
async getInfo() {
|
|
7103
7629
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7104
|
-
const client =
|
|
7630
|
+
const client = createPublicClient21({ transport: http21(this.rpcUrl) });
|
|
7105
7631
|
const totalStaked = await client.readContract({
|
|
7106
7632
|
address: this.staking,
|
|
7107
7633
|
abi: KINETIQ_ABI,
|
|
@@ -7116,7 +7642,7 @@ var init_dist2 = __esm({
|
|
|
7116
7642
|
const rateF64 = hypePrice > 0n && khypePrice > 0n ? Number(khypePrice * 10n ** 18n / hypePrice) / 1e18 : 1;
|
|
7117
7643
|
return {
|
|
7118
7644
|
protocol: this.protocolName,
|
|
7119
|
-
staked_token:
|
|
7645
|
+
staked_token: zeroAddress12,
|
|
7120
7646
|
liquid_token: this.liquidToken,
|
|
7121
7647
|
exchange_rate: rateF64,
|
|
7122
7648
|
total_staked: totalStaked
|
|
@@ -7336,7 +7862,7 @@ var init_dist2 = __esm({
|
|
|
7336
7862
|
}
|
|
7337
7863
|
async getCollectionInfo(collection) {
|
|
7338
7864
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7339
|
-
const client =
|
|
7865
|
+
const client = createPublicClient222({ transport: http222(this.rpcUrl) });
|
|
7340
7866
|
const [collectionName, symbol, totalSupply] = await Promise.all([
|
|
7341
7867
|
client.readContract({ address: collection, abi: ERC721_ABI, functionName: "name" }).catch((e) => {
|
|
7342
7868
|
throw DefiError.rpcError(`[${this.protocolName}] name failed: ${e}`);
|
|
@@ -7355,7 +7881,7 @@ var init_dist2 = __esm({
|
|
|
7355
7881
|
}
|
|
7356
7882
|
async getTokenInfo(collection, tokenId) {
|
|
7357
7883
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7358
|
-
const client =
|
|
7884
|
+
const client = createPublicClient222({ transport: http222(this.rpcUrl) });
|
|
7359
7885
|
const [owner, tokenUri] = await Promise.all([
|
|
7360
7886
|
client.readContract({ address: collection, abi: ERC721_ABI, functionName: "ownerOf", args: [tokenId] }).catch((e) => {
|
|
7361
7887
|
throw DefiError.rpcError(`[${this.protocolName}] ownerOf failed: ${e}`);
|
|
@@ -7371,7 +7897,7 @@ var init_dist2 = __esm({
|
|
|
7371
7897
|
}
|
|
7372
7898
|
async getBalance(owner, collection) {
|
|
7373
7899
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7374
|
-
const client =
|
|
7900
|
+
const client = createPublicClient222({ transport: http222(this.rpcUrl) });
|
|
7375
7901
|
return client.readContract({ address: collection, abi: ERC721_ABI, functionName: "balanceOf", args: [owner] }).catch((e) => {
|
|
7376
7902
|
throw DefiError.rpcError(`[${this.protocolName}] balanceOf failed: ${e}`);
|
|
7377
7903
|
});
|
|
@@ -7441,15 +7967,34 @@ var Executor = class _Executor {
|
|
|
7441
7967
|
dryRun;
|
|
7442
7968
|
rpcUrl;
|
|
7443
7969
|
explorerUrl;
|
|
7444
|
-
|
|
7970
|
+
/**
|
|
7971
|
+
* Optional viem Chain object. When set, all wallet/public clients built
|
|
7972
|
+
* inside this executor anchor to the explicit chainId at construction
|
|
7973
|
+
* time, defending against MITM RPCs that lie about eth_chainId and
|
|
7974
|
+
* keeping offline-signing safe under RPC drift (SSOT 7.4).
|
|
7975
|
+
*/
|
|
7976
|
+
chain;
|
|
7977
|
+
constructor(broadcast, rpcUrl, explorerUrl, chain) {
|
|
7445
7978
|
this.dryRun = !broadcast;
|
|
7446
7979
|
this.rpcUrl = rpcUrl;
|
|
7447
7980
|
this.explorerUrl = explorerUrl;
|
|
7981
|
+
this.chain = chain;
|
|
7982
|
+
}
|
|
7983
|
+
/** Returns the optional `{ chain }` spread for viem client constructors. */
|
|
7984
|
+
chainOpt() {
|
|
7985
|
+
return this.chain ? { chain: this.chain } : {};
|
|
7448
7986
|
}
|
|
7449
7987
|
/** Apply 20% buffer to a gas estimate */
|
|
7450
7988
|
static applyGasBuffer(gas) {
|
|
7451
7989
|
return gas * GAS_BUFFER_BPS / 10000n;
|
|
7452
7990
|
}
|
|
7991
|
+
/**
|
|
7992
|
+
* EIP-1559 max-fee formula: `baseFee * 1.25 + priorityFee`.
|
|
7993
|
+
* Extracted as a static so the math is unit-testable without mocking viem.
|
|
7994
|
+
*/
|
|
7995
|
+
static computeMaxFee(baseFeePerGas, priorityFee) {
|
|
7996
|
+
return baseFeePerGas * 125n / 100n + priorityFee;
|
|
7997
|
+
}
|
|
7453
7998
|
/**
|
|
7454
7999
|
* Check allowance for a single token/spender pair and send an approve tx if needed.
|
|
7455
8000
|
* Only called in broadcast mode (not dry-run).
|
|
@@ -7561,7 +8106,7 @@ var Executor = class _Executor {
|
|
|
7561
8106
|
*/
|
|
7562
8107
|
async fetchEip1559Fees(rpcUrl) {
|
|
7563
8108
|
try {
|
|
7564
|
-
const client = createPublicClient23({ transport: http23(rpcUrl) });
|
|
8109
|
+
const client = createPublicClient23({ transport: http23(rpcUrl), ...this.chainOpt() });
|
|
7565
8110
|
let priorityFee = DEFAULT_PRIORITY_FEE_WEI;
|
|
7566
8111
|
try {
|
|
7567
8112
|
priorityFee = await client.estimateMaxPriorityFeePerGas();
|
|
@@ -7570,8 +8115,7 @@ var Executor = class _Executor {
|
|
|
7570
8115
|
try {
|
|
7571
8116
|
const block = await client.getBlock({ blockTag: "latest" });
|
|
7572
8117
|
if (block.baseFeePerGas !== null && block.baseFeePerGas !== void 0) {
|
|
7573
|
-
|
|
7574
|
-
return [maxFee, priorityFee];
|
|
8118
|
+
return [_Executor.computeMaxFee(block.baseFeePerGas, priorityFee), priorityFee];
|
|
7575
8119
|
}
|
|
7576
8120
|
} catch {
|
|
7577
8121
|
}
|
|
@@ -7584,7 +8128,7 @@ var Executor = class _Executor {
|
|
|
7584
8128
|
/** Estimate gas dynamically with buffer, falling back to a hardcoded estimate */
|
|
7585
8129
|
async estimateGasWithBuffer(rpcUrl, tx, from) {
|
|
7586
8130
|
try {
|
|
7587
|
-
const client = createPublicClient23({ transport: http23(rpcUrl) });
|
|
8131
|
+
const client = createPublicClient23({ transport: http23(rpcUrl), ...this.chainOpt() });
|
|
7588
8132
|
const estimated = await client.estimateGas({
|
|
7589
8133
|
to: tx.to,
|
|
7590
8134
|
data: tx.data,
|
|
@@ -7608,7 +8152,7 @@ var Executor = class _Executor {
|
|
|
7608
8152
|
if (!rpcUrl) {
|
|
7609
8153
|
throw DefiError.rpcError("No RPC URL \u2014 cannot simulate. Set HYPEREVM_RPC_URL.");
|
|
7610
8154
|
}
|
|
7611
|
-
const client = createPublicClient23({ transport: http23(rpcUrl) });
|
|
8155
|
+
const client = createPublicClient23({ transport: http23(rpcUrl), ...this.chainOpt() });
|
|
7612
8156
|
const privateKey = process.env["DEFI_PRIVATE_KEY"];
|
|
7613
8157
|
const from = privateKey ? privateKeyToAccount(privateKey).address : "0x0000000000000000000000000000000000000001";
|
|
7614
8158
|
if (tx.approvals && tx.approvals.length > 0) {
|
|
@@ -7722,8 +8266,8 @@ var Executor = class _Executor {
|
|
|
7722
8266
|
if (!rpcUrl) {
|
|
7723
8267
|
throw DefiError.rpcError("No RPC URL configured for broadcasting");
|
|
7724
8268
|
}
|
|
7725
|
-
const publicClient = createPublicClient23({ transport: http23(rpcUrl) });
|
|
7726
|
-
const walletClient = createWalletClient({ account, transport: http23(rpcUrl) });
|
|
8269
|
+
const publicClient = createPublicClient23({ transport: http23(rpcUrl), ...this.chainOpt() });
|
|
8270
|
+
const walletClient = createWalletClient({ account, transport: http23(rpcUrl), ...this.chainOpt() });
|
|
7727
8271
|
if (tx.pre_txs && tx.pre_txs.length > 0) {
|
|
7728
8272
|
for (const preTx of tx.pre_txs) {
|
|
7729
8273
|
process.stderr.write(` Pre-tx: ${preTx.description}...
|
|
@@ -8110,19 +8654,20 @@ server.tool(
|
|
|
8110
8654
|
);
|
|
8111
8655
|
server.tool(
|
|
8112
8656
|
"defi_bridge",
|
|
8113
|
-
"Get a cross-chain bridge quote via LI.FI
|
|
8657
|
+
"Get a cross-chain bridge quote via LI.FI (default) or Relay. Returns estimated output and fees. For deBridge / CCTP quotes plus broadcast use the `defi bridge --provider <name>` CLI directly.",
|
|
8114
8658
|
{
|
|
8115
8659
|
from_chain: z.string().describe("Source chain name (supported source chains: hyperevm, mantle, base, bnb, monad)"),
|
|
8116
|
-
to_chain: z.string().describe("Destination chain name. CCTP V2 destinations include ethereum, arbitrum, optimism, polygon, avalanche; LI.FI/deBridge accept any chain"),
|
|
8117
|
-
token: z.string().optional().describe("Token symbol to bridge (default: USDC). Use
|
|
8660
|
+
to_chain: z.string().describe("Destination chain name. CCTP V2 destinations include ethereum, arbitrum, optimism, polygon, avalanche; LI.FI/Relay/deBridge accept any chain they route to"),
|
|
8661
|
+
token: z.string().optional().describe("Token symbol or address to bridge (default: USDC). Use 0x0\u20260 for native gas token. Relay rejects some ERC20s (e.g. BNB USDC/USDT) with INVALID_INPUT_CURRENCY \u2014 fall back to native or LI.FI"),
|
|
8118
8662
|
amount: z.string().describe("Amount in human-readable units, e.g. '100' for 100 USDC"),
|
|
8119
|
-
recipient: z.string().optional().describe("Recipient address on destination chain")
|
|
8663
|
+
recipient: z.string().optional().describe("Recipient address on destination chain"),
|
|
8664
|
+
provider: z.enum(["lifi", "relay"]).optional().describe("Bridge provider for the quote (default: lifi). For debridge/cctp use the CLI.")
|
|
8120
8665
|
},
|
|
8121
|
-
async ({ from_chain, to_chain, token, amount, recipient }) => {
|
|
8666
|
+
async ({ from_chain, to_chain, token, amount, recipient, provider }) => {
|
|
8122
8667
|
try {
|
|
8123
8668
|
const tokenSymbol = token ?? "USDC";
|
|
8124
8669
|
const recipientAddr = recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
|
|
8125
|
-
const
|
|
8670
|
+
const selectedProvider = provider ?? "lifi";
|
|
8126
8671
|
const registry = getRegistry();
|
|
8127
8672
|
let fromChainId;
|
|
8128
8673
|
let toChainId;
|
|
@@ -8134,14 +8679,81 @@ server.tool(
|
|
|
8134
8679
|
toChainId = registry.getChain(to_chain).chain_id;
|
|
8135
8680
|
} catch {
|
|
8136
8681
|
}
|
|
8682
|
+
const isAddr = tokenSymbol.startsWith("0x");
|
|
8683
|
+
const NATIVE = "0x0000000000000000000000000000000000000000";
|
|
8684
|
+
let srcTokenAddr;
|
|
8685
|
+
let dstTokenAddr;
|
|
8686
|
+
let tokenDecimals = 18;
|
|
8687
|
+
let dstTokenDecimals = 18;
|
|
8688
|
+
if (isAddr) {
|
|
8689
|
+
srcTokenAddr = tokenSymbol;
|
|
8690
|
+
dstTokenAddr = tokenSymbol;
|
|
8691
|
+
} else {
|
|
8692
|
+
try {
|
|
8693
|
+
const srcResolved = registry.resolveToken(from_chain, tokenSymbol);
|
|
8694
|
+
srcTokenAddr = srcResolved.address;
|
|
8695
|
+
tokenDecimals = srcResolved.decimals ?? 18;
|
|
8696
|
+
} catch {
|
|
8697
|
+
srcTokenAddr = NATIVE;
|
|
8698
|
+
tokenDecimals = 18;
|
|
8699
|
+
}
|
|
8700
|
+
try {
|
|
8701
|
+
const dstResolved = registry.resolveToken(to_chain, tokenSymbol);
|
|
8702
|
+
dstTokenAddr = dstResolved.address;
|
|
8703
|
+
dstTokenDecimals = dstResolved.decimals ?? tokenDecimals;
|
|
8704
|
+
} catch {
|
|
8705
|
+
dstTokenAddr = srcTokenAddr;
|
|
8706
|
+
dstTokenDecimals = tokenDecimals;
|
|
8707
|
+
}
|
|
8708
|
+
}
|
|
8709
|
+
if (selectedProvider === "relay") {
|
|
8710
|
+
const RELAY_API = "https://api.relay.link";
|
|
8711
|
+
if (!fromChainId || !toChainId) {
|
|
8712
|
+
throw new Error("Relay requires resolvable numeric chain IDs for both source and destination");
|
|
8713
|
+
}
|
|
8714
|
+
const amountRaw = String(BigInt(Math.round(parseFloat(amount) * Math.pow(10, tokenDecimals))));
|
|
8715
|
+
const res2 = await fetch(`${RELAY_API}/quote`, {
|
|
8716
|
+
method: "POST",
|
|
8717
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
8718
|
+
body: JSON.stringify({
|
|
8719
|
+
user: recipientAddr,
|
|
8720
|
+
recipient: recipientAddr,
|
|
8721
|
+
originChainId: fromChainId,
|
|
8722
|
+
destinationChainId: toChainId,
|
|
8723
|
+
originCurrency: srcTokenAddr,
|
|
8724
|
+
destinationCurrency: dstTokenAddr,
|
|
8725
|
+
tradeType: "EXACT_INPUT",
|
|
8726
|
+
amount: amountRaw
|
|
8727
|
+
})
|
|
8728
|
+
});
|
|
8729
|
+
if (!res2.ok) {
|
|
8730
|
+
const text = await res2.text();
|
|
8731
|
+
throw new Error(`Relay quote failed (${res2.status}): ${text.slice(0, 200)}`);
|
|
8732
|
+
}
|
|
8733
|
+
const data2 = await res2.json();
|
|
8734
|
+
const details = data2.details;
|
|
8735
|
+
const fees = data2.fees;
|
|
8736
|
+
const quote2 = {
|
|
8737
|
+
from_chain,
|
|
8738
|
+
to_chain,
|
|
8739
|
+
token: tokenSymbol,
|
|
8740
|
+
amount_in: amount,
|
|
8741
|
+
amount_out: details?.currencyOut?.amountFormatted ?? "unknown",
|
|
8742
|
+
fees,
|
|
8743
|
+
execution_duration_seconds: details?.timeEstimate ?? "unknown",
|
|
8744
|
+
tool: "relay",
|
|
8745
|
+
raw: data2
|
|
8746
|
+
};
|
|
8747
|
+
return { content: [{ type: "text", text: ok(quote2, { from_chain, to_chain, token: tokenSymbol, provider: "relay" }) }] };
|
|
8748
|
+
}
|
|
8749
|
+
const LIFI_API = "https://li.quest/v1";
|
|
8137
8750
|
const params = new URLSearchParams({
|
|
8138
8751
|
fromChain: fromChainId ? String(fromChainId) : from_chain,
|
|
8139
8752
|
toChain: toChainId ? String(toChainId) : to_chain,
|
|
8140
|
-
fromToken:
|
|
8141
|
-
toToken:
|
|
8142
|
-
fromAmount: String(Math.round(parseFloat(amount) *
|
|
8143
|
-
|
|
8144
|
-
toAddress: recipientAddr
|
|
8753
|
+
fromToken: srcTokenAddr,
|
|
8754
|
+
toToken: dstTokenAddr,
|
|
8755
|
+
fromAmount: String(BigInt(Math.round(parseFloat(amount) * Math.pow(10, tokenDecimals)))),
|
|
8756
|
+
fromAddress: recipientAddr
|
|
8145
8757
|
});
|
|
8146
8758
|
const res = await fetch(`${LIFI_API}/quote?${params}`, {
|
|
8147
8759
|
headers: { Accept: "application/json" }
|
|
@@ -8152,19 +8764,21 @@ server.tool(
|
|
|
8152
8764
|
}
|
|
8153
8765
|
const data = await res.json();
|
|
8154
8766
|
const estimate = data.estimate;
|
|
8767
|
+
const lifiDstDecimals = estimate?.toToken?.decimals;
|
|
8768
|
+
const amountOutDivisor = Math.pow(10, lifiDstDecimals ?? dstTokenDecimals);
|
|
8155
8769
|
const quote = {
|
|
8156
8770
|
from_chain,
|
|
8157
8771
|
to_chain,
|
|
8158
8772
|
token: tokenSymbol,
|
|
8159
8773
|
amount_in: amount,
|
|
8160
|
-
amount_out: estimate?.toAmount ? String(Number(estimate.toAmount) /
|
|
8774
|
+
amount_out: estimate?.toAmount ? String(Number(estimate.toAmount) / amountOutDivisor) : "unknown",
|
|
8161
8775
|
fee_costs: estimate?.feeCosts ?? [],
|
|
8162
8776
|
gas_costs: estimate?.gasCosts ?? [],
|
|
8163
8777
|
execution_duration_seconds: estimate?.executionDuration ?? "unknown",
|
|
8164
8778
|
tool: data.tool ?? "unknown",
|
|
8165
8779
|
raw: data
|
|
8166
8780
|
};
|
|
8167
|
-
return { content: [{ type: "text", text: ok(quote, { from_chain, to_chain, token: tokenSymbol }) }] };
|
|
8781
|
+
return { content: [{ type: "text", text: ok(quote, { from_chain, to_chain, token: tokenSymbol, provider: "lifi" }) }] };
|
|
8168
8782
|
} catch (e) {
|
|
8169
8783
|
return { content: [{ type: "text", text: err(e instanceof Error ? e.message : String(e), { from_chain, to_chain }) }], isError: true };
|
|
8170
8784
|
}
|