@hypurrquant/defi-cli 0.4.1 → 1.0.0
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 +156 -265
- package/config/pools.example.toml +1 -1
- package/dist/index.js +8921 -6091
- package/dist/index.js.map +1 -1
- package/dist/main.js +8965 -6133
- package/dist/main.js.map +1 -1
- package/dist/mcp-server.js +1122 -126
- package/dist/mcp-server.js.map +1 -1
- package/package.json +3 -3
- package/skills/defi-cli/SKILL.md +0 -1
- package/skills/defi-cli/references/protocols.md +0 -1
- package/config/protocols/lending/lendle_mantle.toml +0 -14
package/dist/mcp-server.js
CHANGED
|
@@ -336,6 +336,7 @@ var init_dist = __esm({
|
|
|
336
336
|
native_token;
|
|
337
337
|
wrapped_native;
|
|
338
338
|
multicall3;
|
|
339
|
+
aggregators;
|
|
339
340
|
effectiveRpcUrl() {
|
|
340
341
|
const chainEnv = this.name.toUpperCase().replace(/ /g, "_") + "_RPC_URL";
|
|
341
342
|
return process.env[chainEnv] ?? process.env["HYPEREVM_RPC_URL"] ?? this.rpc_url;
|
|
@@ -436,9 +437,9 @@ var init_dist = __esm({
|
|
|
436
437
|
getProtocolsByCategory(category) {
|
|
437
438
|
return this.protocols.filter((p) => p.category === category);
|
|
438
439
|
}
|
|
439
|
-
getProtocolsForChain(chain) {
|
|
440
|
+
getProtocolsForChain(chain, includeUnverified = false) {
|
|
440
441
|
return this.protocols.filter(
|
|
441
|
-
(p) => p.chain.toLowerCase() === chain.toLowerCase()
|
|
442
|
+
(p) => p.chain.toLowerCase() === chain.toLowerCase() && (includeUnverified || p.verified !== false) && p.is_active !== false
|
|
442
443
|
);
|
|
443
444
|
}
|
|
444
445
|
resolveToken(chain, symbol) {
|
|
@@ -500,6 +501,7 @@ __export(dist_exports2, {
|
|
|
500
501
|
MasterChefAdapter: () => MasterChefAdapter,
|
|
501
502
|
MerchantMoeLBAdapter: () => MerchantMoeLBAdapter,
|
|
502
503
|
MorphoBlueAdapter: () => MorphoBlueAdapter,
|
|
504
|
+
NestOffChainAdapter: () => NestOffChainAdapter,
|
|
503
505
|
PendleAdapter: () => PendleAdapter,
|
|
504
506
|
RyskAdapter: () => RyskAdapter,
|
|
505
507
|
SolidlyAdapter: () => SolidlyAdapter,
|
|
@@ -518,10 +520,12 @@ __export(dist_exports2, {
|
|
|
518
520
|
createLiquidStaking: () => createLiquidStaking,
|
|
519
521
|
createMasterChef: () => createMasterChef,
|
|
520
522
|
createMerchantMoeLB: () => createMerchantMoeLB,
|
|
523
|
+
createNestOffChain: () => createNestOffChain,
|
|
521
524
|
createNft: () => createNft,
|
|
522
525
|
createOptions: () => createOptions,
|
|
523
526
|
createOracleFromCdp: () => createOracleFromCdp,
|
|
524
527
|
createOracleFromLending: () => createOracleFromLending,
|
|
528
|
+
createRewardReader: () => createRewardReader,
|
|
525
529
|
createVault: () => createVault,
|
|
526
530
|
createYieldSource: () => createYieldSource
|
|
527
531
|
});
|
|
@@ -762,8 +766,8 @@ function encodeClaimReward(rewardToken, to) {
|
|
|
762
766
|
return encodeFunctionData13({
|
|
763
767
|
abi: farmingCenterAbi,
|
|
764
768
|
functionName: "claimReward",
|
|
765
|
-
args: [rewardToken, to, 2n **
|
|
766
|
-
// max uint128
|
|
769
|
+
args: [rewardToken, to, 2n ** 256n - 1n]
|
|
770
|
+
// max uint256 (KittenSwap variant uses uint256 amount, not uint128)
|
|
767
771
|
});
|
|
768
772
|
}
|
|
769
773
|
function encodeMulticall(calls) {
|
|
@@ -932,7 +936,7 @@ function createLiquidStaking(entry, rpcUrl) {
|
|
|
932
936
|
return new GenericLstAdapter(entry, rpcUrl);
|
|
933
937
|
}
|
|
934
938
|
}
|
|
935
|
-
function createGauge(entry, rpcUrl) {
|
|
939
|
+
function createGauge(entry, rpcUrl, tokens) {
|
|
936
940
|
if (entry.interface === "hybra" || entry.contracts?.["gauge_manager"]) {
|
|
937
941
|
return new HybraGaugeAdapter(entry, rpcUrl);
|
|
938
942
|
}
|
|
@@ -940,7 +944,11 @@ function createGauge(entry, rpcUrl) {
|
|
|
940
944
|
case "solidly_v2":
|
|
941
945
|
case "solidly_cl":
|
|
942
946
|
case "algebra_v3":
|
|
943
|
-
return new SolidlyGaugeAdapter(entry, rpcUrl);
|
|
947
|
+
return new SolidlyGaugeAdapter(entry, rpcUrl, tokens);
|
|
948
|
+
// uniswap_v3 with voter = ve(3,3) CL (e.g., Aerodrome Slipstream, Ramses CL)
|
|
949
|
+
case "uniswap_v3":
|
|
950
|
+
if (entry.contracts?.["voter"]) return new SolidlyGaugeAdapter(entry, rpcUrl, tokens);
|
|
951
|
+
throw DefiError.unsupported(`Gauge interface '${entry.interface}' not supported (no voter contract)`);
|
|
944
952
|
default:
|
|
945
953
|
throw DefiError.unsupported(`Gauge interface '${entry.interface}' not supported`);
|
|
946
954
|
}
|
|
@@ -1002,6 +1010,46 @@ function createOracleFromCdp(entry, _asset, rpcUrl) {
|
|
|
1002
1010
|
function createMerchantMoeLB(entry, rpcUrl) {
|
|
1003
1011
|
return new MerchantMoeLBAdapter(entry, rpcUrl);
|
|
1004
1012
|
}
|
|
1013
|
+
function createNestOffChain(entry) {
|
|
1014
|
+
return new NestOffChainAdapter(entry);
|
|
1015
|
+
}
|
|
1016
|
+
function createRewardReader(entry, rpcUrl, tokens) {
|
|
1017
|
+
const strategy = entry.reward_strategy ?? inferRewardStrategy(entry);
|
|
1018
|
+
switch (strategy) {
|
|
1019
|
+
case "off_chain_api":
|
|
1020
|
+
return { kind: "off_chain_api", adapter: new NestOffChainAdapter(entry) };
|
|
1021
|
+
case "on_chain_farming_center":
|
|
1022
|
+
if (!rpcUrl) throw DefiError.invalidParam("createRewardReader: rpcUrl required for on_chain_farming_center");
|
|
1023
|
+
return { kind: "on_chain_farming_center", adapter: createKittenSwapFarming(entry, rpcUrl) };
|
|
1024
|
+
case "on_chain_gauge_tokenid":
|
|
1025
|
+
return { kind: "on_chain_gauge_tokenid", adapter: new HybraGaugeAdapter(entry, rpcUrl) };
|
|
1026
|
+
case "on_chain_gauge":
|
|
1027
|
+
return { kind: "on_chain_gauge", adapter: new SolidlyGaugeAdapter(entry, rpcUrl, tokens) };
|
|
1028
|
+
case "auto_stake":
|
|
1029
|
+
return { kind: "auto_stake", adapter: new SolidlyGaugeAdapter(entry, rpcUrl, tokens) };
|
|
1030
|
+
case "on_chain_masterchef":
|
|
1031
|
+
return { kind: "on_chain_masterchef", adapter: new MasterChefAdapter(entry, rpcUrl) };
|
|
1032
|
+
case "none":
|
|
1033
|
+
return { kind: "none" };
|
|
1034
|
+
default:
|
|
1035
|
+
throw DefiError.unsupported(`Unknown reward_strategy '${strategy}' on '${entry.slug}'`);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function inferRewardStrategy(entry) {
|
|
1039
|
+
if (entry.interface === "hybra" || entry.contracts?.["gauge_manager"]) {
|
|
1040
|
+
return "on_chain_gauge_tokenid";
|
|
1041
|
+
}
|
|
1042
|
+
if (entry.contracts?.["farming_center"] && entry.contracts?.["eternal_farming"]) {
|
|
1043
|
+
return "on_chain_farming_center";
|
|
1044
|
+
}
|
|
1045
|
+
if (entry.contracts?.["voter"]) {
|
|
1046
|
+
return "on_chain_gauge";
|
|
1047
|
+
}
|
|
1048
|
+
if (entry.contracts?.["master_chef"] || entry.contracts?.["masterChef"]) {
|
|
1049
|
+
return "on_chain_masterchef";
|
|
1050
|
+
}
|
|
1051
|
+
return "none";
|
|
1052
|
+
}
|
|
1005
1053
|
function createKittenSwapFarming(entry, rpcUrl) {
|
|
1006
1054
|
const farmingCenter = entry.contracts?.["farming_center"];
|
|
1007
1055
|
if (!farmingCenter) {
|
|
@@ -1016,9 +1064,11 @@ function createKittenSwapFarming(entry, rpcUrl) {
|
|
|
1016
1064
|
throw new DefiError("CONTRACT_ERROR", `[${entry.name}] Missing 'position_manager' contract address`);
|
|
1017
1065
|
}
|
|
1018
1066
|
const factory = entry.contracts?.["factory"];
|
|
1019
|
-
|
|
1067
|
+
const rewardToken = entry.contracts?.["reward_token"];
|
|
1068
|
+
const bonusRewardToken = entry.contracts?.["bonus_reward_token"];
|
|
1069
|
+
return new KittenSwapFarmingAdapter(entry.name, farmingCenter, eternalFarming, positionManager, rpcUrl, factory, rewardToken, bonusRewardToken);
|
|
1020
1070
|
}
|
|
1021
|
-
var DEFAULT_FEE, swapRouterAbi, quoterAbi, ramsesQuoterAbi, positionManagerAbi, UniswapV3Adapter, abi, lbQuoterAbi, UniswapV2Adapter, abi2, algebraQuoterAbi, algebraSingleQuoterAbi, algebraIntegralPmAbi, algebraV2PmAbi, 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, 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, IRM_ABI, SECONDS_PER_YEAR3, MorphoBlueAdapter, BORROWER_OPS_ABI, TROVE_MANAGER_ABI, HINT_HELPERS_ABI, SORTED_TROVES_ABI, FelixCdpAdapter, PRICE_FEED_ABI, FelixOracleAdapter,
|
|
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;
|
|
1022
1072
|
var init_dist2 = __esm({
|
|
1023
1073
|
"../defi-protocols/dist/index.js"() {
|
|
1024
1074
|
"use strict";
|
|
@@ -1056,6 +1106,7 @@ var init_dist2 = __esm({
|
|
|
1056
1106
|
init_dist();
|
|
1057
1107
|
init_dist();
|
|
1058
1108
|
init_dist();
|
|
1109
|
+
init_dist();
|
|
1059
1110
|
DEFAULT_FEE = 3e3;
|
|
1060
1111
|
swapRouterAbi = parseAbi3([
|
|
1061
1112
|
"struct ExactInputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; }",
|
|
@@ -1071,7 +1122,19 @@ var init_dist2 = __esm({
|
|
|
1071
1122
|
]);
|
|
1072
1123
|
positionManagerAbi = parseAbi3([
|
|
1073
1124
|
"struct MintParams { address token0; address token1; uint24 fee; int24 tickLower; int24 tickUpper; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; uint256 deadline; }",
|
|
1074
|
-
"function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
|
|
1125
|
+
"function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)",
|
|
1126
|
+
"struct CollectParams { uint256 tokenId; address recipient; uint128 amount0Max; uint128 amount1Max; }",
|
|
1127
|
+
"function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1)",
|
|
1128
|
+
"struct DecreaseLiquidityParams { uint256 tokenId; uint128 liquidity; uint256 amount0Min; uint256 amount1Min; uint256 deadline; }",
|
|
1129
|
+
"function decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable returns (uint256 amount0, uint256 amount1)",
|
|
1130
|
+
"struct IncreaseLiquidityParams { uint256 tokenId; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; uint256 deadline; }",
|
|
1131
|
+
"function increaseLiquidity(IncreaseLiquidityParams calldata params) external payable returns (uint128 liquidity, uint256 amount0, uint256 amount1)",
|
|
1132
|
+
"function positions(uint256 tokenId) external view returns (uint96 nonce, address operator, address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, uint128 tokensOwed0, uint128 tokensOwed1)",
|
|
1133
|
+
"function multicall(bytes[] data) external payable returns (bytes[] memory results)"
|
|
1134
|
+
]);
|
|
1135
|
+
slipstreamMintAbi = parseAbi3([
|
|
1136
|
+
"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
|
+
"function mint(SlipstreamMintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
|
|
1075
1138
|
]);
|
|
1076
1139
|
UniswapV3Adapter = class {
|
|
1077
1140
|
protocolName;
|
|
@@ -1094,7 +1157,7 @@ var init_dist2 = __esm({
|
|
|
1094
1157
|
this.factory = entry.contracts?.["factory"];
|
|
1095
1158
|
this.fee = DEFAULT_FEE;
|
|
1096
1159
|
this.rpcUrl = rpcUrl;
|
|
1097
|
-
this.useTickSpacingQuoter = entry.contracts?.["pool_deployer"] !== void 0 || entry.contracts?.["gauge_factory"] !== void 0;
|
|
1160
|
+
this.useTickSpacingQuoter = entry.cl_style === "slipstream" || entry.cl_style === "ramses" || entry.contracts?.["pool_deployer"] !== void 0 || entry.contracts?.["gauge_factory"] !== void 0;
|
|
1098
1161
|
}
|
|
1099
1162
|
name() {
|
|
1100
1163
|
return this.protocolName;
|
|
@@ -1270,16 +1333,64 @@ var init_dist2 = __esm({
|
|
|
1270
1333
|
const [token0, token1, rawAmount0, rawAmount1] = params.token_a.toLowerCase() < params.token_b.toLowerCase() ? [params.token_a, params.token_b, params.amount_a, params.amount_b] : [params.token_b, params.token_a, params.amount_b, params.amount_a];
|
|
1271
1334
|
const amount0 = rawAmount0 === 0n && rawAmount1 > 0n ? 1n : rawAmount0;
|
|
1272
1335
|
const amount1 = rawAmount1 === 0n && rawAmount0 > 0n ? 1n : rawAmount1;
|
|
1273
|
-
|
|
1336
|
+
let thirdField = this.fee;
|
|
1337
|
+
let tickLower = -887220;
|
|
1338
|
+
let tickUpper = 887220;
|
|
1339
|
+
if (params.pool && this.rpcUrl) {
|
|
1340
|
+
const poolAbi2 = parseAbi3([
|
|
1341
|
+
"function fee() view returns (uint24)",
|
|
1342
|
+
"function tickSpacing() view returns (int24)",
|
|
1343
|
+
"function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16, uint16, uint16, bool)"
|
|
1344
|
+
]);
|
|
1345
|
+
const client = createPublicClient2({ transport: http2(this.rpcUrl) });
|
|
1346
|
+
const [poolFee, poolTs, slot0] = await Promise.all([
|
|
1347
|
+
client.readContract({ address: params.pool, abi: poolAbi2, functionName: "fee" }).catch(() => null),
|
|
1348
|
+
client.readContract({ address: params.pool, abi: poolAbi2, functionName: "tickSpacing" }).catch(() => null),
|
|
1349
|
+
client.readContract({ address: params.pool, abi: poolAbi2, functionName: "slot0" }).catch(() => null)
|
|
1350
|
+
]);
|
|
1351
|
+
if (this.useTickSpacingQuoter && poolTs !== null) {
|
|
1352
|
+
thirdField = poolTs;
|
|
1353
|
+
} else if (poolFee !== null) {
|
|
1354
|
+
thirdField = poolFee;
|
|
1355
|
+
}
|
|
1356
|
+
if (params.range_pct !== void 0 && slot0 && poolTs !== null) {
|
|
1357
|
+
const currentTick = slot0[1];
|
|
1358
|
+
const rangeTicks = Math.floor(params.range_pct * 100);
|
|
1359
|
+
tickLower = Math.floor((currentTick - rangeTicks) / poolTs) * poolTs;
|
|
1360
|
+
tickUpper = Math.ceil((currentTick + rangeTicks) / poolTs) * poolTs;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (params.tick_lower !== void 0) tickLower = params.tick_lower;
|
|
1364
|
+
if (params.tick_upper !== void 0) tickUpper = params.tick_upper;
|
|
1365
|
+
const data = this.useTickSpacingQuoter ? encodeFunctionData3({
|
|
1366
|
+
abi: slipstreamMintAbi,
|
|
1367
|
+
functionName: "mint",
|
|
1368
|
+
args: [
|
|
1369
|
+
{
|
|
1370
|
+
token0,
|
|
1371
|
+
token1,
|
|
1372
|
+
tickSpacing: thirdField,
|
|
1373
|
+
tickLower,
|
|
1374
|
+
tickUpper,
|
|
1375
|
+
amount0Desired: amount0,
|
|
1376
|
+
amount1Desired: amount1,
|
|
1377
|
+
amount0Min: 0n,
|
|
1378
|
+
amount1Min: 0n,
|
|
1379
|
+
recipient: params.recipient,
|
|
1380
|
+
deadline: BigInt("18446744073709551615"),
|
|
1381
|
+
sqrtPriceX96: 0n
|
|
1382
|
+
}
|
|
1383
|
+
]
|
|
1384
|
+
}) : encodeFunctionData3({
|
|
1274
1385
|
abi: positionManagerAbi,
|
|
1275
1386
|
functionName: "mint",
|
|
1276
1387
|
args: [
|
|
1277
1388
|
{
|
|
1278
1389
|
token0,
|
|
1279
1390
|
token1,
|
|
1280
|
-
fee:
|
|
1281
|
-
tickLower
|
|
1282
|
-
tickUpper
|
|
1391
|
+
fee: thirdField,
|
|
1392
|
+
tickLower,
|
|
1393
|
+
tickUpper,
|
|
1283
1394
|
amount0Desired: amount0,
|
|
1284
1395
|
amount1Desired: amount1,
|
|
1285
1396
|
amount0Min: 0n,
|
|
@@ -1301,10 +1412,125 @@ var init_dist2 = __esm({
|
|
|
1301
1412
|
]
|
|
1302
1413
|
};
|
|
1303
1414
|
}
|
|
1304
|
-
async buildRemoveLiquidity(
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1415
|
+
async buildRemoveLiquidity(params) {
|
|
1416
|
+
const pm = this.positionManager;
|
|
1417
|
+
if (!pm) {
|
|
1418
|
+
throw DefiError.contractError(
|
|
1419
|
+
`[${this.protocolName}] Missing 'position_manager' for liquidity removal`
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
if (!params.token_id) {
|
|
1423
|
+
throw DefiError.invalidParam(
|
|
1424
|
+
`[${this.protocolName}] V3 remove_liquidity requires --token-id (NFT positionId)`
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
const tokenId = params.token_id;
|
|
1428
|
+
const liquidity = params.liquidity;
|
|
1429
|
+
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
1430
|
+
const deadline = BigInt("18446744073709551615");
|
|
1431
|
+
const decreaseData = encodeFunctionData3({
|
|
1432
|
+
abi: positionManagerAbi,
|
|
1433
|
+
functionName: "decreaseLiquidity",
|
|
1434
|
+
args: [{ tokenId, liquidity, amount0Min: 0n, amount1Min: 0n, deadline }]
|
|
1435
|
+
});
|
|
1436
|
+
const collectData = encodeFunctionData3({
|
|
1437
|
+
abi: positionManagerAbi,
|
|
1438
|
+
functionName: "collect",
|
|
1439
|
+
args: [{ tokenId, recipient: params.recipient, amount0Max: MAX_UINT128, amount1Max: MAX_UINT128 }]
|
|
1440
|
+
});
|
|
1441
|
+
const data = encodeFunctionData3({
|
|
1442
|
+
abi: positionManagerAbi,
|
|
1443
|
+
functionName: "multicall",
|
|
1444
|
+
args: [[decreaseData, collectData]]
|
|
1445
|
+
});
|
|
1446
|
+
return {
|
|
1447
|
+
description: `[${this.protocolName}] Remove ${liquidity} liquidity from tokenId ${tokenId}`,
|
|
1448
|
+
to: pm,
|
|
1449
|
+
data,
|
|
1450
|
+
value: 0n,
|
|
1451
|
+
gas_estimate: 4e5
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Collect accrued LP trading fees for a CL position via NPM.collect().
|
|
1456
|
+
* Used as the reward path for V3 forks with reward_strategy = "lp_fee_only"
|
|
1457
|
+
* (e.g., HyperSwap V3, Project X — no gauge/emissions, fees are the only reward).
|
|
1458
|
+
*/
|
|
1459
|
+
async buildCollectFees(tokenId, recipient) {
|
|
1460
|
+
const pm = this.positionManager;
|
|
1461
|
+
if (!pm) {
|
|
1462
|
+
throw DefiError.contractError(
|
|
1463
|
+
`[${this.protocolName}] Missing 'position_manager' for fee collection`
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
1467
|
+
const data = encodeFunctionData3({
|
|
1468
|
+
abi: positionManagerAbi,
|
|
1469
|
+
functionName: "collect",
|
|
1470
|
+
args: [{ tokenId, recipient, amount0Max: MAX_UINT128, amount1Max: MAX_UINT128 }]
|
|
1471
|
+
});
|
|
1472
|
+
return {
|
|
1473
|
+
description: `[${this.protocolName}] Collect LP fees for tokenId ${tokenId}`,
|
|
1474
|
+
to: pm,
|
|
1475
|
+
data,
|
|
1476
|
+
value: 0n,
|
|
1477
|
+
gas_estimate: 2e5
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Compound: collect accrued fees and immediately re-add them as liquidity to the same position.
|
|
1482
|
+
* Flow: static-call collect to learn fee amounts → multicall([collect, increaseLiquidity]) on NPM.
|
|
1483
|
+
* Requires existing token approvals on the NPM (set during initial mint).
|
|
1484
|
+
* v1: V3 fee-only protocols (Project X, HyperSwap V3). Gauge protocols need swap routing first.
|
|
1485
|
+
*/
|
|
1486
|
+
async buildCompound(tokenId, recipient, opts) {
|
|
1487
|
+
const pm = this.positionManager;
|
|
1488
|
+
if (!pm) {
|
|
1489
|
+
throw DefiError.contractError(`[${this.protocolName}] Missing 'position_manager' for compound`);
|
|
1490
|
+
}
|
|
1491
|
+
if (!this.rpcUrl) throw DefiError.rpcError("RPC required to preview fees");
|
|
1492
|
+
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
1493
|
+
const deadline = BigInt("18446744073709551615");
|
|
1494
|
+
const slippageBps = BigInt(opts?.slippageBps ?? 50);
|
|
1495
|
+
if (slippageBps > 10000n) {
|
|
1496
|
+
throw DefiError.invalidParam(`[${this.protocolName}] slippageBps must be <= 10000 (got ${slippageBps})`);
|
|
1497
|
+
}
|
|
1498
|
+
const client = createPublicClient2({ transport: http2(this.rpcUrl) });
|
|
1499
|
+
const sim = await client.simulateContract({
|
|
1500
|
+
address: pm,
|
|
1501
|
+
abi: positionManagerAbi,
|
|
1502
|
+
functionName: "collect",
|
|
1503
|
+
args: [{ tokenId, recipient, amount0Max: MAX_UINT128, amount1Max: MAX_UINT128 }],
|
|
1504
|
+
account: recipient
|
|
1505
|
+
});
|
|
1506
|
+
const [amount0, amount1] = sim.result;
|
|
1507
|
+
if (amount0 === 0n && amount1 === 0n) {
|
|
1508
|
+
throw DefiError.invalidParam(`[${this.protocolName}] No fees to compound for tokenId ${tokenId}`);
|
|
1509
|
+
}
|
|
1510
|
+
const amount0Min = amount0 * (10000n - slippageBps) / 10000n;
|
|
1511
|
+
const amount1Min = amount1 * (10000n - slippageBps) / 10000n;
|
|
1512
|
+
const collectData = encodeFunctionData3({
|
|
1513
|
+
abi: positionManagerAbi,
|
|
1514
|
+
functionName: "collect",
|
|
1515
|
+
args: [{ tokenId, recipient, amount0Max: MAX_UINT128, amount1Max: MAX_UINT128 }]
|
|
1516
|
+
});
|
|
1517
|
+
const increaseData = encodeFunctionData3({
|
|
1518
|
+
abi: positionManagerAbi,
|
|
1519
|
+
functionName: "increaseLiquidity",
|
|
1520
|
+
args: [{ tokenId, amount0Desired: amount0, amount1Desired: amount1, amount0Min, amount1Min, deadline }]
|
|
1521
|
+
});
|
|
1522
|
+
const data = encodeFunctionData3({
|
|
1523
|
+
abi: positionManagerAbi,
|
|
1524
|
+
functionName: "multicall",
|
|
1525
|
+
args: [[collectData, increaseData]]
|
|
1526
|
+
});
|
|
1527
|
+
return {
|
|
1528
|
+
description: `[${this.protocolName}] Compound tokenId ${tokenId}: collect ${amount0}/${amount1} \u2192 increaseLiquidity (slippage ${slippageBps}bps)`,
|
|
1529
|
+
to: pm,
|
|
1530
|
+
data,
|
|
1531
|
+
value: 0n,
|
|
1532
|
+
gas_estimate: 5e5
|
|
1533
|
+
};
|
|
1308
1534
|
}
|
|
1309
1535
|
};
|
|
1310
1536
|
abi = parseAbi22([
|
|
@@ -1525,6 +1751,13 @@ var init_dist2 = __esm({
|
|
|
1525
1751
|
"struct MintParams { address token0; address token1; int24 tickLower; int24 tickUpper; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; uint256 deadline; }",
|
|
1526
1752
|
"function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
|
|
1527
1753
|
]);
|
|
1754
|
+
algebraSharedPmAbi = parseAbi32([
|
|
1755
|
+
"struct DecreaseLiquidityParams { uint256 tokenId; uint128 liquidity; uint256 amount0Min; uint256 amount1Min; uint256 deadline; }",
|
|
1756
|
+
"function decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable returns (uint256 amount0, uint256 amount1)",
|
|
1757
|
+
"struct CollectParams { uint256 tokenId; address recipient; uint128 amount0Max; uint128 amount1Max; }",
|
|
1758
|
+
"function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1)",
|
|
1759
|
+
"function multicall(bytes[] data) external payable returns (bytes[] memory results)"
|
|
1760
|
+
]);
|
|
1528
1761
|
AlgebraV3Adapter = class {
|
|
1529
1762
|
protocolName;
|
|
1530
1763
|
router;
|
|
@@ -1720,10 +1953,34 @@ var init_dist2 = __esm({
|
|
|
1720
1953
|
approvals
|
|
1721
1954
|
};
|
|
1722
1955
|
}
|
|
1723
|
-
async buildRemoveLiquidity(
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
);
|
|
1956
|
+
async buildRemoveLiquidity(params) {
|
|
1957
|
+
const pm = this.positionManager;
|
|
1958
|
+
if (!pm) throw DefiError.contractError(`[${this.protocolName}] Missing 'position_manager'`);
|
|
1959
|
+
if (!params.token_id) throw DefiError.invalidParam(`[${this.protocolName}] V3 remove_liquidity requires --token-id`);
|
|
1960
|
+
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
1961
|
+
const deadline = BigInt("18446744073709551615");
|
|
1962
|
+
const decreaseData = encodeFunctionData32({
|
|
1963
|
+
abi: algebraSharedPmAbi,
|
|
1964
|
+
functionName: "decreaseLiquidity",
|
|
1965
|
+
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min: 0n, amount1Min: 0n, deadline }]
|
|
1966
|
+
});
|
|
1967
|
+
const collectData = encodeFunctionData32({
|
|
1968
|
+
abi: algebraSharedPmAbi,
|
|
1969
|
+
functionName: "collect",
|
|
1970
|
+
args: [{ tokenId: params.token_id, recipient: params.recipient, amount0Max: MAX_UINT128, amount1Max: MAX_UINT128 }]
|
|
1971
|
+
});
|
|
1972
|
+
const data = encodeFunctionData32({
|
|
1973
|
+
abi: algebraSharedPmAbi,
|
|
1974
|
+
functionName: "multicall",
|
|
1975
|
+
args: [[decreaseData, collectData]]
|
|
1976
|
+
});
|
|
1977
|
+
return {
|
|
1978
|
+
description: `[${this.protocolName}] Remove ${params.liquidity} liquidity from tokenId ${params.token_id}`,
|
|
1979
|
+
to: pm,
|
|
1980
|
+
data,
|
|
1981
|
+
value: 0n,
|
|
1982
|
+
gas_estimate: 4e5
|
|
1983
|
+
};
|
|
1727
1984
|
}
|
|
1728
1985
|
};
|
|
1729
1986
|
abi3 = parseAbi4([
|
|
@@ -2011,7 +2268,12 @@ var init_dist2 = __esm({
|
|
|
2011
2268
|
};
|
|
2012
2269
|
thenaPmAbi = parseAbi7([
|
|
2013
2270
|
"struct MintParams { address token0; address token1; int24 tickSpacing; int24 tickLower; int24 tickUpper; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; uint256 deadline; uint160 sqrtPriceX96; }",
|
|
2014
|
-
"function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
|
|
2271
|
+
"function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)",
|
|
2272
|
+
"struct DecreaseLiquidityParams { uint256 tokenId; uint128 liquidity; uint256 amount0Min; uint256 amount1Min; uint256 deadline; }",
|
|
2273
|
+
"function decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable returns (uint256 amount0, uint256 amount1)",
|
|
2274
|
+
"struct CollectParams { uint256 tokenId; address recipient; uint128 amount0Max; uint128 amount1Max; }",
|
|
2275
|
+
"function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1)",
|
|
2276
|
+
"function multicall(bytes[] data) external payable returns (bytes[] memory results)"
|
|
2015
2277
|
]);
|
|
2016
2278
|
thenaRouterAbi = parseAbi7([
|
|
2017
2279
|
"struct ExactInputSingleParams { address tokenIn; address tokenOut; int24 tickSpacing; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; }",
|
|
@@ -2154,8 +2416,34 @@ var init_dist2 = __esm({
|
|
|
2154
2416
|
approvals
|
|
2155
2417
|
};
|
|
2156
2418
|
}
|
|
2157
|
-
async buildRemoveLiquidity(
|
|
2158
|
-
|
|
2419
|
+
async buildRemoveLiquidity(params) {
|
|
2420
|
+
const pm = this.positionManager;
|
|
2421
|
+
if (!pm) throw DefiError.contractError(`[${this.protocolName}] Missing 'position_manager'`);
|
|
2422
|
+
if (!params.token_id) throw DefiError.invalidParam(`[${this.protocolName}] V3 remove_liquidity requires --token-id`);
|
|
2423
|
+
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
2424
|
+
const deadline = BigInt("18446744073709551615");
|
|
2425
|
+
const decreaseData = encodeFunctionData7({
|
|
2426
|
+
abi: thenaPmAbi,
|
|
2427
|
+
functionName: "decreaseLiquidity",
|
|
2428
|
+
args: [{ tokenId: params.token_id, liquidity: params.liquidity, amount0Min: 0n, amount1Min: 0n, deadline }]
|
|
2429
|
+
});
|
|
2430
|
+
const collectData = encodeFunctionData7({
|
|
2431
|
+
abi: thenaPmAbi,
|
|
2432
|
+
functionName: "collect",
|
|
2433
|
+
args: [{ tokenId: params.token_id, recipient: params.recipient, amount0Max: MAX_UINT128, amount1Max: MAX_UINT128 }]
|
|
2434
|
+
});
|
|
2435
|
+
const data = encodeFunctionData7({
|
|
2436
|
+
abi: thenaPmAbi,
|
|
2437
|
+
functionName: "multicall",
|
|
2438
|
+
args: [[decreaseData, collectData]]
|
|
2439
|
+
});
|
|
2440
|
+
return {
|
|
2441
|
+
description: `[${this.protocolName}] Remove ${params.liquidity} liquidity from tokenId ${params.token_id}`,
|
|
2442
|
+
to: pm,
|
|
2443
|
+
data,
|
|
2444
|
+
value: 0n,
|
|
2445
|
+
gas_estimate: 4e5
|
|
2446
|
+
};
|
|
2159
2447
|
}
|
|
2160
2448
|
};
|
|
2161
2449
|
_addressDecodeAbi = parseAbi8(["function f() external view returns (address)"]);
|
|
@@ -2295,6 +2583,8 @@ var init_dist2 = __esm({
|
|
|
2295
2583
|
gauge,
|
|
2296
2584
|
token0: t0 ? symbolMap.get(t0) ?? t0.slice(0, 10) : "?",
|
|
2297
2585
|
token1: t1 ? symbolMap.get(t1) ?? t1.slice(0, 10) : "?",
|
|
2586
|
+
token0Addr: t0 ?? void 0,
|
|
2587
|
+
token1Addr: t1 ?? void 0,
|
|
2298
2588
|
type: "CL"
|
|
2299
2589
|
});
|
|
2300
2590
|
}
|
|
@@ -2332,12 +2622,14 @@ var init_dist2 = __esm({
|
|
|
2332
2622
|
pre_txs: [approveTx]
|
|
2333
2623
|
};
|
|
2334
2624
|
}
|
|
2335
|
-
async buildWithdraw(gauge, _amount, tokenId) {
|
|
2625
|
+
async buildWithdraw(gauge, _amount, tokenId, opts) {
|
|
2336
2626
|
if (tokenId === void 0) throw new DefiError("CONTRACT_ERROR", "tokenId required for CL gauge withdraw");
|
|
2627
|
+
const redeemType = opts?.redeemType ?? 1;
|
|
2628
|
+
const warning = redeemType === 1 ? " \u2014 WARNING: redeemType=1 locks accumulated rewards into 2-year veHYBR NFT. Use --redeem-type 0 for instant exit (with penalty)." : "";
|
|
2337
2629
|
return {
|
|
2338
|
-
description: `[${this.protocolName}] Withdraw NFT #${tokenId} from gauge`,
|
|
2630
|
+
description: `[${this.protocolName}] Withdraw NFT #${tokenId} from gauge (redeemType=${redeemType})${warning}`,
|
|
2339
2631
|
to: gauge,
|
|
2340
|
-
data: encodeFunctionData8({ abi: gaugeCLAbi, functionName: "withdraw", args: [tokenId,
|
|
2632
|
+
data: encodeFunctionData8({ abi: gaugeCLAbi, functionName: "withdraw", args: [tokenId, redeemType] }),
|
|
2341
2633
|
value: 0n,
|
|
2342
2634
|
gas_estimate: 1e6
|
|
2343
2635
|
};
|
|
@@ -2346,15 +2638,15 @@ var init_dist2 = __esm({
|
|
|
2346
2638
|
async buildClaimRewards(gauge, _account) {
|
|
2347
2639
|
throw DefiError.unsupported(`[${this.protocolName}] Use buildClaimRewardsByTokenId for CL gauges`);
|
|
2348
2640
|
}
|
|
2349
|
-
async buildClaimRewardsByTokenId(gauge, tokenId) {
|
|
2641
|
+
async buildClaimRewardsByTokenId(gauge, tokenId, opts) {
|
|
2642
|
+
const redeemType = opts?.redeemType ?? 1;
|
|
2350
2643
|
return {
|
|
2351
|
-
description: `[${this.protocolName}] Claim rewards for NFT #${tokenId}`,
|
|
2644
|
+
description: `[${this.protocolName}] Claim rewards for NFT #${tokenId} (redeemType=${redeemType})`,
|
|
2352
2645
|
to: this.gaugeManager,
|
|
2353
2646
|
data: encodeFunctionData8({
|
|
2354
2647
|
abi: gaugeManagerAbi,
|
|
2355
2648
|
functionName: "claimRewards",
|
|
2356
|
-
args: [gauge, [tokenId],
|
|
2357
|
-
// redeemType=1
|
|
2649
|
+
args: [gauge, [tokenId], redeemType]
|
|
2358
2650
|
}),
|
|
2359
2651
|
value: 0n,
|
|
2360
2652
|
gas_estimate: 1e6
|
|
@@ -2499,14 +2791,21 @@ var init_dist2 = __esm({
|
|
|
2499
2791
|
"function getReward(address account) external",
|
|
2500
2792
|
"function getReward(address account, address[] tokens) external",
|
|
2501
2793
|
"function getReward(uint256 tokenId) external",
|
|
2794
|
+
// Ramses CL gauge factory (delegate target): tokenId-keyed multi-token claim
|
|
2795
|
+
"function getReward(uint256 tokenId, address[] tokens) external",
|
|
2502
2796
|
"function earned(address account) external view returns (uint256)",
|
|
2797
|
+
"function earned(address account, uint256 tokenId) external view returns (uint256)",
|
|
2503
2798
|
"function earned(address token, address account) external view returns (uint256)",
|
|
2504
2799
|
"function earned(uint256 tokenId) external view returns (uint256)",
|
|
2800
|
+
// Ramses CL: token-first + tokenId (no account; account is ownerOf(tokenId) inferred by gauge)
|
|
2801
|
+
"function earned(address token, uint256 tokenId) external view returns (uint256)",
|
|
2505
2802
|
"function rewardRate() external view returns (uint256)",
|
|
2506
2803
|
"function rewardToken() external view returns (address)",
|
|
2507
2804
|
"function totalSupply() external view returns (uint256)",
|
|
2508
2805
|
"function rewardsListLength() external view returns (uint256)",
|
|
2509
2806
|
"function rewardData(address token) external view returns (uint256 periodFinish, uint256 rewardRate, uint256 lastUpdateTime, uint256 rewardPerTokenStored)",
|
|
2807
|
+
// Ramses CL gauge factory exposes the canonical reward-token list
|
|
2808
|
+
"function getRewardTokens() external view returns (address[])",
|
|
2510
2809
|
"function nonfungiblePositionManager() external view returns (address)"
|
|
2511
2810
|
]);
|
|
2512
2811
|
veAbi2 = parseAbi10([
|
|
@@ -2550,7 +2849,12 @@ var init_dist2 = __esm({
|
|
|
2550
2849
|
rpcUrl;
|
|
2551
2850
|
clFactory;
|
|
2552
2851
|
v2Factory;
|
|
2553
|
-
|
|
2852
|
+
tokens;
|
|
2853
|
+
// CL gauges (Aerodrome Slipstream / Velodrome CL) take an LP NFT via deposit(tokenId);
|
|
2854
|
+
// V2 gauges take an LP token amount via deposit(amount). Detect via uniswap_v3 + position_manager.
|
|
2855
|
+
clNftMode;
|
|
2856
|
+
positionManager;
|
|
2857
|
+
constructor(entry, rpcUrl, tokens) {
|
|
2554
2858
|
this.protocolName = entry.name;
|
|
2555
2859
|
const voter = entry.contracts?.["voter"];
|
|
2556
2860
|
if (!voter) {
|
|
@@ -2563,8 +2867,11 @@ var init_dist2 = __esm({
|
|
|
2563
2867
|
this.voter = voter;
|
|
2564
2868
|
this.veToken = veToken;
|
|
2565
2869
|
this.rpcUrl = rpcUrl;
|
|
2870
|
+
this.tokens = tokens;
|
|
2566
2871
|
this.clFactory = entry.contracts?.["cl_factory"] ?? entry.contracts?.["factory"];
|
|
2567
2872
|
this.v2Factory = entry.contracts?.["pair_factory"] ?? entry.contracts?.["factory"];
|
|
2873
|
+
this.positionManager = entry.contracts?.["position_manager"];
|
|
2874
|
+
this.clNftMode = entry.interface === "uniswap_v3" && this.positionManager !== void 0;
|
|
2568
2875
|
}
|
|
2569
2876
|
name() {
|
|
2570
2877
|
return this.protocolName;
|
|
@@ -2577,57 +2884,148 @@ var init_dist2 = __esm({
|
|
|
2577
2884
|
this._discoverV2GaugedPools(results),
|
|
2578
2885
|
this._discoverCLGaugedPools(results)
|
|
2579
2886
|
]);
|
|
2887
|
+
await this._enrichGaugeMetrics(results);
|
|
2580
2888
|
return results;
|
|
2581
2889
|
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Batch query rewardRate, totalSupply, rewardToken for all discovered gauges.
|
|
2892
|
+
* Handles both single-token (rewardRate) and multi-token (rewardData) gauges.
|
|
2893
|
+
*/
|
|
2894
|
+
async _enrichGaugeMetrics(pools) {
|
|
2895
|
+
if (!this.rpcUrl || pools.length === 0) return;
|
|
2896
|
+
const _u256Abi = parseAbi10(["function f() view returns (uint256)"]);
|
|
2897
|
+
const calls = [];
|
|
2898
|
+
for (const p of pools) {
|
|
2899
|
+
calls.push([p.gauge, encodeFunctionData10({ abi: gaugeAbi, functionName: "rewardRate" })]);
|
|
2900
|
+
calls.push([p.gauge, encodeFunctionData10({ abi: gaugeAbi, functionName: "totalSupply" })]);
|
|
2901
|
+
calls.push([p.gauge, encodeFunctionData10({ abi: gaugeAbi, functionName: "rewardToken" })]);
|
|
2902
|
+
}
|
|
2903
|
+
const results = await multicallRead(this.rpcUrl, calls).catch(() => []);
|
|
2904
|
+
for (let i = 0; i < pools.length; i++) {
|
|
2905
|
+
const base = i * 3;
|
|
2906
|
+
try {
|
|
2907
|
+
pools[i].rewardRate = results[base] ? decodeFunctionResult3({ abi: _u256Abi, functionName: "f", data: results[base] }) : 0n;
|
|
2908
|
+
} catch {
|
|
2909
|
+
pools[i].rewardRate = 0n;
|
|
2910
|
+
}
|
|
2911
|
+
try {
|
|
2912
|
+
pools[i].totalStaked = results[base + 1] ? decodeFunctionResult3({ abi: _u256Abi, functionName: "f", data: results[base + 1] }) : 0n;
|
|
2913
|
+
} catch {
|
|
2914
|
+
pools[i].totalStaked = 0n;
|
|
2915
|
+
}
|
|
2916
|
+
try {
|
|
2917
|
+
pools[i].rewardToken = results[base + 2] ? decodeAddress2(results[base + 2]) ?? void 0 : void 0;
|
|
2918
|
+
} catch {
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
const KNOWN_REWARD_TOKENS = [
|
|
2922
|
+
"0x555570a286F15EbDFE42B66eDE2f724Aa1AB5555",
|
|
2923
|
+
// xRAM
|
|
2924
|
+
"0x5555555555555555555555555555555555555555",
|
|
2925
|
+
// WHYPE
|
|
2926
|
+
"0x067b0C72aa4C6Bd3BFEFfF443c536DCd6a25a9C8",
|
|
2927
|
+
// HYBR
|
|
2928
|
+
"0x07c57E32a3C29D5659bda1d3EFC2E7BF004E3035"
|
|
2929
|
+
// NEST
|
|
2930
|
+
];
|
|
2931
|
+
const needsFallback = pools.filter((p) => (p.rewardRate ?? 0n) === 0n && (p.totalStaked ?? 0n) > 0n);
|
|
2932
|
+
if (needsFallback.length === 0) return;
|
|
2933
|
+
const rdCalls = [];
|
|
2934
|
+
const rdMeta = [];
|
|
2935
|
+
for (const p of needsFallback) {
|
|
2936
|
+
const poolIdx = pools.indexOf(p);
|
|
2937
|
+
for (const token of KNOWN_REWARD_TOKENS) {
|
|
2938
|
+
rdCalls.push([p.gauge, encodeFunctionData10({ abi: gaugeAbi, functionName: "rewardData", args: [token] })]);
|
|
2939
|
+
rdMeta.push({ poolIdx, token });
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
const rdResults = await multicallRead(this.rpcUrl, rdCalls).catch(() => []);
|
|
2943
|
+
const _rdAbi = parseAbi10(["function f() view returns (uint256, uint256, uint256, uint256)"]);
|
|
2944
|
+
for (let i = 0; i < rdMeta.length; i++) {
|
|
2945
|
+
const { poolIdx, token } = rdMeta[i];
|
|
2946
|
+
const pool = pools[poolIdx];
|
|
2947
|
+
if ((pool.rewardRate ?? 0n) > 0n) continue;
|
|
2948
|
+
try {
|
|
2949
|
+
if (!rdResults[i]) continue;
|
|
2950
|
+
const decoded = decodeFunctionResult3({ abi: _rdAbi, functionName: "f", data: rdResults[i] });
|
|
2951
|
+
const [periodFinish, rewardRate] = decoded;
|
|
2952
|
+
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
2953
|
+
if (rewardRate > 0n && periodFinish > now) {
|
|
2954
|
+
pool.rewardRate = rewardRate;
|
|
2955
|
+
pool.rewardToken = token;
|
|
2956
|
+
}
|
|
2957
|
+
} catch {
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2582
2961
|
async _discoverV2GaugedPools(out) {
|
|
2583
2962
|
if (!this.rpcUrl || !this.v2Factory) return;
|
|
2584
|
-
const v2FactoryAbi = parseAbi10([
|
|
2585
|
-
"function allPairsLength() external view returns (uint256)",
|
|
2586
|
-
"function allPairs(uint256) external view returns (address)"
|
|
2587
|
-
]);
|
|
2588
2963
|
const pairAbi = parseAbi10([
|
|
2589
2964
|
"function token0() external view returns (address)",
|
|
2590
2965
|
"function token1() external view returns (address)",
|
|
2591
2966
|
"function stable() external view returns (bool)"
|
|
2592
2967
|
]);
|
|
2593
2968
|
const erc20SymbolAbi = parseAbi10(["function symbol() external view returns (string)"]);
|
|
2969
|
+
const voterLengthAbi = parseAbi10(["function length() external view returns (uint256)"]);
|
|
2970
|
+
const voterPoolsAbi = parseAbi10(["function pools(uint256) external view returns (address)"]);
|
|
2971
|
+
const gaugeForPoolAbi = parseAbi10(["function gaugeForPool(address) external view returns (address)"]);
|
|
2972
|
+
const poolToGaugeAbi = parseAbi10(["function poolToGauge(address) external view returns (address)"]);
|
|
2973
|
+
const gaugesAbi = parseAbi10(["function gauges(address) external view returns (address)"]);
|
|
2594
2974
|
const client = createPublicClient6({ transport: http6(this.rpcUrl) });
|
|
2595
|
-
let
|
|
2975
|
+
let pairs = [];
|
|
2976
|
+
let voterPoolCount = 0;
|
|
2596
2977
|
try {
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
abi: v2FactoryAbi,
|
|
2600
|
-
functionName: "allPairsLength"
|
|
2601
|
-
});
|
|
2978
|
+
const len = await client.readContract({ address: this.voter, abi: voterLengthAbi, functionName: "length" });
|
|
2979
|
+
voterPoolCount = Number(len);
|
|
2602
2980
|
} catch {
|
|
2603
|
-
return;
|
|
2604
2981
|
}
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
this.
|
|
2611
|
-
|
|
2612
|
-
|
|
2982
|
+
if (voterPoolCount > 0) {
|
|
2983
|
+
const MAX_SCAN = 500;
|
|
2984
|
+
const startIdx = Math.max(0, voterPoolCount - MAX_SCAN);
|
|
2985
|
+
const poolCalls = [];
|
|
2986
|
+
for (let i = startIdx; i < voterPoolCount; i++) {
|
|
2987
|
+
poolCalls.push([this.voter, encodeFunctionData10({ abi: voterPoolsAbi, functionName: "pools", args: [BigInt(i)] })]);
|
|
2988
|
+
}
|
|
2989
|
+
const poolResults = await multicallRead(this.rpcUrl, poolCalls);
|
|
2990
|
+
pairs = poolResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !== zeroAddress6);
|
|
2991
|
+
} else {
|
|
2992
|
+
const v2FactoryAbi = parseAbi10(["function allPairsLength() view returns (uint256)", "function allPairs(uint256) view returns (address)"]);
|
|
2993
|
+
const solidlyFactoryAbi = parseAbi10(["function allPoolsLength() view returns (uint256)", "function allPools(uint256) view returns (address)"]);
|
|
2994
|
+
let pairCount = 0n;
|
|
2995
|
+
let useSolidly = false;
|
|
2996
|
+
try {
|
|
2997
|
+
pairCount = await client.readContract({ address: this.v2Factory, abi: v2FactoryAbi, functionName: "allPairsLength" });
|
|
2998
|
+
} catch {
|
|
2999
|
+
try {
|
|
3000
|
+
pairCount = await client.readContract({ address: this.v2Factory, abi: solidlyFactoryAbi, functionName: "allPoolsLength" });
|
|
3001
|
+
useSolidly = true;
|
|
3002
|
+
} catch {
|
|
3003
|
+
return;
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
const count = Number(pairCount);
|
|
3007
|
+
if (count === 0) return;
|
|
3008
|
+
const MAX_SCAN = 500;
|
|
3009
|
+
const startIdx = Math.max(0, count - MAX_SCAN);
|
|
3010
|
+
const fetchAbi = useSolidly ? solidlyFactoryAbi : v2FactoryAbi;
|
|
3011
|
+
const fetchFn = useSolidly ? "allPools" : "allPairs";
|
|
3012
|
+
const pairCalls = [];
|
|
3013
|
+
for (let i = startIdx; i < count; i++) pairCalls.push([this.v2Factory, encodeFunctionData10({ abi: fetchAbi, functionName: fetchFn, args: [BigInt(i)] })]);
|
|
3014
|
+
const pairResults = await multicallRead(this.rpcUrl, pairCalls);
|
|
3015
|
+
pairs = pairResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !== zeroAddress6);
|
|
2613
3016
|
}
|
|
2614
|
-
const pairAddressResults = await multicallRead(this.rpcUrl, pairAddressCalls);
|
|
2615
|
-
const pairs = pairAddressResults.map((r) => decodeAddress2(r)).filter((a) => a !== null && a !== zeroAddress6);
|
|
2616
3017
|
if (pairs.length === 0) return;
|
|
2617
|
-
const
|
|
2618
|
-
const poolToGaugeAbi = parseAbi10(["function poolToGauge(address) external view returns (address)"]);
|
|
2619
|
-
const gaugeCalls = pairs.map((pair) => [
|
|
2620
|
-
this.voter,
|
|
2621
|
-
encodeFunctionData10({ abi: gaugeForPoolAbi, functionName: "gaugeForPool", args: [pair] })
|
|
2622
|
-
]);
|
|
3018
|
+
const gaugeCalls = pairs.map((p) => [this.voter, encodeFunctionData10({ abi: gaugeForPoolAbi, functionName: "gaugeForPool", args: [p] })]);
|
|
2623
3019
|
let gaugeResults = await multicallRead(this.rpcUrl, gaugeCalls);
|
|
2624
|
-
const
|
|
2625
|
-
if (
|
|
2626
|
-
const
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
3020
|
+
const allNull = gaugeResults.every((r) => !r || decodeAddress2(r) === zeroAddress6 || decodeAddress2(r) === null);
|
|
3021
|
+
if (allNull) {
|
|
3022
|
+
const fb1 = pairs.map((p) => [this.voter, encodeFunctionData10({ abi: poolToGaugeAbi, functionName: "poolToGauge", args: [p] })]);
|
|
3023
|
+
gaugeResults = await multicallRead(this.rpcUrl, fb1);
|
|
3024
|
+
}
|
|
3025
|
+
const allNull2 = gaugeResults.every((r) => !r || decodeAddress2(r) === zeroAddress6 || decodeAddress2(r) === null);
|
|
3026
|
+
if (allNull2) {
|
|
3027
|
+
const fb2 = pairs.map((p) => [this.voter, encodeFunctionData10({ abi: gaugesAbi, functionName: "gauges", args: [p] })]);
|
|
3028
|
+
gaugeResults = await multicallRead(this.rpcUrl, fb2);
|
|
2631
3029
|
}
|
|
2632
3030
|
const gaugedPairs = [];
|
|
2633
3031
|
for (let i = 0; i < pairs.length; i++) {
|
|
@@ -2672,6 +3070,8 @@ var init_dist2 = __esm({
|
|
|
2672
3070
|
gauge,
|
|
2673
3071
|
token0: t0 ? symbolMap.get(t0) ?? t0.slice(0, 10) : "?",
|
|
2674
3072
|
token1: t1 ? symbolMap.get(t1) ?? t1.slice(0, 10) : "?",
|
|
3073
|
+
token0Addr: t0 ?? void 0,
|
|
3074
|
+
token1Addr: t1 ?? void 0,
|
|
2675
3075
|
type: "V2",
|
|
2676
3076
|
stable
|
|
2677
3077
|
});
|
|
@@ -2692,8 +3092,7 @@ var init_dist2 = __esm({
|
|
|
2692
3092
|
const erc20SymbolAbi = parseAbi10(["function symbol() external view returns (string)"]);
|
|
2693
3093
|
const gaugeForPoolAbi = parseAbi10(["function gaugeForPool(address) external view returns (address)"]);
|
|
2694
3094
|
const poolToGaugeAbi = parseAbi10(["function poolToGauge(address) external view returns (address)"]);
|
|
2695
|
-
const
|
|
2696
|
-
const tokenAddresses = tokenEntries.map(([, addr]) => addr);
|
|
3095
|
+
const tokenAddresses = this.tokens ?? Object.values(HYPEREVM_TOKENS);
|
|
2697
3096
|
const pairs = [];
|
|
2698
3097
|
for (let i = 0; i < tokenAddresses.length; i++) {
|
|
2699
3098
|
for (let j = i + 1; j < tokenAddresses.length; j++) {
|
|
@@ -2798,6 +3197,8 @@ var init_dist2 = __esm({
|
|
|
2798
3197
|
gauge,
|
|
2799
3198
|
token0: symbolMap.get(t0) ?? t0.slice(0, 10),
|
|
2800
3199
|
token1: symbolMap.get(t1) ?? t1.slice(0, 10),
|
|
3200
|
+
token0Addr: t0,
|
|
3201
|
+
token1Addr: t1,
|
|
2801
3202
|
type: "CL",
|
|
2802
3203
|
tickSpacing
|
|
2803
3204
|
});
|
|
@@ -2805,6 +3206,25 @@ var init_dist2 = __esm({
|
|
|
2805
3206
|
}
|
|
2806
3207
|
// IGauge
|
|
2807
3208
|
async buildDeposit(gauge, amount, tokenId, lpToken) {
|
|
3209
|
+
if (this.clNftMode && tokenId !== void 0 && this.positionManager) {
|
|
3210
|
+
const nftAbi = parseAbi10(["function approve(address to, uint256 tokenId) external"]);
|
|
3211
|
+
const clGaugeAbi = parseAbi10(["function deposit(uint256 tokenId) external"]);
|
|
3212
|
+
const approveTx = {
|
|
3213
|
+
description: `[${this.protocolName}] Approve LP NFT #${tokenId} to gauge`,
|
|
3214
|
+
to: this.positionManager,
|
|
3215
|
+
data: encodeFunctionData10({ abi: nftAbi, functionName: "approve", args: [gauge, tokenId] }),
|
|
3216
|
+
value: 0n,
|
|
3217
|
+
gas_estimate: 8e4
|
|
3218
|
+
};
|
|
3219
|
+
return {
|
|
3220
|
+
description: `[${this.protocolName}] Deposit LP NFT #${tokenId} to CL gauge`,
|
|
3221
|
+
to: gauge,
|
|
3222
|
+
data: encodeFunctionData10({ abi: clGaugeAbi, functionName: "deposit", args: [tokenId] }),
|
|
3223
|
+
value: 0n,
|
|
3224
|
+
gas_estimate: 9e5,
|
|
3225
|
+
pre_txs: [approveTx]
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
2808
3228
|
if (tokenId !== void 0) {
|
|
2809
3229
|
const data2 = encodeFunctionData10({
|
|
2810
3230
|
abi: gaugeAbi,
|
|
@@ -2834,7 +3254,17 @@ var init_dist2 = __esm({
|
|
|
2834
3254
|
approvals: lpToken ? [{ token: lpToken, spender: gauge, amount }] : void 0
|
|
2835
3255
|
};
|
|
2836
3256
|
}
|
|
2837
|
-
async buildWithdraw(gauge, amount) {
|
|
3257
|
+
async buildWithdraw(gauge, amount, tokenId) {
|
|
3258
|
+
if (this.clNftMode && tokenId !== void 0) {
|
|
3259
|
+
const clGaugeAbi = parseAbi10(["function withdraw(uint256 tokenId) external"]);
|
|
3260
|
+
return {
|
|
3261
|
+
description: `[${this.protocolName}] Withdraw LP NFT #${tokenId} from CL gauge`,
|
|
3262
|
+
to: gauge,
|
|
3263
|
+
data: encodeFunctionData10({ abi: clGaugeAbi, functionName: "withdraw", args: [tokenId] }),
|
|
3264
|
+
value: 0n,
|
|
3265
|
+
gas_estimate: 6e5
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
2838
3268
|
const data = encodeFunctionData10({
|
|
2839
3269
|
abi: gaugeAbi,
|
|
2840
3270
|
functionName: "withdraw",
|
|
@@ -2924,16 +3354,16 @@ var init_dist2 = __esm({
|
|
|
2924
3354
|
}
|
|
2925
3355
|
async buildClaimRewards(gauge, account) {
|
|
2926
3356
|
if (!this.rpcUrl || !account) {
|
|
2927
|
-
const
|
|
3357
|
+
const data = encodeFunctionData10({
|
|
2928
3358
|
abi: gaugeAbi,
|
|
2929
3359
|
functionName: "getReward",
|
|
2930
3360
|
args: [account ?? zeroAddress6]
|
|
2931
3361
|
});
|
|
2932
|
-
return { description: `[${this.protocolName}] Claim gauge rewards`, to: gauge, data
|
|
3362
|
+
return { description: `[${this.protocolName}] Claim gauge rewards`, to: gauge, data, value: 0n, gas_estimate: 2e5 };
|
|
2933
3363
|
}
|
|
2934
3364
|
const { tokens, multiToken } = await this.discoverRewardTokens(gauge);
|
|
2935
3365
|
if (multiToken && tokens.length > 0) {
|
|
2936
|
-
const
|
|
3366
|
+
const data = encodeFunctionData10({
|
|
2937
3367
|
abi: gaugeAbi,
|
|
2938
3368
|
functionName: "getReward",
|
|
2939
3369
|
args: [account, tokens]
|
|
@@ -2941,41 +3371,179 @@ var init_dist2 = __esm({
|
|
|
2941
3371
|
return {
|
|
2942
3372
|
description: `[${this.protocolName}] Claim gauge rewards (${tokens.length} tokens)`,
|
|
2943
3373
|
to: gauge,
|
|
2944
|
-
data
|
|
3374
|
+
data,
|
|
2945
3375
|
value: 0n,
|
|
2946
3376
|
gas_estimate: 3e5
|
|
2947
3377
|
};
|
|
2948
3378
|
}
|
|
3379
|
+
const accountVariant = encodeFunctionData10({
|
|
3380
|
+
abi: gaugeAbi,
|
|
3381
|
+
functionName: "getReward",
|
|
3382
|
+
args: [account]
|
|
3383
|
+
});
|
|
3384
|
+
try {
|
|
3385
|
+
const client = createPublicClient6({ transport: http6(this.rpcUrl) });
|
|
3386
|
+
await client.call({ account, to: gauge, data: accountVariant });
|
|
3387
|
+
return {
|
|
3388
|
+
description: `[${this.protocolName}] Claim gauge rewards (getReward(account))`,
|
|
3389
|
+
to: gauge,
|
|
3390
|
+
data: accountVariant,
|
|
3391
|
+
value: 0n,
|
|
3392
|
+
gas_estimate: 2e5
|
|
3393
|
+
};
|
|
3394
|
+
} catch {
|
|
3395
|
+
const noArg = encodeFunctionData10({ abi: gaugeAbi, functionName: "getReward", args: [] });
|
|
3396
|
+
return {
|
|
3397
|
+
description: `[${this.protocolName}] Claim gauge rewards (getReward())`,
|
|
3398
|
+
to: gauge,
|
|
3399
|
+
data: noArg,
|
|
3400
|
+
value: 0n,
|
|
3401
|
+
gas_estimate: 2e5
|
|
3402
|
+
};
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
/**
|
|
3406
|
+
* Claim rewards for a CL gauge by NFT tokenId (Hybra V4 style — single-arg getReward(tokenId)).
|
|
3407
|
+
*/
|
|
3408
|
+
async buildClaimRewardsByTokenId(gauge, tokenId) {
|
|
2949
3409
|
const data = encodeFunctionData10({
|
|
2950
3410
|
abi: gaugeAbi,
|
|
2951
3411
|
functionName: "getReward",
|
|
2952
|
-
args: []
|
|
3412
|
+
args: [tokenId]
|
|
2953
3413
|
});
|
|
2954
3414
|
return {
|
|
2955
|
-
description: `[${this.protocolName}] Claim gauge rewards`,
|
|
3415
|
+
description: `[${this.protocolName}] Claim gauge rewards for NFT #${tokenId}`,
|
|
2956
3416
|
to: gauge,
|
|
2957
3417
|
data,
|
|
2958
3418
|
value: 0n,
|
|
2959
|
-
gas_estimate:
|
|
3419
|
+
gas_estimate: 3e5
|
|
2960
3420
|
};
|
|
2961
3421
|
}
|
|
2962
3422
|
/**
|
|
2963
|
-
*
|
|
3423
|
+
* Ramses-CL claim via NPM.getPeriodReward — the user-facing claim path.
|
|
3424
|
+
* The gauge contract restricts `getReward*` to authorized claimers (voter + NPM only);
|
|
3425
|
+
* EOAs must route through NPM, which calls into the gauge with msg.sender = NPM.
|
|
3426
|
+
*
|
|
3427
|
+
* ABI: getPeriodReward(uint256 period, uint256 tokenId, address[] tokens, address receiver)
|
|
3428
|
+
* `period` defaults to current Solidly weekly epoch index (block.timestamp / 604800).
|
|
3429
|
+
* `tokens` defaults to gauge.getRewardTokens() when `gauge` is provided.
|
|
3430
|
+
*
|
|
3431
|
+
* Verified 2026-04-29 on anvil fork: NPM.getPeriodReward(2938, 177068, [..., xRAM], wallet)
|
|
3432
|
+
* delivered 71.11 xRAM after 1h emission warp; direct gauge.getReward(...) reverts
|
|
3433
|
+
* with NOT_AUTHORIZED_CLAIMER for the same EOA.
|
|
2964
3434
|
*/
|
|
2965
|
-
async
|
|
3435
|
+
async buildClaimRewardsViaNPMPeriodReward(npm, tokenId, receiver, opts) {
|
|
3436
|
+
let rewardTokens = opts?.tokens;
|
|
3437
|
+
if (!rewardTokens || rewardTokens.length === 0) {
|
|
3438
|
+
if (!opts?.gauge) {
|
|
3439
|
+
throw DefiError.invalidParam(
|
|
3440
|
+
"Ramses CL claim requires either `tokens` or `gauge` (for getRewardTokens lookup)"
|
|
3441
|
+
);
|
|
3442
|
+
}
|
|
3443
|
+
if (!this.rpcUrl) throw DefiError.rpcError("RPC URL required to discover reward tokens");
|
|
3444
|
+
const client = createPublicClient6({ transport: http6(this.rpcUrl) });
|
|
3445
|
+
try {
|
|
3446
|
+
rewardTokens = await client.readContract({
|
|
3447
|
+
address: opts.gauge,
|
|
3448
|
+
abi: gaugeAbi,
|
|
3449
|
+
functionName: "getRewardTokens"
|
|
3450
|
+
});
|
|
3451
|
+
} catch {
|
|
3452
|
+
throw DefiError.contractError(
|
|
3453
|
+
`[${this.protocolName}] gauge.getRewardTokens() reverted \u2014 pass tokens[] explicitly`
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
if (rewardTokens.length === 0) {
|
|
3458
|
+
throw DefiError.contractError(`[${this.protocolName}] no reward tokens to claim`);
|
|
3459
|
+
}
|
|
3460
|
+
const epochSeconds = 604800n;
|
|
3461
|
+
const period = opts?.period ?? BigInt(Math.floor(Date.now() / 1e3)) / epochSeconds;
|
|
3462
|
+
const npmClaimAbi = parseAbi10([
|
|
3463
|
+
"function getPeriodReward(uint256 period, uint256 tokenId, address[] tokens, address receiver) external"
|
|
3464
|
+
]);
|
|
3465
|
+
const data = encodeFunctionData10({
|
|
3466
|
+
abi: npmClaimAbi,
|
|
3467
|
+
functionName: "getPeriodReward",
|
|
3468
|
+
args: [period, tokenId, rewardTokens, receiver]
|
|
3469
|
+
});
|
|
3470
|
+
return {
|
|
3471
|
+
description: `[${this.protocolName}] Claim via NPM.getPeriodReward(period=${period}, tokenId=${tokenId}, ${rewardTokens.length} tokens)`,
|
|
3472
|
+
to: npm,
|
|
3473
|
+
data,
|
|
3474
|
+
value: 0n,
|
|
3475
|
+
gas_estimate: 6e5
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
/**
|
|
3479
|
+
* @deprecated Direct gauge.getReward(tokenId, tokens[]) reverts with NOT_AUTHORIZED_CLAIMER
|
|
3480
|
+
* for EOAs on Ramses CL. Use buildClaimRewardsViaNPMPeriodReward instead.
|
|
3481
|
+
*/
|
|
3482
|
+
async buildClaimRewardsByCLTokenIdMulti(gauge, tokenId, tokens) {
|
|
3483
|
+
let rewardTokens = tokens;
|
|
3484
|
+
if (!rewardTokens || rewardTokens.length === 0) {
|
|
3485
|
+
if (!this.rpcUrl) throw DefiError.rpcError("RPC URL required to discover reward tokens");
|
|
3486
|
+
const client = createPublicClient6({ transport: http6(this.rpcUrl) });
|
|
3487
|
+
try {
|
|
3488
|
+
rewardTokens = await client.readContract({
|
|
3489
|
+
address: gauge,
|
|
3490
|
+
abi: gaugeAbi,
|
|
3491
|
+
functionName: "getRewardTokens"
|
|
3492
|
+
});
|
|
3493
|
+
} catch {
|
|
3494
|
+
throw DefiError.contractError(
|
|
3495
|
+
`[${this.protocolName}] gauge.getRewardTokens() reverted \u2014 pass tokens[] explicitly`
|
|
3496
|
+
);
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
if (rewardTokens.length === 0) {
|
|
3500
|
+
throw DefiError.contractError(`[${this.protocolName}] no reward tokens to claim`);
|
|
3501
|
+
}
|
|
2966
3502
|
const data = encodeFunctionData10({
|
|
2967
3503
|
abi: gaugeAbi,
|
|
2968
3504
|
functionName: "getReward",
|
|
2969
|
-
args: [tokenId]
|
|
3505
|
+
args: [tokenId, rewardTokens]
|
|
2970
3506
|
});
|
|
2971
3507
|
return {
|
|
2972
|
-
description: `[${this.protocolName}] Claim gauge rewards for NFT #${tokenId}`,
|
|
3508
|
+
description: `[${this.protocolName}] Claim CL gauge rewards for NFT #${tokenId} (${rewardTokens.length} tokens)`,
|
|
2973
3509
|
to: gauge,
|
|
2974
3510
|
data,
|
|
2975
3511
|
value: 0n,
|
|
2976
|
-
gas_estimate:
|
|
3512
|
+
gas_estimate: 4e5
|
|
2977
3513
|
};
|
|
2978
3514
|
}
|
|
3515
|
+
/**
|
|
3516
|
+
* Ramses-CL-style pending rewards: earned(token, tokenId) per reward token from
|
|
3517
|
+
* gauge.getRewardTokens(). Returns raw amounts; caller resolves USD value.
|
|
3518
|
+
*/
|
|
3519
|
+
async getPendingRewardsByCLTokenIdMulti(gauge, tokenId) {
|
|
3520
|
+
if (!this.rpcUrl) throw DefiError.rpcError("RPC URL required");
|
|
3521
|
+
const client = createPublicClient6({ transport: http6(this.rpcUrl) });
|
|
3522
|
+
let rewardTokens;
|
|
3523
|
+
try {
|
|
3524
|
+
rewardTokens = await client.readContract({
|
|
3525
|
+
address: gauge,
|
|
3526
|
+
abi: gaugeAbi,
|
|
3527
|
+
functionName: "getRewardTokens"
|
|
3528
|
+
});
|
|
3529
|
+
} catch {
|
|
3530
|
+
return [];
|
|
3531
|
+
}
|
|
3532
|
+
const out = [];
|
|
3533
|
+
for (const token of rewardTokens) {
|
|
3534
|
+
try {
|
|
3535
|
+
const amount = await client.readContract({
|
|
3536
|
+
address: gauge,
|
|
3537
|
+
abi: gaugeAbi,
|
|
3538
|
+
functionName: "earned",
|
|
3539
|
+
args: [token, tokenId]
|
|
3540
|
+
});
|
|
3541
|
+
out.push({ token, symbol: token.slice(0, 10), amount });
|
|
3542
|
+
} catch {
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
return out;
|
|
3546
|
+
}
|
|
2979
3547
|
async getPendingRewards(gauge, user) {
|
|
2980
3548
|
if (!this.rpcUrl) throw DefiError.rpcError("RPC URL required");
|
|
2981
3549
|
const client = createPublicClient6({ transport: http6(this.rpcUrl) });
|
|
@@ -3032,6 +3600,21 @@ var init_dist2 = __esm({
|
|
|
3032
3600
|
args: [tokenId]
|
|
3033
3601
|
});
|
|
3034
3602
|
}
|
|
3603
|
+
/**
|
|
3604
|
+
* Get pending rewards for an Aerodrome Slipstream CL gauge NFT position.
|
|
3605
|
+
* Uses the earned(address account, uint256 tokenId) overload, which is required
|
|
3606
|
+
* for CL gauges — the single-param earned(address) reverts on these contracts.
|
|
3607
|
+
*/
|
|
3608
|
+
async getPendingRewardsByCLTokenId(gauge, user, tokenId) {
|
|
3609
|
+
if (!this.rpcUrl) throw DefiError.rpcError("RPC URL required");
|
|
3610
|
+
const client = createPublicClient6({ transport: http6(this.rpcUrl) });
|
|
3611
|
+
return await client.readContract({
|
|
3612
|
+
address: gauge,
|
|
3613
|
+
abi: gaugeAbi,
|
|
3614
|
+
functionName: "earned",
|
|
3615
|
+
args: [user, tokenId]
|
|
3616
|
+
});
|
|
3617
|
+
}
|
|
3035
3618
|
// IVoteEscrow
|
|
3036
3619
|
async buildCreateLock(amount, lockDuration) {
|
|
3037
3620
|
const data = encodeFunctionData10({
|
|
@@ -3527,9 +4110,44 @@ var init_dist2 = __esm({
|
|
|
3527
4110
|
}
|
|
3528
4111
|
];
|
|
3529
4112
|
}
|
|
4113
|
+
/**
|
|
4114
|
+
* Scan ±scanRange bins around the active bin and return the user's non-zero balance bin IDs.
|
|
4115
|
+
* Critical: the rewarder may track pending rewards for bins OUTSIDE its current rewarded range
|
|
4116
|
+
* (e.g. when the rewarded range shifts after a position was already in place). Always claim
|
|
4117
|
+
* against the user's actual positions, not the rewarder's "current" range.
|
|
4118
|
+
*/
|
|
4119
|
+
async findUserBinsWithBalance(pool, user, scanRange = 50) {
|
|
4120
|
+
const rpcUrl = this.requireRpc();
|
|
4121
|
+
const client = createPublicClient8({ transport: http8(rpcUrl) });
|
|
4122
|
+
const activeId = await client.readContract({
|
|
4123
|
+
address: pool,
|
|
4124
|
+
abi: lbPairAbi,
|
|
4125
|
+
functionName: "getActiveId"
|
|
4126
|
+
});
|
|
4127
|
+
const calls = [];
|
|
4128
|
+
const binIds = [];
|
|
4129
|
+
for (let b = activeId - scanRange; b <= activeId + scanRange; b++) {
|
|
4130
|
+
binIds.push(b);
|
|
4131
|
+
calls.push([pool, encodeFunctionData12({
|
|
4132
|
+
abi: parseAbi12(["function balanceOf(address account, uint256 id) view returns (uint256)"]),
|
|
4133
|
+
functionName: "balanceOf",
|
|
4134
|
+
args: [user, BigInt(b)]
|
|
4135
|
+
})]);
|
|
4136
|
+
}
|
|
4137
|
+
const results = await multicallRead(rpcUrl, calls);
|
|
4138
|
+
const owned = [];
|
|
4139
|
+
for (let i = 0; i < binIds.length; i++) {
|
|
4140
|
+
const data = results[i];
|
|
4141
|
+
if (!data) continue;
|
|
4142
|
+
const hex = data.slice(2).padStart(64, "0");
|
|
4143
|
+
if (hex !== "0".repeat(64)) owned.push(binIds[i]);
|
|
4144
|
+
}
|
|
4145
|
+
return owned;
|
|
4146
|
+
}
|
|
3530
4147
|
/**
|
|
3531
4148
|
* Build a claim rewards transaction for specific LB bins.
|
|
3532
|
-
* If binIds is omitted, auto-detects from the
|
|
4149
|
+
* If binIds is omitted, auto-detects from the user's actual non-zero balance bins (active ±50 scan).
|
|
4150
|
+
* This catches rewards accumulated in bins outside the rewarder's current rewarded range.
|
|
3533
4151
|
*/
|
|
3534
4152
|
async buildClaimRewards(user, pool, binIds) {
|
|
3535
4153
|
const rpcUrl = this.requireRpc();
|
|
@@ -3545,15 +4163,18 @@ var init_dist2 = __esm({
|
|
|
3545
4163
|
}
|
|
3546
4164
|
let resolvedBinIds = binIds;
|
|
3547
4165
|
if (!resolvedBinIds || resolvedBinIds.length === 0) {
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
4166
|
+
resolvedBinIds = await this.findUserBinsWithBalance(pool, user);
|
|
4167
|
+
if (resolvedBinIds.length === 0) {
|
|
4168
|
+
const range = await client.readContract({
|
|
4169
|
+
address: rewarder,
|
|
4170
|
+
abi: lbRewarderAbi,
|
|
4171
|
+
functionName: "getRewardedRange"
|
|
4172
|
+
});
|
|
4173
|
+
const min = Number(range[0]);
|
|
4174
|
+
const max = Number(range[1]);
|
|
4175
|
+
resolvedBinIds = [];
|
|
4176
|
+
for (let b = min; b <= max; b++) resolvedBinIds.push(b);
|
|
4177
|
+
}
|
|
3557
4178
|
}
|
|
3558
4179
|
const data = encodeFunctionData12({
|
|
3559
4180
|
abi: lbRewarderAbi,
|
|
@@ -3964,7 +4585,7 @@ var init_dist2 = __esm({
|
|
|
3964
4585
|
"function enterFarming((address rewardToken, address bonusRewardToken, address pool, uint256 nonce) key, uint256 tokenId) external",
|
|
3965
4586
|
"function exitFarming((address rewardToken, address bonusRewardToken, address pool, uint256 nonce) key, uint256 tokenId) external",
|
|
3966
4587
|
"function collectRewards((address rewardToken, address bonusRewardToken, address pool, uint256 nonce) key, uint256 tokenId) external",
|
|
3967
|
-
"function claimReward(address rewardToken, address to,
|
|
4588
|
+
"function claimReward(address rewardToken, address to, uint256 amountRequested) external returns (uint256 reward)"
|
|
3968
4589
|
]);
|
|
3969
4590
|
positionManagerAbi2 = parseAbi13([
|
|
3970
4591
|
"function approveForFarming(uint256 tokenId, bool approve, address farmingAddress) external",
|
|
@@ -3986,13 +4607,17 @@ var init_dist2 = __esm({
|
|
|
3986
4607
|
positionManager;
|
|
3987
4608
|
rpcUrl;
|
|
3988
4609
|
factory;
|
|
3989
|
-
|
|
4610
|
+
rewardToken;
|
|
4611
|
+
bonusRewardToken;
|
|
4612
|
+
constructor(protocolName, farmingCenter, eternalFarming, positionManager, rpcUrl, factory, rewardToken = KITTEN_TOKEN, bonusRewardToken = WHYPE_TOKEN) {
|
|
3990
4613
|
this.protocolName = protocolName;
|
|
3991
4614
|
this.farmingCenter = farmingCenter;
|
|
3992
4615
|
this.eternalFarming = eternalFarming;
|
|
3993
4616
|
this.positionManager = positionManager;
|
|
3994
4617
|
this.rpcUrl = rpcUrl;
|
|
3995
4618
|
this.factory = factory;
|
|
4619
|
+
this.rewardToken = rewardToken;
|
|
4620
|
+
this.bonusRewardToken = bonusRewardToken;
|
|
3996
4621
|
}
|
|
3997
4622
|
name() {
|
|
3998
4623
|
return this.protocolName;
|
|
@@ -4007,8 +4632,8 @@ var init_dist2 = __esm({
|
|
|
4007
4632
|
const poolLc = pool.toLowerCase();
|
|
4008
4633
|
if (nonceCache.has(poolLc)) {
|
|
4009
4634
|
return {
|
|
4010
|
-
rewardToken:
|
|
4011
|
-
bonusRewardToken:
|
|
4635
|
+
rewardToken: this.rewardToken,
|
|
4636
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4012
4637
|
pool,
|
|
4013
4638
|
nonce: nonceCache.get(poolLc)
|
|
4014
4639
|
};
|
|
@@ -4019,8 +4644,8 @@ var init_dist2 = __esm({
|
|
|
4019
4644
|
const nonce = BigInt(n);
|
|
4020
4645
|
nonces.push(nonce);
|
|
4021
4646
|
const key = {
|
|
4022
|
-
rewardToken:
|
|
4023
|
-
bonusRewardToken:
|
|
4647
|
+
rewardToken: this.rewardToken,
|
|
4648
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4024
4649
|
pool,
|
|
4025
4650
|
nonce
|
|
4026
4651
|
};
|
|
@@ -4055,8 +4680,8 @@ var init_dist2 = __esm({
|
|
|
4055
4680
|
const nonce = nonces[i];
|
|
4056
4681
|
nonceCache.set(poolLc, nonce);
|
|
4057
4682
|
return {
|
|
4058
|
-
rewardToken:
|
|
4059
|
-
bonusRewardToken:
|
|
4683
|
+
rewardToken: this.rewardToken,
|
|
4684
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4060
4685
|
pool,
|
|
4061
4686
|
nonce
|
|
4062
4687
|
};
|
|
@@ -4148,8 +4773,8 @@ var init_dist2 = __esm({
|
|
|
4148
4773
|
}
|
|
4149
4774
|
const calls = [
|
|
4150
4775
|
encodeCollectRewards(key, tokenId),
|
|
4151
|
-
encodeClaimReward(
|
|
4152
|
-
encodeClaimReward(
|
|
4776
|
+
encodeClaimReward(this.rewardToken, owner),
|
|
4777
|
+
encodeClaimReward(this.bonusRewardToken, owner)
|
|
4153
4778
|
];
|
|
4154
4779
|
return {
|
|
4155
4780
|
description: `[${this.protocolName}] Collect + claim rewards for NFT #${tokenId} in pool ${pool}`,
|
|
@@ -4164,8 +4789,8 @@ var init_dist2 = __esm({
|
|
|
4164
4789
|
*/
|
|
4165
4790
|
async buildClaimReward(owner) {
|
|
4166
4791
|
const calls = [
|
|
4167
|
-
encodeClaimReward(
|
|
4168
|
-
encodeClaimReward(
|
|
4792
|
+
encodeClaimReward(this.rewardToken, owner),
|
|
4793
|
+
encodeClaimReward(this.bonusRewardToken, owner)
|
|
4169
4794
|
];
|
|
4170
4795
|
return {
|
|
4171
4796
|
description: `[${this.protocolName}] Claim KITTEN + WHYPE farming rewards to ${owner}`,
|
|
@@ -4234,8 +4859,8 @@ var init_dist2 = __esm({
|
|
|
4234
4859
|
for (const pool of pools) {
|
|
4235
4860
|
for (let n = 0; n <= MAX_NONCE_SCAN; n++) {
|
|
4236
4861
|
const key = {
|
|
4237
|
-
rewardToken:
|
|
4238
|
-
bonusRewardToken:
|
|
4862
|
+
rewardToken: this.rewardToken,
|
|
4863
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4239
4864
|
pool,
|
|
4240
4865
|
nonce: BigInt(n)
|
|
4241
4866
|
};
|
|
@@ -4282,8 +4907,8 @@ var init_dist2 = __esm({
|
|
|
4282
4907
|
const isActive = !deactivated;
|
|
4283
4908
|
if (!bestKey || isActive && !bestActive || isActive === bestActive && nonce > bestKey.nonce) {
|
|
4284
4909
|
bestKey = {
|
|
4285
|
-
rewardToken:
|
|
4286
|
-
bonusRewardToken:
|
|
4910
|
+
rewardToken: this.rewardToken,
|
|
4911
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4287
4912
|
pool,
|
|
4288
4913
|
nonce
|
|
4289
4914
|
};
|
|
@@ -4309,6 +4934,235 @@ var init_dist2 = __esm({
|
|
|
4309
4934
|
return results;
|
|
4310
4935
|
}
|
|
4311
4936
|
};
|
|
4937
|
+
DEFAULT_BASE_URL = "https://app.usenest.xyz/api/blaze";
|
|
4938
|
+
FALLBACK_BASE_URL = "https://blaze.nest.aegas.it";
|
|
4939
|
+
NEST_TOKEN = "0x07c57E32a3C29D5659bda1d3EFC2E7BF004E3035";
|
|
4940
|
+
NEST_DECIMALS = 18;
|
|
4941
|
+
NestOffChainAdapter = class {
|
|
4942
|
+
baseUrl;
|
|
4943
|
+
fallbackUrl;
|
|
4944
|
+
voter;
|
|
4945
|
+
constructor(entry) {
|
|
4946
|
+
const voter = entry.contracts?.["voter"];
|
|
4947
|
+
if (!voter) {
|
|
4948
|
+
throw DefiError.contractError("Nest off-chain: missing 'voter' contract");
|
|
4949
|
+
}
|
|
4950
|
+
this.voter = voter;
|
|
4951
|
+
this.baseUrl = process.env["NEST_API_URL"] ?? DEFAULT_BASE_URL;
|
|
4952
|
+
this.fallbackUrl = FALLBACK_BASE_URL;
|
|
4953
|
+
}
|
|
4954
|
+
name() {
|
|
4955
|
+
return "Nest";
|
|
4956
|
+
}
|
|
4957
|
+
/** Cumulative claimed + available NEST emissions for a wallet */
|
|
4958
|
+
async getClaimStatus(wallet) {
|
|
4959
|
+
const data = await this.fetchJson(
|
|
4960
|
+
`/claim/claim-status?publicAddress=${wallet}`
|
|
4961
|
+
);
|
|
4962
|
+
const totalClaimedRaw = BigInt(data.totalClaimed);
|
|
4963
|
+
const totalAvailableRaw = BigInt(data.totalAvailable);
|
|
4964
|
+
const pendingRaw = totalAvailableRaw > totalClaimedRaw ? totalAvailableRaw - totalClaimedRaw : 0n;
|
|
4965
|
+
return {
|
|
4966
|
+
totalClaimedRaw,
|
|
4967
|
+
totalAvailableRaw,
|
|
4968
|
+
pendingRaw,
|
|
4969
|
+
pendingFormatted: Number(pendingRaw) / 10 ** NEST_DECIMALS
|
|
4970
|
+
};
|
|
4971
|
+
}
|
|
4972
|
+
/**
|
|
4973
|
+
* Backend-signed claim ticket (or null when nothing to claim).
|
|
4974
|
+
* Returns the raw ticket; `buildClaim()` is not yet implemented because the
|
|
4975
|
+
* voter contract source is unverified — function selector 0xd6d7a454 takes
|
|
4976
|
+
* 5 dynamic arrays we have not been able to disambiguate yet.
|
|
4977
|
+
*/
|
|
4978
|
+
async getClaimTicket(wallet) {
|
|
4979
|
+
const url = `${this.baseUrl}/claim/claim-data?publicAddress=${wallet}`;
|
|
4980
|
+
const res = await fetch(url, this.requestInit());
|
|
4981
|
+
const text = await res.text();
|
|
4982
|
+
if (text.includes("no points to claim")) return null;
|
|
4983
|
+
if (!res.ok) {
|
|
4984
|
+
throw DefiError.providerError(`Nest claim-data ${res.status}: ${text.slice(0, 200)}`);
|
|
4985
|
+
}
|
|
4986
|
+
let json;
|
|
4987
|
+
try {
|
|
4988
|
+
json = JSON.parse(text);
|
|
4989
|
+
} catch {
|
|
4990
|
+
throw DefiError.providerError(`Nest claim-data: non-JSON response: ${text.slice(0, 200)}`);
|
|
4991
|
+
}
|
|
4992
|
+
return {
|
|
4993
|
+
user: json.user,
|
|
4994
|
+
amount: BigInt(json.amount),
|
|
4995
|
+
timestamp: BigInt(json.timestamp),
|
|
4996
|
+
day: json.day === null ? null : BigInt(json.day),
|
|
4997
|
+
signature: json.signature.startsWith("0x") ? json.signature : `0x${json.signature}`
|
|
4998
|
+
};
|
|
4999
|
+
}
|
|
5000
|
+
/** APR estimate (percent) for a CL position with given tick range and amounts */
|
|
5001
|
+
async estimateLpApr(params) {
|
|
5002
|
+
const qs = new URLSearchParams({
|
|
5003
|
+
poolAddress: params.poolAddress,
|
|
5004
|
+
minTick: String(params.minTick),
|
|
5005
|
+
maxTick: String(params.maxTick),
|
|
5006
|
+
token0Amount: params.token0Amount.toString(),
|
|
5007
|
+
token1Amount: params.token1Amount.toString()
|
|
5008
|
+
});
|
|
5009
|
+
const data = await this.fetchJson(`/liquidity/apr/estimate?${qs}`);
|
|
5010
|
+
const apr = Number(data.apr);
|
|
5011
|
+
if (!Number.isFinite(apr)) {
|
|
5012
|
+
throw DefiError.providerError(`Nest apr/estimate: invalid apr value '${data.apr}'`);
|
|
5013
|
+
}
|
|
5014
|
+
return apr;
|
|
5015
|
+
}
|
|
5016
|
+
/** Pending NEST emissions as IGauge-compatible RewardInfo[] */
|
|
5017
|
+
async getPendingRewards(user) {
|
|
5018
|
+
const status = await this.getClaimStatus(user);
|
|
5019
|
+
if (status.pendingRaw === 0n) return [];
|
|
5020
|
+
return [{
|
|
5021
|
+
token: NEST_TOKEN,
|
|
5022
|
+
symbol: "NEST",
|
|
5023
|
+
amount: status.pendingRaw
|
|
5024
|
+
}];
|
|
5025
|
+
}
|
|
5026
|
+
/** Voter address used by aggregateClaim() — exposed for callers that build the tx themselves */
|
|
5027
|
+
getVoterAddress() {
|
|
5028
|
+
return this.voter;
|
|
5029
|
+
}
|
|
5030
|
+
/**
|
|
5031
|
+
* Build a Nest voter claim transaction by reproducing the byte-level calldata
|
|
5032
|
+
* pattern observed in successful onchain claims, swapping in the ticket's
|
|
5033
|
+
* (amount, timestamp, signature) words.
|
|
5034
|
+
*
|
|
5035
|
+
* The voter implementation source is not verified, so we cannot derive a
|
|
5036
|
+
* Solidity ABI for selector 0xd6d7a454. Instead, two known-successful claim
|
|
5037
|
+
* transactions were diffed:
|
|
5038
|
+
*
|
|
5039
|
+
* tx1: 0x99f35cfdb6fc3885ebe046c4625acc083e42d5afe6ca6962c6c81cd9006b99ba
|
|
5040
|
+
* tx2: 0x3e120ab95e9e0a9148cb8964993dd066b8a36363353fe727462231857724e7bb
|
|
5041
|
+
*
|
|
5042
|
+
* 31 of 34 calldata words are identical between the two; only words 21, 22,
|
|
5043
|
+
* 25, 26, 27 differ — and those map exactly to the backend ticket's
|
|
5044
|
+
* (amount, timestamp, sigR, sigS, sigVPadded). msg.sender is not encoded in
|
|
5045
|
+
* calldata; voter binds the claim to the caller, so the ticket signature
|
|
5046
|
+
* authorizes the EOA holding the wallet.
|
|
5047
|
+
*
|
|
5048
|
+
* Throws if no claim ticket is available.
|
|
5049
|
+
*/
|
|
5050
|
+
async buildClaim(wallet) {
|
|
5051
|
+
const ticket = await this.getClaimTicket(wallet);
|
|
5052
|
+
if (!ticket) {
|
|
5053
|
+
throw DefiError.invalidParam(`Nest: no claim ticket available for ${wallet}`);
|
|
5054
|
+
}
|
|
5055
|
+
const sigHex = ticket.signature.startsWith("0x") ? ticket.signature.slice(2) : ticket.signature;
|
|
5056
|
+
if (sigHex.length !== 130) {
|
|
5057
|
+
throw DefiError.providerError(`Nest: signature must be 65 bytes (130 hex chars), got ${sigHex.length}`);
|
|
5058
|
+
}
|
|
5059
|
+
const r = sigHex.slice(0, 64);
|
|
5060
|
+
const s = sigHex.slice(64, 128);
|
|
5061
|
+
const v = sigHex.slice(128, 130);
|
|
5062
|
+
const vPadded = v + "0".repeat(62);
|
|
5063
|
+
const amountHex = ticket.amount.toString(16).padStart(64, "0");
|
|
5064
|
+
const timestampHex = ticket.timestamp.toString(16).padStart(64, "0");
|
|
5065
|
+
const words = [
|
|
5066
|
+
"0000000000000000000000000000000000000000000000000000000000000160",
|
|
5067
|
+
// 0
|
|
5068
|
+
"0000000000000000000000000000000000000000000000000000000000000180",
|
|
5069
|
+
// 1
|
|
5070
|
+
"0000000000000000000000000000000000000000000000000000000000000200",
|
|
5071
|
+
// 2
|
|
5072
|
+
"00000000000000000000000000000000000000000000000000000000000002a0",
|
|
5073
|
+
// 3
|
|
5074
|
+
"0000000000000000000000000000000000000000000000000000000000000380",
|
|
5075
|
+
// 4
|
|
5076
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5077
|
+
// 5
|
|
5078
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5079
|
+
// 6
|
|
5080
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5081
|
+
// 7
|
|
5082
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5083
|
+
// 8
|
|
5084
|
+
"0000000000000000000000000000000000000000000000000000000000000001",
|
|
5085
|
+
// 9
|
|
5086
|
+
"0000000000000000000000000000000000000000000000000000000000000001",
|
|
5087
|
+
// 10
|
|
5088
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5089
|
+
// 11 — empty array length
|
|
5090
|
+
"0000000000000000000000000000000000000000000000000000000000000040",
|
|
5091
|
+
// 12
|
|
5092
|
+
"0000000000000000000000000000000000000000000000000000000000000060",
|
|
5093
|
+
// 13
|
|
5094
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5095
|
+
// 14
|
|
5096
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5097
|
+
// 15
|
|
5098
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5099
|
+
// 16
|
|
5100
|
+
"0000000000000000000000000000000000000000000000000000000000000060",
|
|
5101
|
+
// 17
|
|
5102
|
+
"0000000000000000000000000000000000000000000000000000000000000080",
|
|
5103
|
+
// 18
|
|
5104
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5105
|
+
// 19
|
|
5106
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5107
|
+
// 20
|
|
5108
|
+
amountHex,
|
|
5109
|
+
// 21 — ticket amount
|
|
5110
|
+
timestampHex,
|
|
5111
|
+
// 22 — ticket timestamp
|
|
5112
|
+
"0000000000000000000000000000000000000000000000000000000000000060",
|
|
5113
|
+
// 23 — sig offset
|
|
5114
|
+
"0000000000000000000000000000000000000000000000000000000000000041",
|
|
5115
|
+
// 24 — sig length (65)
|
|
5116
|
+
r,
|
|
5117
|
+
// 25 — sig r
|
|
5118
|
+
s,
|
|
5119
|
+
// 26 — sig s
|
|
5120
|
+
vPadded,
|
|
5121
|
+
// 27 — sig v + zero padding
|
|
5122
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5123
|
+
// 28
|
|
5124
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5125
|
+
// 29
|
|
5126
|
+
"0000000000000000000000000000000000000000000000000000000000000001",
|
|
5127
|
+
// 30
|
|
5128
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
5129
|
+
// 31
|
|
5130
|
+
"00000000000000000000000000000000000000000000000000000000000000a0",
|
|
5131
|
+
// 32
|
|
5132
|
+
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
5133
|
+
// 33
|
|
5134
|
+
];
|
|
5135
|
+
const data = "0xd6d7a454" + words.join("");
|
|
5136
|
+
return {
|
|
5137
|
+
description: `[${this.name()}] Claim NEST emissions (${(Number(ticket.amount) / 1e18).toFixed(2)} NEST cumulative; backend-signed ts=${ticket.timestamp})`,
|
|
5138
|
+
to: this.voter,
|
|
5139
|
+
data,
|
|
5140
|
+
value: 0n,
|
|
5141
|
+
gas_estimate: 6e5
|
|
5142
|
+
};
|
|
5143
|
+
}
|
|
5144
|
+
// ── internal ──
|
|
5145
|
+
async fetchJson(path) {
|
|
5146
|
+
const primary = `${this.baseUrl}${path.startsWith("/claim") ? path : path}`;
|
|
5147
|
+
try {
|
|
5148
|
+
const res = await fetch(primary, this.requestInit());
|
|
5149
|
+
if (res.ok) return await res.json();
|
|
5150
|
+
if (res.status >= 500) throw new Error(`upstream ${res.status}`);
|
|
5151
|
+
throw DefiError.providerError(`Nest API ${res.status}: ${(await res.text()).slice(0, 200)}`);
|
|
5152
|
+
} catch (e) {
|
|
5153
|
+
if (this.baseUrl === this.fallbackUrl) throw e;
|
|
5154
|
+
const fallback = `${this.fallbackUrl}${path}`;
|
|
5155
|
+
const res = await fetch(fallback, this.requestInit());
|
|
5156
|
+
if (!res.ok) {
|
|
5157
|
+
throw DefiError.providerError(`Nest fallback ${res.status}: ${(await res.text()).slice(0, 200)}`);
|
|
5158
|
+
}
|
|
5159
|
+
return await res.json();
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
requestInit() {
|
|
5163
|
+
return { headers: { "User-Agent": "defi-cli/0.5", "Accept": "application/json" } };
|
|
5164
|
+
}
|
|
5165
|
+
};
|
|
4312
5166
|
POOL_ABI = parseAbi14([
|
|
4313
5167
|
"function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external",
|
|
4314
5168
|
"function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf) external",
|
|
@@ -4610,8 +5464,8 @@ var init_dist2 = __esm({
|
|
|
4610
5464
|
throw DefiError.rpcError(`[${this.protocolName}] getUserAccountData failed: ${e}`);
|
|
4611
5465
|
});
|
|
4612
5466
|
const [totalCollateralBase, totalDebtBase, , , ltv, healthFactor] = result;
|
|
4613
|
-
const
|
|
4614
|
-
const hf = healthFactor >=
|
|
5467
|
+
const MAX_UINT2562 = 2n ** 256n - 1n;
|
|
5468
|
+
const hf = healthFactor >= MAX_UINT2562 ? Infinity : Number(healthFactor) / 1e18;
|
|
4615
5469
|
const collateralUsd = u256ToF64(totalCollateralBase) / 1e8;
|
|
4616
5470
|
const debtUsd = u256ToF64(totalDebtBase) / 1e8;
|
|
4617
5471
|
const ltvBps = u256ToF64(ltv);
|
|
@@ -4775,8 +5629,8 @@ var init_dist2 = __esm({
|
|
|
4775
5629
|
throw DefiError.rpcError(`[${this.protocolName}] getUserAccountData failed: ${e}`);
|
|
4776
5630
|
});
|
|
4777
5631
|
const [totalCollateralBase, totalDebtBase, , , ltv, healthFactor] = result;
|
|
4778
|
-
const
|
|
4779
|
-
const hf = healthFactor >=
|
|
5632
|
+
const MAX_UINT2562 = 2n ** 256n - 1n;
|
|
5633
|
+
const hf = healthFactor >= MAX_UINT2562 ? Infinity : Number(healthFactor) / 1e18;
|
|
4780
5634
|
const collateralUsd = u256ToF642(totalCollateralBase) / 1e18;
|
|
4781
5635
|
const debtUsd = u256ToF642(totalDebtBase) / 1e18;
|
|
4782
5636
|
const ltvBps = u256ToF642(ltv);
|
|
@@ -5023,7 +5877,8 @@ var init_dist2 = __esm({
|
|
|
5023
5877
|
to: this.comet,
|
|
5024
5878
|
data,
|
|
5025
5879
|
value: 0n,
|
|
5026
|
-
gas_estimate: 3e5
|
|
5880
|
+
gas_estimate: 3e5,
|
|
5881
|
+
approvals: [{ token: params.asset, spender: this.comet, amount: params.amount }]
|
|
5027
5882
|
};
|
|
5028
5883
|
}
|
|
5029
5884
|
async buildBorrow(params) {
|
|
@@ -5051,7 +5906,8 @@ var init_dist2 = __esm({
|
|
|
5051
5906
|
to: this.comet,
|
|
5052
5907
|
data,
|
|
5053
5908
|
value: 0n,
|
|
5054
|
-
gas_estimate: 3e5
|
|
5909
|
+
gas_estimate: 3e5,
|
|
5910
|
+
approvals: [{ token: params.asset, spender: this.comet, amount: params.amount }]
|
|
5055
5911
|
};
|
|
5056
5912
|
}
|
|
5057
5913
|
async buildWithdraw(params) {
|
|
@@ -5240,6 +6096,14 @@ var init_dist2 = __esm({
|
|
|
5240
6096
|
"function totalAssets() external view returns (uint256)",
|
|
5241
6097
|
"function totalSupply() external view returns (uint256)"
|
|
5242
6098
|
]);
|
|
6099
|
+
ERC4626_ABI = parseAbi20([
|
|
6100
|
+
"function asset() external view returns (address)",
|
|
6101
|
+
"function deposit(uint256 assets, address receiver) external returns (uint256 shares)",
|
|
6102
|
+
"function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets)",
|
|
6103
|
+
"function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares)",
|
|
6104
|
+
"function balanceOf(address owner) external view returns (uint256)"
|
|
6105
|
+
]);
|
|
6106
|
+
MAX_UINT256 = (1n << 256n) - 1n;
|
|
5243
6107
|
IRM_ABI = parseAbi20([
|
|
5244
6108
|
"function borrowRateView((address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee) market) external view returns (uint256)"
|
|
5245
6109
|
]);
|
|
@@ -5249,6 +6113,9 @@ var init_dist2 = __esm({
|
|
|
5249
6113
|
morpho;
|
|
5250
6114
|
defaultVault;
|
|
5251
6115
|
rpcUrl;
|
|
6116
|
+
metaMorphoVaults;
|
|
6117
|
+
metaMorphoVaultEntries;
|
|
6118
|
+
vaultAssetMap = null;
|
|
5252
6119
|
constructor(entry, rpcUrl) {
|
|
5253
6120
|
this.protocolName = entry.name;
|
|
5254
6121
|
this.rpcUrl = rpcUrl;
|
|
@@ -5257,11 +6124,58 @@ var init_dist2 = __esm({
|
|
|
5257
6124
|
if (!morpho) throw DefiError.contractError("Missing 'morpho_blue' contract address");
|
|
5258
6125
|
this.morpho = morpho;
|
|
5259
6126
|
this.defaultVault = contracts["fehype"] ?? contracts["vault"] ?? contracts["feusdc"];
|
|
6127
|
+
this.metaMorphoVaultEntries = Object.entries(contracts).filter(([key]) => /^fe[a-z0-9_]+$/i.test(key) || key === "vault").map(([key, addr]) => ({ key, addr }));
|
|
6128
|
+
this.metaMorphoVaults = this.metaMorphoVaultEntries.map((e) => e.addr);
|
|
6129
|
+
}
|
|
6130
|
+
async resolveVault(asset, preferKey) {
|
|
6131
|
+
if (this.metaMorphoVaultEntries.length === 0 || !this.rpcUrl) return null;
|
|
6132
|
+
if (preferKey) {
|
|
6133
|
+
const direct = this.metaMorphoVaultEntries.find((e) => e.key === preferKey);
|
|
6134
|
+
if (direct) return direct.addr;
|
|
6135
|
+
}
|
|
6136
|
+
if (!this.vaultAssetMap) {
|
|
6137
|
+
const calls = this.metaMorphoVaultEntries.map((e) => [
|
|
6138
|
+
e.addr,
|
|
6139
|
+
encodeFunctionData19({ abi: ERC4626_ABI, functionName: "asset" })
|
|
6140
|
+
]);
|
|
6141
|
+
const results = await multicallRead(this.rpcUrl, calls).catch(() => []);
|
|
6142
|
+
const map = /* @__PURE__ */ new Map();
|
|
6143
|
+
for (let i = 0; i < results.length; i++) {
|
|
6144
|
+
const data = results[i];
|
|
6145
|
+
if (!data || data.length < 66) continue;
|
|
6146
|
+
const a = `0x${data.slice(26, 66)}`.toLowerCase();
|
|
6147
|
+
const entry = this.metaMorphoVaultEntries[i];
|
|
6148
|
+
const existing = map.get(a);
|
|
6149
|
+
if (!existing || entry.key.length < existing.key.length) {
|
|
6150
|
+
map.set(a, entry);
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
const flatMap = /* @__PURE__ */ new Map();
|
|
6154
|
+
for (const [k, v] of map) flatMap.set(k, v.addr);
|
|
6155
|
+
this.vaultAssetMap = flatMap;
|
|
6156
|
+
}
|
|
6157
|
+
return this.vaultAssetMap.get(asset.toLowerCase()) ?? null;
|
|
5260
6158
|
}
|
|
5261
6159
|
name() {
|
|
5262
6160
|
return this.protocolName;
|
|
5263
6161
|
}
|
|
5264
6162
|
async buildSupply(params) {
|
|
6163
|
+
const vault = await this.resolveVault(params.asset);
|
|
6164
|
+
if (vault) {
|
|
6165
|
+
const data2 = encodeFunctionData19({
|
|
6166
|
+
abi: ERC4626_ABI,
|
|
6167
|
+
functionName: "deposit",
|
|
6168
|
+
args: [params.amount, params.on_behalf_of]
|
|
6169
|
+
});
|
|
6170
|
+
return {
|
|
6171
|
+
description: `[${this.protocolName}] Deposit ${params.amount} into MetaMorpho vault`,
|
|
6172
|
+
to: vault,
|
|
6173
|
+
data: data2,
|
|
6174
|
+
value: 0n,
|
|
6175
|
+
gas_estimate: 4e5,
|
|
6176
|
+
approvals: [{ token: params.asset, spender: vault, amount: params.amount }]
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
5265
6179
|
const market = defaultMarketParams(params.asset);
|
|
5266
6180
|
const data = encodeFunctionData19({
|
|
5267
6181
|
abi: MORPHO_ABI,
|
|
@@ -5307,6 +6221,40 @@ var init_dist2 = __esm({
|
|
|
5307
6221
|
};
|
|
5308
6222
|
}
|
|
5309
6223
|
async buildWithdraw(params) {
|
|
6224
|
+
const vault = await this.resolveVault(params.asset);
|
|
6225
|
+
if (vault) {
|
|
6226
|
+
if (params.amount === MAX_UINT256) {
|
|
6227
|
+
if (!this.rpcUrl) throw DefiError.rpcError("RPC required to fetch vault shares");
|
|
6228
|
+
const [balRaw] = await multicallRead(this.rpcUrl, [
|
|
6229
|
+
[vault, encodeFunctionData19({ abi: ERC4626_ABI, functionName: "balanceOf", args: [params.to] })]
|
|
6230
|
+
]);
|
|
6231
|
+
const shares = decodeU256(balRaw ?? null);
|
|
6232
|
+
const data3 = encodeFunctionData19({
|
|
6233
|
+
abi: ERC4626_ABI,
|
|
6234
|
+
functionName: "redeem",
|
|
6235
|
+
args: [shares, params.to, params.to]
|
|
6236
|
+
});
|
|
6237
|
+
return {
|
|
6238
|
+
description: `[${this.protocolName}] Redeem all shares (${shares}) from MetaMorpho vault`,
|
|
6239
|
+
to: vault,
|
|
6240
|
+
data: data3,
|
|
6241
|
+
value: 0n,
|
|
6242
|
+
gas_estimate: 4e5
|
|
6243
|
+
};
|
|
6244
|
+
}
|
|
6245
|
+
const data2 = encodeFunctionData19({
|
|
6246
|
+
abi: ERC4626_ABI,
|
|
6247
|
+
functionName: "withdraw",
|
|
6248
|
+
args: [params.amount, params.to, params.to]
|
|
6249
|
+
});
|
|
6250
|
+
return {
|
|
6251
|
+
description: `[${this.protocolName}] Withdraw ${params.amount} assets from MetaMorpho vault`,
|
|
6252
|
+
to: vault,
|
|
6253
|
+
data: data2,
|
|
6254
|
+
value: 0n,
|
|
6255
|
+
gas_estimate: 4e5
|
|
6256
|
+
};
|
|
6257
|
+
}
|
|
5310
6258
|
const market = defaultMarketParams(params.asset);
|
|
5311
6259
|
const data = encodeFunctionData19({
|
|
5312
6260
|
abi: MORPHO_ABI,
|
|
@@ -5324,7 +6272,7 @@ var init_dist2 = __esm({
|
|
|
5324
6272
|
async getRates(asset) {
|
|
5325
6273
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
5326
6274
|
if (!this.defaultVault) {
|
|
5327
|
-
|
|
6275
|
+
return { protocol: this.protocolName, asset, supply_apy: 0, borrow_variable_apy: 0, borrow_stable_apy: 0, utilization: 0, total_supply: 0n, total_borrow: 0n };
|
|
5328
6276
|
}
|
|
5329
6277
|
const [queueLenRaw] = await multicallRead(this.rpcUrl, [
|
|
5330
6278
|
[this.defaultVault, encodeFunctionData19({ abi: META_MORPHO_ABI, functionName: "supplyQueueLength" })]
|
|
@@ -5625,7 +6573,7 @@ var init_dist2 = __esm({
|
|
|
5625
6573
|
return results;
|
|
5626
6574
|
}
|
|
5627
6575
|
};
|
|
5628
|
-
|
|
6576
|
+
ERC4626_ABI2 = parseAbi23([
|
|
5629
6577
|
"function asset() external view returns (address)",
|
|
5630
6578
|
"function totalAssets() external view returns (uint256)",
|
|
5631
6579
|
"function totalSupply() external view returns (uint256)",
|
|
@@ -5650,7 +6598,7 @@ var init_dist2 = __esm({
|
|
|
5650
6598
|
}
|
|
5651
6599
|
async buildDeposit(assets, receiver) {
|
|
5652
6600
|
const data = encodeFunctionData21({
|
|
5653
|
-
abi:
|
|
6601
|
+
abi: ERC4626_ABI2,
|
|
5654
6602
|
functionName: "deposit",
|
|
5655
6603
|
args: [assets, receiver]
|
|
5656
6604
|
});
|
|
@@ -5664,7 +6612,7 @@ var init_dist2 = __esm({
|
|
|
5664
6612
|
}
|
|
5665
6613
|
async buildWithdraw(assets, receiver, owner) {
|
|
5666
6614
|
const data = encodeFunctionData21({
|
|
5667
|
-
abi:
|
|
6615
|
+
abi: ERC4626_ABI2,
|
|
5668
6616
|
functionName: "withdraw",
|
|
5669
6617
|
args: [assets, receiver, owner]
|
|
5670
6618
|
});
|
|
@@ -5681,7 +6629,7 @@ var init_dist2 = __esm({
|
|
|
5681
6629
|
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
5682
6630
|
return client.readContract({
|
|
5683
6631
|
address: this.vaultAddress,
|
|
5684
|
-
abi:
|
|
6632
|
+
abi: ERC4626_ABI2,
|
|
5685
6633
|
functionName: "totalAssets"
|
|
5686
6634
|
}).catch((e) => {
|
|
5687
6635
|
throw DefiError.rpcError(`[${this.protocolName}] totalAssets failed: ${e}`);
|
|
@@ -5692,7 +6640,7 @@ var init_dist2 = __esm({
|
|
|
5692
6640
|
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
5693
6641
|
return client.readContract({
|
|
5694
6642
|
address: this.vaultAddress,
|
|
5695
|
-
abi:
|
|
6643
|
+
abi: ERC4626_ABI2,
|
|
5696
6644
|
functionName: "convertToShares",
|
|
5697
6645
|
args: [assets]
|
|
5698
6646
|
}).catch((e) => {
|
|
@@ -5704,7 +6652,7 @@ var init_dist2 = __esm({
|
|
|
5704
6652
|
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
5705
6653
|
return client.readContract({
|
|
5706
6654
|
address: this.vaultAddress,
|
|
5707
|
-
abi:
|
|
6655
|
+
abi: ERC4626_ABI2,
|
|
5708
6656
|
functionName: "convertToAssets",
|
|
5709
6657
|
args: [shares]
|
|
5710
6658
|
}).catch((e) => {
|
|
@@ -5715,13 +6663,13 @@ var init_dist2 = __esm({
|
|
|
5715
6663
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
5716
6664
|
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
5717
6665
|
const [totalAssets, totalSupply, asset] = await Promise.all([
|
|
5718
|
-
client.readContract({ address: this.vaultAddress, abi:
|
|
6666
|
+
client.readContract({ address: this.vaultAddress, abi: ERC4626_ABI2, functionName: "totalAssets" }).catch((e) => {
|
|
5719
6667
|
throw DefiError.rpcError(`[${this.protocolName}] totalAssets failed: ${e}`);
|
|
5720
6668
|
}),
|
|
5721
|
-
client.readContract({ address: this.vaultAddress, abi:
|
|
6669
|
+
client.readContract({ address: this.vaultAddress, abi: ERC4626_ABI2, functionName: "totalSupply" }).catch((e) => {
|
|
5722
6670
|
throw DefiError.rpcError(`[${this.protocolName}] totalSupply failed: ${e}`);
|
|
5723
6671
|
}),
|
|
5724
|
-
client.readContract({ address: this.vaultAddress, abi:
|
|
6672
|
+
client.readContract({ address: this.vaultAddress, abi: ERC4626_ABI2, functionName: "asset" }).catch((e) => {
|
|
5725
6673
|
throw DefiError.rpcError(`[${this.protocolName}] asset failed: ${e}`);
|
|
5726
6674
|
})
|
|
5727
6675
|
]);
|
|
@@ -6818,10 +7766,11 @@ server.tool(
|
|
|
6818
7766
|
token_a: z.string().describe("First token symbol or address"),
|
|
6819
7767
|
token_b: z.string().describe("Second token symbol or address"),
|
|
6820
7768
|
liquidity: z.string().describe("Liquidity amount to remove in wei"),
|
|
7769
|
+
token_id: z.string().optional().describe("NFT tokenId \u2014 required for V3/CL position-manager based protocols (uniswap_v3, algebra_v3, hybra)"),
|
|
6821
7770
|
recipient: z.string().optional().describe("Recipient address for returned tokens"),
|
|
6822
7771
|
broadcast: z.boolean().optional().describe("Set true to broadcast (default: false)")
|
|
6823
7772
|
},
|
|
6824
|
-
async ({ chain, protocol, token_a, token_b, liquidity, recipient, broadcast }) => {
|
|
7773
|
+
async ({ chain, protocol, token_a, token_b, liquidity, token_id, recipient, broadcast }) => {
|
|
6825
7774
|
try {
|
|
6826
7775
|
const chainName = chain ?? "hyperevm";
|
|
6827
7776
|
const registry = getRegistry();
|
|
@@ -6836,7 +7785,8 @@ server.tool(
|
|
|
6836
7785
|
token_a: tokenA,
|
|
6837
7786
|
token_b: tokenB,
|
|
6838
7787
|
liquidity: BigInt(liquidity),
|
|
6839
|
-
recipient: recipientAddr
|
|
7788
|
+
recipient: recipientAddr,
|
|
7789
|
+
token_id: token_id ? BigInt(token_id) : void 0
|
|
6840
7790
|
});
|
|
6841
7791
|
const executor = makeExecutor(broadcast ?? false, chainConfig.effectiveRpcUrl(), chainConfig.explorer_url);
|
|
6842
7792
|
const result = await executor.execute(tx);
|
|
@@ -7469,6 +8419,52 @@ server.tool(
|
|
|
7469
8419
|
}
|
|
7470
8420
|
}
|
|
7471
8421
|
);
|
|
8422
|
+
server.tool(
|
|
8423
|
+
"defi_yield_scan",
|
|
8424
|
+
"Scan all configured chains for the best lending yield opportunities for a given asset. Compares supply APY across Aave V3, Compound, Venus, Morpho, etc. Returns ranked results with chain, protocol, supply_apy, borrow_apy, utilization.",
|
|
8425
|
+
{
|
|
8426
|
+
asset: z.string().optional().describe("Token symbol to scan (default: USDC)")
|
|
8427
|
+
},
|
|
8428
|
+
async ({ asset }) => {
|
|
8429
|
+
const registry = Registry.loadEmbedded();
|
|
8430
|
+
const symbol = asset ?? "USDC";
|
|
8431
|
+
const results = [];
|
|
8432
|
+
const chains = Array.from(registry.chains.keys());
|
|
8433
|
+
await Promise.allSettled(
|
|
8434
|
+
chains.map(async (chainName) => {
|
|
8435
|
+
const chain = registry.getChain(chainName);
|
|
8436
|
+
const rpcUrl = chain.effectiveRpcUrl();
|
|
8437
|
+
const lendingProtos = registry.getProtocolsForChain(chainName).filter((p) => p.category === "lending");
|
|
8438
|
+
for (const proto of lendingProtos) {
|
|
8439
|
+
try {
|
|
8440
|
+
const adapter = createLending(proto, rpcUrl);
|
|
8441
|
+
const tokens = registry.tokens.get(chainName);
|
|
8442
|
+
const token = tokens?.find((t) => t.symbol.toUpperCase() === symbol.toUpperCase());
|
|
8443
|
+
if (!token) continue;
|
|
8444
|
+
const rates = await adapter.getRates(token.address);
|
|
8445
|
+
results.push({
|
|
8446
|
+
chain: chainName,
|
|
8447
|
+
protocol: proto.name,
|
|
8448
|
+
slug: proto.slug,
|
|
8449
|
+
asset: token.symbol,
|
|
8450
|
+
asset_address: token.address,
|
|
8451
|
+
supply_apy: rates.supply_apy,
|
|
8452
|
+
borrow_variable_apy: rates.borrow_variable_apy,
|
|
8453
|
+
utilization: rates.utilization,
|
|
8454
|
+
total_supply: rates.total_supply?.toString(),
|
|
8455
|
+
total_borrow: rates.total_borrow?.toString()
|
|
8456
|
+
});
|
|
8457
|
+
} catch {
|
|
8458
|
+
}
|
|
8459
|
+
}
|
|
8460
|
+
})
|
|
8461
|
+
);
|
|
8462
|
+
results.sort((a, b) => b.supply_apy - a.supply_apy);
|
|
8463
|
+
return {
|
|
8464
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
|
|
8465
|
+
};
|
|
8466
|
+
}
|
|
8467
|
+
);
|
|
7472
8468
|
var transport = new StdioServerTransport();
|
|
7473
8469
|
await server.connect(transport);
|
|
7474
8470
|
//# sourceMappingURL=mcp-server.js.map
|