@hypurrquant/defi-cli 0.5.0 → 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 +1489 -127
- package/dist/index.js.map +1 -1
- package/dist/main.js +1490 -128
- package/dist/main.js.map +1 -1
- package/dist/mcp-server.js +917 -85
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- 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;
|
|
@@ -438,7 +439,7 @@ var init_dist = __esm({
|
|
|
438
439
|
}
|
|
439
440
|
getProtocolsForChain(chain, includeUnverified = false) {
|
|
440
441
|
return this.protocols.filter(
|
|
441
|
-
(p) => p.chain.toLowerCase() === chain.toLowerCase() && (includeUnverified || p.verified !== false)
|
|
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) {
|
|
@@ -1006,6 +1010,46 @@ function createOracleFromCdp(entry, _asset, rpcUrl) {
|
|
|
1006
1010
|
function createMerchantMoeLB(entry, rpcUrl) {
|
|
1007
1011
|
return new MerchantMoeLBAdapter(entry, rpcUrl);
|
|
1008
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
|
+
}
|
|
1009
1053
|
function createKittenSwapFarming(entry, rpcUrl) {
|
|
1010
1054
|
const farmingCenter = entry.contracts?.["farming_center"];
|
|
1011
1055
|
if (!farmingCenter) {
|
|
@@ -1020,9 +1064,11 @@ function createKittenSwapFarming(entry, rpcUrl) {
|
|
|
1020
1064
|
throw new DefiError("CONTRACT_ERROR", `[${entry.name}] Missing 'position_manager' contract address`);
|
|
1021
1065
|
}
|
|
1022
1066
|
const factory = entry.contracts?.["factory"];
|
|
1023
|
-
|
|
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);
|
|
1024
1070
|
}
|
|
1025
|
-
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;
|
|
1026
1072
|
var init_dist2 = __esm({
|
|
1027
1073
|
"../defi-protocols/dist/index.js"() {
|
|
1028
1074
|
"use strict";
|
|
@@ -1060,6 +1106,7 @@ var init_dist2 = __esm({
|
|
|
1060
1106
|
init_dist();
|
|
1061
1107
|
init_dist();
|
|
1062
1108
|
init_dist();
|
|
1109
|
+
init_dist();
|
|
1063
1110
|
DEFAULT_FEE = 3e3;
|
|
1064
1111
|
swapRouterAbi = parseAbi3([
|
|
1065
1112
|
"struct ExactInputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; }",
|
|
@@ -1075,7 +1122,19 @@ var init_dist2 = __esm({
|
|
|
1075
1122
|
]);
|
|
1076
1123
|
positionManagerAbi = parseAbi3([
|
|
1077
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; }",
|
|
1078
|
-
"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)"
|
|
1079
1138
|
]);
|
|
1080
1139
|
UniswapV3Adapter = class {
|
|
1081
1140
|
protocolName;
|
|
@@ -1098,7 +1157,7 @@ var init_dist2 = __esm({
|
|
|
1098
1157
|
this.factory = entry.contracts?.["factory"];
|
|
1099
1158
|
this.fee = DEFAULT_FEE;
|
|
1100
1159
|
this.rpcUrl = rpcUrl;
|
|
1101
|
-
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;
|
|
1102
1161
|
}
|
|
1103
1162
|
name() {
|
|
1104
1163
|
return this.protocolName;
|
|
@@ -1274,16 +1333,64 @@ var init_dist2 = __esm({
|
|
|
1274
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];
|
|
1275
1334
|
const amount0 = rawAmount0 === 0n && rawAmount1 > 0n ? 1n : rawAmount0;
|
|
1276
1335
|
const amount1 = rawAmount1 === 0n && rawAmount0 > 0n ? 1n : rawAmount1;
|
|
1277
|
-
|
|
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({
|
|
1278
1385
|
abi: positionManagerAbi,
|
|
1279
1386
|
functionName: "mint",
|
|
1280
1387
|
args: [
|
|
1281
1388
|
{
|
|
1282
1389
|
token0,
|
|
1283
1390
|
token1,
|
|
1284
|
-
fee:
|
|
1285
|
-
tickLower
|
|
1286
|
-
tickUpper
|
|
1391
|
+
fee: thirdField,
|
|
1392
|
+
tickLower,
|
|
1393
|
+
tickUpper,
|
|
1287
1394
|
amount0Desired: amount0,
|
|
1288
1395
|
amount1Desired: amount1,
|
|
1289
1396
|
amount0Min: 0n,
|
|
@@ -1305,10 +1412,125 @@ var init_dist2 = __esm({
|
|
|
1305
1412
|
]
|
|
1306
1413
|
};
|
|
1307
1414
|
}
|
|
1308
|
-
async buildRemoveLiquidity(
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
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
|
+
};
|
|
1312
1534
|
}
|
|
1313
1535
|
};
|
|
1314
1536
|
abi = parseAbi22([
|
|
@@ -1529,6 +1751,13 @@ var init_dist2 = __esm({
|
|
|
1529
1751
|
"struct MintParams { address token0; address token1; int24 tickLower; int24 tickUpper; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; uint256 deadline; }",
|
|
1530
1752
|
"function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)"
|
|
1531
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
|
+
]);
|
|
1532
1761
|
AlgebraV3Adapter = class {
|
|
1533
1762
|
protocolName;
|
|
1534
1763
|
router;
|
|
@@ -1724,10 +1953,34 @@ var init_dist2 = __esm({
|
|
|
1724
1953
|
approvals
|
|
1725
1954
|
};
|
|
1726
1955
|
}
|
|
1727
|
-
async buildRemoveLiquidity(
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
);
|
|
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
|
+
};
|
|
1731
1984
|
}
|
|
1732
1985
|
};
|
|
1733
1986
|
abi3 = parseAbi4([
|
|
@@ -2015,7 +2268,12 @@ var init_dist2 = __esm({
|
|
|
2015
2268
|
};
|
|
2016
2269
|
thenaPmAbi = parseAbi7([
|
|
2017
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; }",
|
|
2018
|
-
"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)"
|
|
2019
2277
|
]);
|
|
2020
2278
|
thenaRouterAbi = parseAbi7([
|
|
2021
2279
|
"struct ExactInputSingleParams { address tokenIn; address tokenOut; int24 tickSpacing; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; }",
|
|
@@ -2158,8 +2416,34 @@ var init_dist2 = __esm({
|
|
|
2158
2416
|
approvals
|
|
2159
2417
|
};
|
|
2160
2418
|
}
|
|
2161
|
-
async buildRemoveLiquidity(
|
|
2162
|
-
|
|
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
|
+
};
|
|
2163
2447
|
}
|
|
2164
2448
|
};
|
|
2165
2449
|
_addressDecodeAbi = parseAbi8(["function f() external view returns (address)"]);
|
|
@@ -2338,12 +2622,14 @@ var init_dist2 = __esm({
|
|
|
2338
2622
|
pre_txs: [approveTx]
|
|
2339
2623
|
};
|
|
2340
2624
|
}
|
|
2341
|
-
async buildWithdraw(gauge, _amount, tokenId) {
|
|
2625
|
+
async buildWithdraw(gauge, _amount, tokenId, opts) {
|
|
2342
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)." : "";
|
|
2343
2629
|
return {
|
|
2344
|
-
description: `[${this.protocolName}] Withdraw NFT #${tokenId} from gauge`,
|
|
2630
|
+
description: `[${this.protocolName}] Withdraw NFT #${tokenId} from gauge (redeemType=${redeemType})${warning}`,
|
|
2345
2631
|
to: gauge,
|
|
2346
|
-
data: encodeFunctionData8({ abi: gaugeCLAbi, functionName: "withdraw", args: [tokenId,
|
|
2632
|
+
data: encodeFunctionData8({ abi: gaugeCLAbi, functionName: "withdraw", args: [tokenId, redeemType] }),
|
|
2347
2633
|
value: 0n,
|
|
2348
2634
|
gas_estimate: 1e6
|
|
2349
2635
|
};
|
|
@@ -2352,15 +2638,15 @@ var init_dist2 = __esm({
|
|
|
2352
2638
|
async buildClaimRewards(gauge, _account) {
|
|
2353
2639
|
throw DefiError.unsupported(`[${this.protocolName}] Use buildClaimRewardsByTokenId for CL gauges`);
|
|
2354
2640
|
}
|
|
2355
|
-
async buildClaimRewardsByTokenId(gauge, tokenId) {
|
|
2641
|
+
async buildClaimRewardsByTokenId(gauge, tokenId, opts) {
|
|
2642
|
+
const redeemType = opts?.redeemType ?? 1;
|
|
2356
2643
|
return {
|
|
2357
|
-
description: `[${this.protocolName}] Claim rewards for NFT #${tokenId}`,
|
|
2644
|
+
description: `[${this.protocolName}] Claim rewards for NFT #${tokenId} (redeemType=${redeemType})`,
|
|
2358
2645
|
to: this.gaugeManager,
|
|
2359
2646
|
data: encodeFunctionData8({
|
|
2360
2647
|
abi: gaugeManagerAbi,
|
|
2361
2648
|
functionName: "claimRewards",
|
|
2362
|
-
args: [gauge, [tokenId],
|
|
2363
|
-
// redeemType=1
|
|
2649
|
+
args: [gauge, [tokenId], redeemType]
|
|
2364
2650
|
}),
|
|
2365
2651
|
value: 0n,
|
|
2366
2652
|
gas_estimate: 1e6
|
|
@@ -2505,15 +2791,21 @@ var init_dist2 = __esm({
|
|
|
2505
2791
|
"function getReward(address account) external",
|
|
2506
2792
|
"function getReward(address account, address[] tokens) external",
|
|
2507
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",
|
|
2508
2796
|
"function earned(address account) external view returns (uint256)",
|
|
2509
2797
|
"function earned(address account, uint256 tokenId) external view returns (uint256)",
|
|
2510
2798
|
"function earned(address token, address account) external view returns (uint256)",
|
|
2511
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)",
|
|
2512
2802
|
"function rewardRate() external view returns (uint256)",
|
|
2513
2803
|
"function rewardToken() external view returns (address)",
|
|
2514
2804
|
"function totalSupply() external view returns (uint256)",
|
|
2515
2805
|
"function rewardsListLength() external view returns (uint256)",
|
|
2516
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[])",
|
|
2517
2809
|
"function nonfungiblePositionManager() external view returns (address)"
|
|
2518
2810
|
]);
|
|
2519
2811
|
veAbi2 = parseAbi10([
|
|
@@ -2558,6 +2850,10 @@ var init_dist2 = __esm({
|
|
|
2558
2850
|
clFactory;
|
|
2559
2851
|
v2Factory;
|
|
2560
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;
|
|
2561
2857
|
constructor(entry, rpcUrl, tokens) {
|
|
2562
2858
|
this.protocolName = entry.name;
|
|
2563
2859
|
const voter = entry.contracts?.["voter"];
|
|
@@ -2574,6 +2870,8 @@ var init_dist2 = __esm({
|
|
|
2574
2870
|
this.tokens = tokens;
|
|
2575
2871
|
this.clFactory = entry.contracts?.["cl_factory"] ?? entry.contracts?.["factory"];
|
|
2576
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;
|
|
2577
2875
|
}
|
|
2578
2876
|
name() {
|
|
2579
2877
|
return this.protocolName;
|
|
@@ -2908,6 +3206,25 @@ var init_dist2 = __esm({
|
|
|
2908
3206
|
}
|
|
2909
3207
|
// IGauge
|
|
2910
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
|
+
}
|
|
2911
3228
|
if (tokenId !== void 0) {
|
|
2912
3229
|
const data2 = encodeFunctionData10({
|
|
2913
3230
|
abi: gaugeAbi,
|
|
@@ -2937,7 +3254,17 @@ var init_dist2 = __esm({
|
|
|
2937
3254
|
approvals: lpToken ? [{ token: lpToken, spender: gauge, amount }] : void 0
|
|
2938
3255
|
};
|
|
2939
3256
|
}
|
|
2940
|
-
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
|
+
}
|
|
2941
3268
|
const data = encodeFunctionData10({
|
|
2942
3269
|
abi: gaugeAbi,
|
|
2943
3270
|
functionName: "withdraw",
|
|
@@ -3027,16 +3354,16 @@ var init_dist2 = __esm({
|
|
|
3027
3354
|
}
|
|
3028
3355
|
async buildClaimRewards(gauge, account) {
|
|
3029
3356
|
if (!this.rpcUrl || !account) {
|
|
3030
|
-
const
|
|
3357
|
+
const data = encodeFunctionData10({
|
|
3031
3358
|
abi: gaugeAbi,
|
|
3032
3359
|
functionName: "getReward",
|
|
3033
3360
|
args: [account ?? zeroAddress6]
|
|
3034
3361
|
});
|
|
3035
|
-
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 };
|
|
3036
3363
|
}
|
|
3037
3364
|
const { tokens, multiToken } = await this.discoverRewardTokens(gauge);
|
|
3038
3365
|
if (multiToken && tokens.length > 0) {
|
|
3039
|
-
const
|
|
3366
|
+
const data = encodeFunctionData10({
|
|
3040
3367
|
abi: gaugeAbi,
|
|
3041
3368
|
functionName: "getReward",
|
|
3042
3369
|
args: [account, tokens]
|
|
@@ -3044,41 +3371,179 @@ var init_dist2 = __esm({
|
|
|
3044
3371
|
return {
|
|
3045
3372
|
description: `[${this.protocolName}] Claim gauge rewards (${tokens.length} tokens)`,
|
|
3046
3373
|
to: gauge,
|
|
3047
|
-
data
|
|
3374
|
+
data,
|
|
3048
3375
|
value: 0n,
|
|
3049
3376
|
gas_estimate: 3e5
|
|
3050
3377
|
};
|
|
3051
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) {
|
|
3052
3409
|
const data = encodeFunctionData10({
|
|
3053
3410
|
abi: gaugeAbi,
|
|
3054
3411
|
functionName: "getReward",
|
|
3055
|
-
args: []
|
|
3412
|
+
args: [tokenId]
|
|
3056
3413
|
});
|
|
3057
3414
|
return {
|
|
3058
|
-
description: `[${this.protocolName}] Claim gauge rewards`,
|
|
3415
|
+
description: `[${this.protocolName}] Claim gauge rewards for NFT #${tokenId}`,
|
|
3059
3416
|
to: gauge,
|
|
3060
3417
|
data,
|
|
3061
3418
|
value: 0n,
|
|
3062
|
-
gas_estimate:
|
|
3419
|
+
gas_estimate: 3e5
|
|
3063
3420
|
};
|
|
3064
3421
|
}
|
|
3065
3422
|
/**
|
|
3066
|
-
*
|
|
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.
|
|
3067
3434
|
*/
|
|
3068
|
-
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
|
+
}
|
|
3069
3502
|
const data = encodeFunctionData10({
|
|
3070
3503
|
abi: gaugeAbi,
|
|
3071
3504
|
functionName: "getReward",
|
|
3072
|
-
args: [tokenId]
|
|
3505
|
+
args: [tokenId, rewardTokens]
|
|
3073
3506
|
});
|
|
3074
3507
|
return {
|
|
3075
|
-
description: `[${this.protocolName}] Claim gauge rewards for NFT #${tokenId}`,
|
|
3508
|
+
description: `[${this.protocolName}] Claim CL gauge rewards for NFT #${tokenId} (${rewardTokens.length} tokens)`,
|
|
3076
3509
|
to: gauge,
|
|
3077
3510
|
data,
|
|
3078
3511
|
value: 0n,
|
|
3079
|
-
gas_estimate:
|
|
3512
|
+
gas_estimate: 4e5
|
|
3080
3513
|
};
|
|
3081
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
|
+
}
|
|
3082
3547
|
async getPendingRewards(gauge, user) {
|
|
3083
3548
|
if (!this.rpcUrl) throw DefiError.rpcError("RPC URL required");
|
|
3084
3549
|
const client = createPublicClient6({ transport: http6(this.rpcUrl) });
|
|
@@ -3645,9 +4110,44 @@ var init_dist2 = __esm({
|
|
|
3645
4110
|
}
|
|
3646
4111
|
];
|
|
3647
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
|
+
}
|
|
3648
4147
|
/**
|
|
3649
4148
|
* Build a claim rewards transaction for specific LB bins.
|
|
3650
|
-
* 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.
|
|
3651
4151
|
*/
|
|
3652
4152
|
async buildClaimRewards(user, pool, binIds) {
|
|
3653
4153
|
const rpcUrl = this.requireRpc();
|
|
@@ -3663,15 +4163,18 @@ var init_dist2 = __esm({
|
|
|
3663
4163
|
}
|
|
3664
4164
|
let resolvedBinIds = binIds;
|
|
3665
4165
|
if (!resolvedBinIds || resolvedBinIds.length === 0) {
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
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
|
+
}
|
|
3675
4178
|
}
|
|
3676
4179
|
const data = encodeFunctionData12({
|
|
3677
4180
|
abi: lbRewarderAbi,
|
|
@@ -4082,7 +4585,7 @@ var init_dist2 = __esm({
|
|
|
4082
4585
|
"function enterFarming((address rewardToken, address bonusRewardToken, address pool, uint256 nonce) key, uint256 tokenId) external",
|
|
4083
4586
|
"function exitFarming((address rewardToken, address bonusRewardToken, address pool, uint256 nonce) key, uint256 tokenId) external",
|
|
4084
4587
|
"function collectRewards((address rewardToken, address bonusRewardToken, address pool, uint256 nonce) key, uint256 tokenId) external",
|
|
4085
|
-
"function claimReward(address rewardToken, address to,
|
|
4588
|
+
"function claimReward(address rewardToken, address to, uint256 amountRequested) external returns (uint256 reward)"
|
|
4086
4589
|
]);
|
|
4087
4590
|
positionManagerAbi2 = parseAbi13([
|
|
4088
4591
|
"function approveForFarming(uint256 tokenId, bool approve, address farmingAddress) external",
|
|
@@ -4104,13 +4607,17 @@ var init_dist2 = __esm({
|
|
|
4104
4607
|
positionManager;
|
|
4105
4608
|
rpcUrl;
|
|
4106
4609
|
factory;
|
|
4107
|
-
|
|
4610
|
+
rewardToken;
|
|
4611
|
+
bonusRewardToken;
|
|
4612
|
+
constructor(protocolName, farmingCenter, eternalFarming, positionManager, rpcUrl, factory, rewardToken = KITTEN_TOKEN, bonusRewardToken = WHYPE_TOKEN) {
|
|
4108
4613
|
this.protocolName = protocolName;
|
|
4109
4614
|
this.farmingCenter = farmingCenter;
|
|
4110
4615
|
this.eternalFarming = eternalFarming;
|
|
4111
4616
|
this.positionManager = positionManager;
|
|
4112
4617
|
this.rpcUrl = rpcUrl;
|
|
4113
4618
|
this.factory = factory;
|
|
4619
|
+
this.rewardToken = rewardToken;
|
|
4620
|
+
this.bonusRewardToken = bonusRewardToken;
|
|
4114
4621
|
}
|
|
4115
4622
|
name() {
|
|
4116
4623
|
return this.protocolName;
|
|
@@ -4125,8 +4632,8 @@ var init_dist2 = __esm({
|
|
|
4125
4632
|
const poolLc = pool.toLowerCase();
|
|
4126
4633
|
if (nonceCache.has(poolLc)) {
|
|
4127
4634
|
return {
|
|
4128
|
-
rewardToken:
|
|
4129
|
-
bonusRewardToken:
|
|
4635
|
+
rewardToken: this.rewardToken,
|
|
4636
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4130
4637
|
pool,
|
|
4131
4638
|
nonce: nonceCache.get(poolLc)
|
|
4132
4639
|
};
|
|
@@ -4137,8 +4644,8 @@ var init_dist2 = __esm({
|
|
|
4137
4644
|
const nonce = BigInt(n);
|
|
4138
4645
|
nonces.push(nonce);
|
|
4139
4646
|
const key = {
|
|
4140
|
-
rewardToken:
|
|
4141
|
-
bonusRewardToken:
|
|
4647
|
+
rewardToken: this.rewardToken,
|
|
4648
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4142
4649
|
pool,
|
|
4143
4650
|
nonce
|
|
4144
4651
|
};
|
|
@@ -4173,8 +4680,8 @@ var init_dist2 = __esm({
|
|
|
4173
4680
|
const nonce = nonces[i];
|
|
4174
4681
|
nonceCache.set(poolLc, nonce);
|
|
4175
4682
|
return {
|
|
4176
|
-
rewardToken:
|
|
4177
|
-
bonusRewardToken:
|
|
4683
|
+
rewardToken: this.rewardToken,
|
|
4684
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4178
4685
|
pool,
|
|
4179
4686
|
nonce
|
|
4180
4687
|
};
|
|
@@ -4266,8 +4773,8 @@ var init_dist2 = __esm({
|
|
|
4266
4773
|
}
|
|
4267
4774
|
const calls = [
|
|
4268
4775
|
encodeCollectRewards(key, tokenId),
|
|
4269
|
-
encodeClaimReward(
|
|
4270
|
-
encodeClaimReward(
|
|
4776
|
+
encodeClaimReward(this.rewardToken, owner),
|
|
4777
|
+
encodeClaimReward(this.bonusRewardToken, owner)
|
|
4271
4778
|
];
|
|
4272
4779
|
return {
|
|
4273
4780
|
description: `[${this.protocolName}] Collect + claim rewards for NFT #${tokenId} in pool ${pool}`,
|
|
@@ -4282,8 +4789,8 @@ var init_dist2 = __esm({
|
|
|
4282
4789
|
*/
|
|
4283
4790
|
async buildClaimReward(owner) {
|
|
4284
4791
|
const calls = [
|
|
4285
|
-
encodeClaimReward(
|
|
4286
|
-
encodeClaimReward(
|
|
4792
|
+
encodeClaimReward(this.rewardToken, owner),
|
|
4793
|
+
encodeClaimReward(this.bonusRewardToken, owner)
|
|
4287
4794
|
];
|
|
4288
4795
|
return {
|
|
4289
4796
|
description: `[${this.protocolName}] Claim KITTEN + WHYPE farming rewards to ${owner}`,
|
|
@@ -4352,8 +4859,8 @@ var init_dist2 = __esm({
|
|
|
4352
4859
|
for (const pool of pools) {
|
|
4353
4860
|
for (let n = 0; n <= MAX_NONCE_SCAN; n++) {
|
|
4354
4861
|
const key = {
|
|
4355
|
-
rewardToken:
|
|
4356
|
-
bonusRewardToken:
|
|
4862
|
+
rewardToken: this.rewardToken,
|
|
4863
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4357
4864
|
pool,
|
|
4358
4865
|
nonce: BigInt(n)
|
|
4359
4866
|
};
|
|
@@ -4400,8 +4907,8 @@ var init_dist2 = __esm({
|
|
|
4400
4907
|
const isActive = !deactivated;
|
|
4401
4908
|
if (!bestKey || isActive && !bestActive || isActive === bestActive && nonce > bestKey.nonce) {
|
|
4402
4909
|
bestKey = {
|
|
4403
|
-
rewardToken:
|
|
4404
|
-
bonusRewardToken:
|
|
4910
|
+
rewardToken: this.rewardToken,
|
|
4911
|
+
bonusRewardToken: this.bonusRewardToken,
|
|
4405
4912
|
pool,
|
|
4406
4913
|
nonce
|
|
4407
4914
|
};
|
|
@@ -4427,6 +4934,235 @@ var init_dist2 = __esm({
|
|
|
4427
4934
|
return results;
|
|
4428
4935
|
}
|
|
4429
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
|
+
};
|
|
4430
5166
|
POOL_ABI = parseAbi14([
|
|
4431
5167
|
"function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external",
|
|
4432
5168
|
"function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf) external",
|
|
@@ -4728,8 +5464,8 @@ var init_dist2 = __esm({
|
|
|
4728
5464
|
throw DefiError.rpcError(`[${this.protocolName}] getUserAccountData failed: ${e}`);
|
|
4729
5465
|
});
|
|
4730
5466
|
const [totalCollateralBase, totalDebtBase, , , ltv, healthFactor] = result;
|
|
4731
|
-
const
|
|
4732
|
-
const hf = healthFactor >=
|
|
5467
|
+
const MAX_UINT2562 = 2n ** 256n - 1n;
|
|
5468
|
+
const hf = healthFactor >= MAX_UINT2562 ? Infinity : Number(healthFactor) / 1e18;
|
|
4733
5469
|
const collateralUsd = u256ToF64(totalCollateralBase) / 1e8;
|
|
4734
5470
|
const debtUsd = u256ToF64(totalDebtBase) / 1e8;
|
|
4735
5471
|
const ltvBps = u256ToF64(ltv);
|
|
@@ -4893,8 +5629,8 @@ var init_dist2 = __esm({
|
|
|
4893
5629
|
throw DefiError.rpcError(`[${this.protocolName}] getUserAccountData failed: ${e}`);
|
|
4894
5630
|
});
|
|
4895
5631
|
const [totalCollateralBase, totalDebtBase, , , ltv, healthFactor] = result;
|
|
4896
|
-
const
|
|
4897
|
-
const hf = healthFactor >=
|
|
5632
|
+
const MAX_UINT2562 = 2n ** 256n - 1n;
|
|
5633
|
+
const hf = healthFactor >= MAX_UINT2562 ? Infinity : Number(healthFactor) / 1e18;
|
|
4898
5634
|
const collateralUsd = u256ToF642(totalCollateralBase) / 1e18;
|
|
4899
5635
|
const debtUsd = u256ToF642(totalDebtBase) / 1e18;
|
|
4900
5636
|
const ltvBps = u256ToF642(ltv);
|
|
@@ -5141,7 +5877,8 @@ var init_dist2 = __esm({
|
|
|
5141
5877
|
to: this.comet,
|
|
5142
5878
|
data,
|
|
5143
5879
|
value: 0n,
|
|
5144
|
-
gas_estimate: 3e5
|
|
5880
|
+
gas_estimate: 3e5,
|
|
5881
|
+
approvals: [{ token: params.asset, spender: this.comet, amount: params.amount }]
|
|
5145
5882
|
};
|
|
5146
5883
|
}
|
|
5147
5884
|
async buildBorrow(params) {
|
|
@@ -5169,7 +5906,8 @@ var init_dist2 = __esm({
|
|
|
5169
5906
|
to: this.comet,
|
|
5170
5907
|
data,
|
|
5171
5908
|
value: 0n,
|
|
5172
|
-
gas_estimate: 3e5
|
|
5909
|
+
gas_estimate: 3e5,
|
|
5910
|
+
approvals: [{ token: params.asset, spender: this.comet, amount: params.amount }]
|
|
5173
5911
|
};
|
|
5174
5912
|
}
|
|
5175
5913
|
async buildWithdraw(params) {
|
|
@@ -5358,6 +6096,14 @@ var init_dist2 = __esm({
|
|
|
5358
6096
|
"function totalAssets() external view returns (uint256)",
|
|
5359
6097
|
"function totalSupply() external view returns (uint256)"
|
|
5360
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;
|
|
5361
6107
|
IRM_ABI = parseAbi20([
|
|
5362
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)"
|
|
5363
6109
|
]);
|
|
@@ -5367,6 +6113,9 @@ var init_dist2 = __esm({
|
|
|
5367
6113
|
morpho;
|
|
5368
6114
|
defaultVault;
|
|
5369
6115
|
rpcUrl;
|
|
6116
|
+
metaMorphoVaults;
|
|
6117
|
+
metaMorphoVaultEntries;
|
|
6118
|
+
vaultAssetMap = null;
|
|
5370
6119
|
constructor(entry, rpcUrl) {
|
|
5371
6120
|
this.protocolName = entry.name;
|
|
5372
6121
|
this.rpcUrl = rpcUrl;
|
|
@@ -5375,11 +6124,58 @@ var init_dist2 = __esm({
|
|
|
5375
6124
|
if (!morpho) throw DefiError.contractError("Missing 'morpho_blue' contract address");
|
|
5376
6125
|
this.morpho = morpho;
|
|
5377
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;
|
|
5378
6158
|
}
|
|
5379
6159
|
name() {
|
|
5380
6160
|
return this.protocolName;
|
|
5381
6161
|
}
|
|
5382
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
|
+
}
|
|
5383
6179
|
const market = defaultMarketParams(params.asset);
|
|
5384
6180
|
const data = encodeFunctionData19({
|
|
5385
6181
|
abi: MORPHO_ABI,
|
|
@@ -5425,6 +6221,40 @@ var init_dist2 = __esm({
|
|
|
5425
6221
|
};
|
|
5426
6222
|
}
|
|
5427
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
|
+
}
|
|
5428
6258
|
const market = defaultMarketParams(params.asset);
|
|
5429
6259
|
const data = encodeFunctionData19({
|
|
5430
6260
|
abi: MORPHO_ABI,
|
|
@@ -5743,7 +6573,7 @@ var init_dist2 = __esm({
|
|
|
5743
6573
|
return results;
|
|
5744
6574
|
}
|
|
5745
6575
|
};
|
|
5746
|
-
|
|
6576
|
+
ERC4626_ABI2 = parseAbi23([
|
|
5747
6577
|
"function asset() external view returns (address)",
|
|
5748
6578
|
"function totalAssets() external view returns (uint256)",
|
|
5749
6579
|
"function totalSupply() external view returns (uint256)",
|
|
@@ -5768,7 +6598,7 @@ var init_dist2 = __esm({
|
|
|
5768
6598
|
}
|
|
5769
6599
|
async buildDeposit(assets, receiver) {
|
|
5770
6600
|
const data = encodeFunctionData21({
|
|
5771
|
-
abi:
|
|
6601
|
+
abi: ERC4626_ABI2,
|
|
5772
6602
|
functionName: "deposit",
|
|
5773
6603
|
args: [assets, receiver]
|
|
5774
6604
|
});
|
|
@@ -5782,7 +6612,7 @@ var init_dist2 = __esm({
|
|
|
5782
6612
|
}
|
|
5783
6613
|
async buildWithdraw(assets, receiver, owner) {
|
|
5784
6614
|
const data = encodeFunctionData21({
|
|
5785
|
-
abi:
|
|
6615
|
+
abi: ERC4626_ABI2,
|
|
5786
6616
|
functionName: "withdraw",
|
|
5787
6617
|
args: [assets, receiver, owner]
|
|
5788
6618
|
});
|
|
@@ -5799,7 +6629,7 @@ var init_dist2 = __esm({
|
|
|
5799
6629
|
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
5800
6630
|
return client.readContract({
|
|
5801
6631
|
address: this.vaultAddress,
|
|
5802
|
-
abi:
|
|
6632
|
+
abi: ERC4626_ABI2,
|
|
5803
6633
|
functionName: "totalAssets"
|
|
5804
6634
|
}).catch((e) => {
|
|
5805
6635
|
throw DefiError.rpcError(`[${this.protocolName}] totalAssets failed: ${e}`);
|
|
@@ -5810,7 +6640,7 @@ var init_dist2 = __esm({
|
|
|
5810
6640
|
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
5811
6641
|
return client.readContract({
|
|
5812
6642
|
address: this.vaultAddress,
|
|
5813
|
-
abi:
|
|
6643
|
+
abi: ERC4626_ABI2,
|
|
5814
6644
|
functionName: "convertToShares",
|
|
5815
6645
|
args: [assets]
|
|
5816
6646
|
}).catch((e) => {
|
|
@@ -5822,7 +6652,7 @@ var init_dist2 = __esm({
|
|
|
5822
6652
|
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
5823
6653
|
return client.readContract({
|
|
5824
6654
|
address: this.vaultAddress,
|
|
5825
|
-
abi:
|
|
6655
|
+
abi: ERC4626_ABI2,
|
|
5826
6656
|
functionName: "convertToAssets",
|
|
5827
6657
|
args: [shares]
|
|
5828
6658
|
}).catch((e) => {
|
|
@@ -5833,13 +6663,13 @@ var init_dist2 = __esm({
|
|
|
5833
6663
|
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
5834
6664
|
const client = createPublicClient18({ transport: http18(this.rpcUrl) });
|
|
5835
6665
|
const [totalAssets, totalSupply, asset] = await Promise.all([
|
|
5836
|
-
client.readContract({ address: this.vaultAddress, abi:
|
|
6666
|
+
client.readContract({ address: this.vaultAddress, abi: ERC4626_ABI2, functionName: "totalAssets" }).catch((e) => {
|
|
5837
6667
|
throw DefiError.rpcError(`[${this.protocolName}] totalAssets failed: ${e}`);
|
|
5838
6668
|
}),
|
|
5839
|
-
client.readContract({ address: this.vaultAddress, abi:
|
|
6669
|
+
client.readContract({ address: this.vaultAddress, abi: ERC4626_ABI2, functionName: "totalSupply" }).catch((e) => {
|
|
5840
6670
|
throw DefiError.rpcError(`[${this.protocolName}] totalSupply failed: ${e}`);
|
|
5841
6671
|
}),
|
|
5842
|
-
client.readContract({ address: this.vaultAddress, abi:
|
|
6672
|
+
client.readContract({ address: this.vaultAddress, abi: ERC4626_ABI2, functionName: "asset" }).catch((e) => {
|
|
5843
6673
|
throw DefiError.rpcError(`[${this.protocolName}] asset failed: ${e}`);
|
|
5844
6674
|
})
|
|
5845
6675
|
]);
|
|
@@ -6936,10 +7766,11 @@ server.tool(
|
|
|
6936
7766
|
token_a: z.string().describe("First token symbol or address"),
|
|
6937
7767
|
token_b: z.string().describe("Second token symbol or address"),
|
|
6938
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)"),
|
|
6939
7770
|
recipient: z.string().optional().describe("Recipient address for returned tokens"),
|
|
6940
7771
|
broadcast: z.boolean().optional().describe("Set true to broadcast (default: false)")
|
|
6941
7772
|
},
|
|
6942
|
-
async ({ chain, protocol, token_a, token_b, liquidity, recipient, broadcast }) => {
|
|
7773
|
+
async ({ chain, protocol, token_a, token_b, liquidity, token_id, recipient, broadcast }) => {
|
|
6943
7774
|
try {
|
|
6944
7775
|
const chainName = chain ?? "hyperevm";
|
|
6945
7776
|
const registry = getRegistry();
|
|
@@ -6954,7 +7785,8 @@ server.tool(
|
|
|
6954
7785
|
token_a: tokenA,
|
|
6955
7786
|
token_b: tokenB,
|
|
6956
7787
|
liquidity: BigInt(liquidity),
|
|
6957
|
-
recipient: recipientAddr
|
|
7788
|
+
recipient: recipientAddr,
|
|
7789
|
+
token_id: token_id ? BigInt(token_id) : void 0
|
|
6958
7790
|
});
|
|
6959
7791
|
const executor = makeExecutor(broadcast ?? false, chainConfig.effectiveRpcUrl(), chainConfig.explorer_url);
|
|
6960
7792
|
const result = await executor.execute(tx);
|