@hypurrquant/defi-cli 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +487 -1168
- package/dist/index.js.map +1 -1
- package/dist/main.js +490 -1179
- package/dist/main.js.map +1 -1
- package/dist/mcp-server.js +4 -4
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1212,7 +1212,7 @@ var Executor = class _Executor {
|
|
|
1212
1212
|
const [maxFee, priorityFee] = await this.fetchEip1559Fees(rpcUrl);
|
|
1213
1213
|
return {
|
|
1214
1214
|
tx_hash: void 0,
|
|
1215
|
-
status:
|
|
1215
|
+
status: TxStatus.Simulated,
|
|
1216
1216
|
gas_used: gasEstimate > 0n ? Number(gasEstimate) : void 0,
|
|
1217
1217
|
description: tx.description,
|
|
1218
1218
|
details: {
|
|
@@ -1232,7 +1232,7 @@ var Executor = class _Executor {
|
|
|
1232
1232
|
const revertReason = extractRevertReason(errMsg);
|
|
1233
1233
|
return {
|
|
1234
1234
|
tx_hash: void 0,
|
|
1235
|
-
status:
|
|
1235
|
+
status: TxStatus.SimulationFailed,
|
|
1236
1236
|
gas_used: tx.gas_estimate,
|
|
1237
1237
|
description: tx.description,
|
|
1238
1238
|
details: {
|
|
@@ -1254,7 +1254,7 @@ var Executor = class _Executor {
|
|
|
1254
1254
|
}
|
|
1255
1255
|
return {
|
|
1256
1256
|
tx_hash: void 0,
|
|
1257
|
-
status:
|
|
1257
|
+
status: TxStatus.DryRun,
|
|
1258
1258
|
gas_used: tx.gas_estimate,
|
|
1259
1259
|
description: tx.description,
|
|
1260
1260
|
details: {
|
|
@@ -1339,7 +1339,7 @@ var Executor = class _Executor {
|
|
|
1339
1339
|
`);
|
|
1340
1340
|
process.stderr.write("Waiting for confirmation...\n");
|
|
1341
1341
|
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
1342
|
-
const status = receipt.status === "success" ?
|
|
1342
|
+
const status = receipt.status === "success" ? TxStatus.Confirmed : TxStatus.Failed;
|
|
1343
1343
|
let mintedTokenId;
|
|
1344
1344
|
if (receipt.status === "success" && receipt.logs) {
|
|
1345
1345
|
const TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
@@ -1907,6 +1907,9 @@ function registerSchema(parent, getOpts) {
|
|
|
1907
1907
|
});
|
|
1908
1908
|
}
|
|
1909
1909
|
|
|
1910
|
+
// src/commands/lp.ts
|
|
1911
|
+
import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
1912
|
+
|
|
1910
1913
|
// ../defi-protocols/dist/index.js
|
|
1911
1914
|
import { encodeFunctionData as encodeFunctionData4, parseAbi as parseAbi4, createPublicClient as createPublicClient4, http as http4, decodeAbiParameters } from "viem";
|
|
1912
1915
|
import { encodeFunctionData as encodeFunctionData22, parseAbi as parseAbi22, createPublicClient as createPublicClient22, http as http22, decodeFunctionResult as decodeFunctionResult2, decodeAbiParameters as decodeAbiParameters2 } from "viem";
|
|
@@ -4095,128 +4098,6 @@ var masterchefAbi = parseAbi11([
|
|
|
4095
4098
|
"function getNumberOfFarms() view returns (uint256)",
|
|
4096
4099
|
"function getPidByPool(address pool) view returns (uint256)"
|
|
4097
4100
|
]);
|
|
4098
|
-
var MasterChefAdapter = class {
|
|
4099
|
-
protocolName;
|
|
4100
|
-
masterchef;
|
|
4101
|
-
rpcUrl;
|
|
4102
|
-
constructor(entry, rpcUrl) {
|
|
4103
|
-
this.protocolName = entry.name;
|
|
4104
|
-
const masterchef = entry.contracts?.["masterchef"];
|
|
4105
|
-
if (!masterchef) {
|
|
4106
|
-
throw new DefiError("CONTRACT_ERROR", "Missing 'masterchef' contract");
|
|
4107
|
-
}
|
|
4108
|
-
this.masterchef = masterchef;
|
|
4109
|
-
this.rpcUrl = rpcUrl;
|
|
4110
|
-
}
|
|
4111
|
-
name() {
|
|
4112
|
-
return this.protocolName;
|
|
4113
|
-
}
|
|
4114
|
-
/**
|
|
4115
|
-
* Deposit LP tokens into a MasterChef farm.
|
|
4116
|
-
* `gauge` is the pool address (unused for calldata — MasterChef is the target).
|
|
4117
|
-
* `tokenId` carries the farm pid.
|
|
4118
|
-
*/
|
|
4119
|
-
async buildDeposit(gauge, amount, tokenId) {
|
|
4120
|
-
const pid = tokenId ?? 0n;
|
|
4121
|
-
const data = encodeFunctionData11({
|
|
4122
|
-
abi: masterchefAbi,
|
|
4123
|
-
functionName: "deposit",
|
|
4124
|
-
args: [pid, amount]
|
|
4125
|
-
});
|
|
4126
|
-
return {
|
|
4127
|
-
description: `[${this.protocolName}] Deposit ${amount} LP to farm pid=${pid} (pool ${gauge})`,
|
|
4128
|
-
to: this.masterchef,
|
|
4129
|
-
data,
|
|
4130
|
-
value: 0n,
|
|
4131
|
-
gas_estimate: 2e5
|
|
4132
|
-
};
|
|
4133
|
-
}
|
|
4134
|
-
/**
|
|
4135
|
-
* Withdraw LP tokens from a MasterChef farm.
|
|
4136
|
-
* `gauge` is used to look up the pid description only; call site should pass pid via tokenId
|
|
4137
|
-
* on the deposit flow. Here pid defaults to 0 — callers should encode the pid in the gauge
|
|
4138
|
-
* address slot or wrap this adapter with a pid-aware helper.
|
|
4139
|
-
*/
|
|
4140
|
-
async buildWithdraw(gauge, amount) {
|
|
4141
|
-
const pid = 0n;
|
|
4142
|
-
const data = encodeFunctionData11({
|
|
4143
|
-
abi: masterchefAbi,
|
|
4144
|
-
functionName: "withdraw",
|
|
4145
|
-
args: [pid, amount]
|
|
4146
|
-
});
|
|
4147
|
-
return {
|
|
4148
|
-
description: `[${this.protocolName}] Withdraw ${amount} LP from farm pid=${pid} (pool ${gauge})`,
|
|
4149
|
-
to: this.masterchef,
|
|
4150
|
-
data,
|
|
4151
|
-
value: 0n,
|
|
4152
|
-
gas_estimate: 2e5
|
|
4153
|
-
};
|
|
4154
|
-
}
|
|
4155
|
-
/** Withdraw LP tokens specifying a pid explicitly (MasterChef extension beyond IGauge). */
|
|
4156
|
-
async buildWithdrawPid(pid, amount) {
|
|
4157
|
-
const data = encodeFunctionData11({
|
|
4158
|
-
abi: masterchefAbi,
|
|
4159
|
-
functionName: "withdraw",
|
|
4160
|
-
args: [pid, amount]
|
|
4161
|
-
});
|
|
4162
|
-
return {
|
|
4163
|
-
description: `[${this.protocolName}] Withdraw ${amount} LP from farm pid=${pid}`,
|
|
4164
|
-
to: this.masterchef,
|
|
4165
|
-
data,
|
|
4166
|
-
value: 0n,
|
|
4167
|
-
gas_estimate: 2e5
|
|
4168
|
-
};
|
|
4169
|
-
}
|
|
4170
|
-
/** Claim pending MOE rewards. IGauge interface provides no pid — defaults to pid=0. */
|
|
4171
|
-
async buildClaimRewards(gauge) {
|
|
4172
|
-
const pid = 0n;
|
|
4173
|
-
const data = encodeFunctionData11({
|
|
4174
|
-
abi: masterchefAbi,
|
|
4175
|
-
functionName: "claim",
|
|
4176
|
-
args: [[pid]]
|
|
4177
|
-
});
|
|
4178
|
-
return {
|
|
4179
|
-
description: `[${this.protocolName}] Claim MOE rewards for farm pid=${pid} (pool ${gauge})`,
|
|
4180
|
-
to: this.masterchef,
|
|
4181
|
-
data,
|
|
4182
|
-
value: 0n,
|
|
4183
|
-
gas_estimate: 2e5
|
|
4184
|
-
};
|
|
4185
|
-
}
|
|
4186
|
-
/** Claim pending MOE rewards for a specific pid (MasterChef extension beyond IGauge). */
|
|
4187
|
-
async buildClaimRewardsPid(pid) {
|
|
4188
|
-
const data = encodeFunctionData11({
|
|
4189
|
-
abi: masterchefAbi,
|
|
4190
|
-
functionName: "claim",
|
|
4191
|
-
args: [[pid]]
|
|
4192
|
-
});
|
|
4193
|
-
return {
|
|
4194
|
-
description: `[${this.protocolName}] Claim MOE rewards for farm pid=${pid}`,
|
|
4195
|
-
to: this.masterchef,
|
|
4196
|
-
data,
|
|
4197
|
-
value: 0n,
|
|
4198
|
-
gas_estimate: 2e5
|
|
4199
|
-
};
|
|
4200
|
-
}
|
|
4201
|
-
/** Get pending MOE rewards for a user. Requires rpcUrl. */
|
|
4202
|
-
async getPendingRewards(_gauge, user) {
|
|
4203
|
-
if (!this.rpcUrl) {
|
|
4204
|
-
throw DefiError.unsupported(`[${this.protocolName}] getPendingRewards requires RPC`);
|
|
4205
|
-
}
|
|
4206
|
-
const client = createPublicClient7({ transport: http7(this.rpcUrl) });
|
|
4207
|
-
const rewards = await client.readContract({
|
|
4208
|
-
address: this.masterchef,
|
|
4209
|
-
abi: masterchefAbi,
|
|
4210
|
-
functionName: "pendingRewards",
|
|
4211
|
-
args: [user, [0n]]
|
|
4212
|
-
});
|
|
4213
|
-
return rewards.map((amount) => ({
|
|
4214
|
-
token: this.masterchef,
|
|
4215
|
-
symbol: "MOE",
|
|
4216
|
-
amount
|
|
4217
|
-
}));
|
|
4218
|
-
}
|
|
4219
|
-
};
|
|
4220
4101
|
var lbRouterAbi = parseAbi12([
|
|
4221
4102
|
"struct LiquidityParameters { address tokenX; address tokenY; uint256 binStep; uint256 amountX; uint256 amountY; uint256 amountXMin; uint256 amountYMin; uint256 activeIdDesired; uint256 idSlippage; int256[] deltaIds; uint256[] distributionX; uint256[] distributionY; address to; address refundTo; uint256 deadline; }",
|
|
4222
4103
|
"function addLiquidity(LiquidityParameters calldata liquidityParameters) external returns (uint256 amountXAdded, uint256 amountYAdded, uint256 amountXLeft, uint256 amountYLeft, uint256[] memory depositIds, uint256[] memory liquidityMinted)",
|
|
@@ -6838,46 +6719,6 @@ var GENERIC_LST_ABI = parseAbi24([
|
|
|
6838
6719
|
"function stake() external payable returns (uint256)",
|
|
6839
6720
|
"function unstake(uint256 amount) external returns (uint256)"
|
|
6840
6721
|
]);
|
|
6841
|
-
var GenericLstAdapter = class {
|
|
6842
|
-
protocolName;
|
|
6843
|
-
staking;
|
|
6844
|
-
constructor(entry, _rpcUrl) {
|
|
6845
|
-
this.protocolName = entry.name;
|
|
6846
|
-
const staking = entry.contracts?.["staking"];
|
|
6847
|
-
if (!staking) throw DefiError.contractError("Missing 'staking' contract");
|
|
6848
|
-
this.staking = staking;
|
|
6849
|
-
}
|
|
6850
|
-
name() {
|
|
6851
|
-
return this.protocolName;
|
|
6852
|
-
}
|
|
6853
|
-
async buildStake(params) {
|
|
6854
|
-
const data = encodeFunctionData222({ abi: GENERIC_LST_ABI, functionName: "stake" });
|
|
6855
|
-
return {
|
|
6856
|
-
description: `[${this.protocolName}] Stake ${params.amount} HYPE`,
|
|
6857
|
-
to: this.staking,
|
|
6858
|
-
data,
|
|
6859
|
-
value: params.amount,
|
|
6860
|
-
gas_estimate: 2e5
|
|
6861
|
-
};
|
|
6862
|
-
}
|
|
6863
|
-
async buildUnstake(params) {
|
|
6864
|
-
const data = encodeFunctionData222({
|
|
6865
|
-
abi: GENERIC_LST_ABI,
|
|
6866
|
-
functionName: "unstake",
|
|
6867
|
-
args: [params.amount]
|
|
6868
|
-
});
|
|
6869
|
-
return {
|
|
6870
|
-
description: `[${this.protocolName}] Unstake ${params.amount}`,
|
|
6871
|
-
to: this.staking,
|
|
6872
|
-
data,
|
|
6873
|
-
value: 0n,
|
|
6874
|
-
gas_estimate: 2e5
|
|
6875
|
-
};
|
|
6876
|
-
}
|
|
6877
|
-
async getInfo() {
|
|
6878
|
-
throw DefiError.unsupported(`[${this.protocolName}] getInfo requires RPC`);
|
|
6879
|
-
}
|
|
6880
|
-
};
|
|
6881
6722
|
var STHYPE_ABI = parseAbi25([
|
|
6882
6723
|
"function submit(address referral) external payable returns (uint256)",
|
|
6883
6724
|
"function requestWithdrawals(uint256[] amounts, address owner) external returns (uint256[] requestIds)"
|
|
@@ -6885,70 +6726,6 @@ var STHYPE_ABI = parseAbi25([
|
|
|
6885
6726
|
var ERC20_ABI3 = parseAbi25([
|
|
6886
6727
|
"function totalSupply() external view returns (uint256)"
|
|
6887
6728
|
]);
|
|
6888
|
-
var StHypeAdapter = class {
|
|
6889
|
-
protocolName;
|
|
6890
|
-
staking;
|
|
6891
|
-
sthypeToken;
|
|
6892
|
-
rpcUrl;
|
|
6893
|
-
constructor(entry, rpcUrl) {
|
|
6894
|
-
this.protocolName = entry.name;
|
|
6895
|
-
this.rpcUrl = rpcUrl;
|
|
6896
|
-
const staking = entry.contracts?.["staking"];
|
|
6897
|
-
if (!staking) throw DefiError.contractError("Missing 'staking' contract");
|
|
6898
|
-
this.staking = staking;
|
|
6899
|
-
this.sthypeToken = entry.contracts?.["sthype_token"];
|
|
6900
|
-
}
|
|
6901
|
-
name() {
|
|
6902
|
-
return this.protocolName;
|
|
6903
|
-
}
|
|
6904
|
-
async buildStake(params) {
|
|
6905
|
-
const data = encodeFunctionData23({
|
|
6906
|
-
abi: STHYPE_ABI,
|
|
6907
|
-
functionName: "submit",
|
|
6908
|
-
args: [zeroAddress12]
|
|
6909
|
-
});
|
|
6910
|
-
return {
|
|
6911
|
-
description: `[${this.protocolName}] Stake ${params.amount} HYPE for stHYPE`,
|
|
6912
|
-
to: this.staking,
|
|
6913
|
-
data,
|
|
6914
|
-
value: params.amount,
|
|
6915
|
-
gas_estimate: 2e5
|
|
6916
|
-
};
|
|
6917
|
-
}
|
|
6918
|
-
async buildUnstake(params) {
|
|
6919
|
-
const data = encodeFunctionData23({
|
|
6920
|
-
abi: STHYPE_ABI,
|
|
6921
|
-
functionName: "requestWithdrawals",
|
|
6922
|
-
args: [[params.amount], params.recipient]
|
|
6923
|
-
});
|
|
6924
|
-
return {
|
|
6925
|
-
description: `[${this.protocolName}] Request unstake ${params.amount} stHYPE`,
|
|
6926
|
-
to: this.staking,
|
|
6927
|
-
data,
|
|
6928
|
-
value: 0n,
|
|
6929
|
-
gas_estimate: 2e5
|
|
6930
|
-
};
|
|
6931
|
-
}
|
|
6932
|
-
async getInfo() {
|
|
6933
|
-
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
6934
|
-
const client = createPublicClient19({ transport: http19(this.rpcUrl) });
|
|
6935
|
-
const tokenAddr = this.sthypeToken ?? this.staking;
|
|
6936
|
-
const totalSupply = await client.readContract({
|
|
6937
|
-
address: tokenAddr,
|
|
6938
|
-
abi: ERC20_ABI3,
|
|
6939
|
-
functionName: "totalSupply"
|
|
6940
|
-
}).catch((e) => {
|
|
6941
|
-
throw DefiError.rpcError(`[${this.protocolName}] totalSupply failed: ${e}`);
|
|
6942
|
-
});
|
|
6943
|
-
return {
|
|
6944
|
-
protocol: this.protocolName,
|
|
6945
|
-
staked_token: zeroAddress12,
|
|
6946
|
-
liquid_token: tokenAddr,
|
|
6947
|
-
exchange_rate: 1,
|
|
6948
|
-
total_staked: totalSupply
|
|
6949
|
-
};
|
|
6950
|
-
}
|
|
6951
|
-
};
|
|
6952
6729
|
var KINETIQ_ABI = parseAbi26([
|
|
6953
6730
|
"function stake() external payable returns (uint256)",
|
|
6954
6731
|
"function requestUnstake(uint256 amount) external returns (uint256)",
|
|
@@ -6957,72 +6734,6 @@ var KINETIQ_ABI = parseAbi26([
|
|
|
6957
6734
|
var ORACLE_ABI3 = parseAbi26([
|
|
6958
6735
|
"function getAssetPrice(address asset) external view returns (uint256)"
|
|
6959
6736
|
]);
|
|
6960
|
-
var WHYPE = "0x5555555555555555555555555555555555555555";
|
|
6961
|
-
var HYPERLEND_ORACLE = "0xc9fb4fbe842d57ea1df3e641a281827493a63030";
|
|
6962
|
-
var KinetiqAdapter = class {
|
|
6963
|
-
protocolName;
|
|
6964
|
-
staking;
|
|
6965
|
-
liquidToken;
|
|
6966
|
-
rpcUrl;
|
|
6967
|
-
constructor(entry, rpcUrl) {
|
|
6968
|
-
this.protocolName = entry.name;
|
|
6969
|
-
this.rpcUrl = rpcUrl;
|
|
6970
|
-
const staking = entry.contracts?.["staking"];
|
|
6971
|
-
if (!staking) throw DefiError.contractError("Missing 'staking' contract address");
|
|
6972
|
-
this.staking = staking;
|
|
6973
|
-
this.liquidToken = entry.contracts?.["khype_token"] ?? staking;
|
|
6974
|
-
}
|
|
6975
|
-
name() {
|
|
6976
|
-
return this.protocolName;
|
|
6977
|
-
}
|
|
6978
|
-
async buildStake(params) {
|
|
6979
|
-
const data = encodeFunctionData24({ abi: KINETIQ_ABI, functionName: "stake" });
|
|
6980
|
-
return {
|
|
6981
|
-
description: `[${this.protocolName}] Stake ${params.amount} HYPE for kHYPE`,
|
|
6982
|
-
to: this.staking,
|
|
6983
|
-
data,
|
|
6984
|
-
value: params.amount,
|
|
6985
|
-
gas_estimate: 3e5
|
|
6986
|
-
};
|
|
6987
|
-
}
|
|
6988
|
-
async buildUnstake(params) {
|
|
6989
|
-
const data = encodeFunctionData24({
|
|
6990
|
-
abi: KINETIQ_ABI,
|
|
6991
|
-
functionName: "requestUnstake",
|
|
6992
|
-
args: [params.amount]
|
|
6993
|
-
});
|
|
6994
|
-
return {
|
|
6995
|
-
description: `[${this.protocolName}] Request unstake ${params.amount} kHYPE`,
|
|
6996
|
-
to: this.staking,
|
|
6997
|
-
data,
|
|
6998
|
-
value: 0n,
|
|
6999
|
-
gas_estimate: 3e5
|
|
7000
|
-
};
|
|
7001
|
-
}
|
|
7002
|
-
async getInfo() {
|
|
7003
|
-
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7004
|
-
const client = createPublicClient20({ transport: http20(this.rpcUrl) });
|
|
7005
|
-
const totalStaked = await client.readContract({
|
|
7006
|
-
address: this.staking,
|
|
7007
|
-
abi: KINETIQ_ABI,
|
|
7008
|
-
functionName: "totalStaked"
|
|
7009
|
-
}).catch((e) => {
|
|
7010
|
-
throw DefiError.rpcError(`[${this.protocolName}] totalStaked failed: ${e}`);
|
|
7011
|
-
});
|
|
7012
|
-
const [khypePrice, hypePrice] = await Promise.all([
|
|
7013
|
-
client.readContract({ address: HYPERLEND_ORACLE, abi: ORACLE_ABI3, functionName: "getAssetPrice", args: [this.liquidToken] }).catch(() => 0n),
|
|
7014
|
-
client.readContract({ address: HYPERLEND_ORACLE, abi: ORACLE_ABI3, functionName: "getAssetPrice", args: [WHYPE] }).catch(() => 0n)
|
|
7015
|
-
]);
|
|
7016
|
-
const rateF64 = hypePrice > 0n && khypePrice > 0n ? Number(khypePrice * 10n ** 18n / hypePrice) / 1e18 : 1;
|
|
7017
|
-
return {
|
|
7018
|
-
protocol: this.protocolName,
|
|
7019
|
-
staked_token: zeroAddress13,
|
|
7020
|
-
liquid_token: this.liquidToken,
|
|
7021
|
-
exchange_rate: rateF64,
|
|
7022
|
-
total_staked: totalStaked
|
|
7023
|
-
};
|
|
7024
|
-
}
|
|
7025
|
-
};
|
|
7026
6737
|
var HLP_ABI = parseAbi27([
|
|
7027
6738
|
"function deposit(uint256 amount) external returns (uint256)",
|
|
7028
6739
|
"function withdraw(uint256 shares) external returns (uint256)"
|
|
@@ -7039,59 +6750,6 @@ var ERC721_ABI = parseAbi29([
|
|
|
7039
6750
|
"function balanceOf(address owner) returns (uint256)",
|
|
7040
6751
|
"function tokenURI(uint256 tokenId) returns (string)"
|
|
7041
6752
|
]);
|
|
7042
|
-
var ERC721Adapter = class {
|
|
7043
|
-
protocolName;
|
|
7044
|
-
rpcUrl;
|
|
7045
|
-
constructor(entry, rpcUrl) {
|
|
7046
|
-
this.protocolName = entry.name;
|
|
7047
|
-
this.rpcUrl = rpcUrl;
|
|
7048
|
-
}
|
|
7049
|
-
name() {
|
|
7050
|
-
return this.protocolName;
|
|
7051
|
-
}
|
|
7052
|
-
async getCollectionInfo(collection) {
|
|
7053
|
-
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7054
|
-
const client = createPublicClient21({ transport: http21(this.rpcUrl) });
|
|
7055
|
-
const [collectionName, symbol, totalSupply] = await Promise.all([
|
|
7056
|
-
client.readContract({ address: collection, abi: ERC721_ABI, functionName: "name" }).catch((e) => {
|
|
7057
|
-
throw DefiError.rpcError(`[${this.protocolName}] name failed: ${e}`);
|
|
7058
|
-
}),
|
|
7059
|
-
client.readContract({ address: collection, abi: ERC721_ABI, functionName: "symbol" }).catch((e) => {
|
|
7060
|
-
throw DefiError.rpcError(`[${this.protocolName}] symbol failed: ${e}`);
|
|
7061
|
-
}),
|
|
7062
|
-
client.readContract({ address: collection, abi: ERC721_ABI, functionName: "totalSupply" }).catch(() => void 0)
|
|
7063
|
-
]);
|
|
7064
|
-
return {
|
|
7065
|
-
address: collection,
|
|
7066
|
-
name: collectionName,
|
|
7067
|
-
symbol,
|
|
7068
|
-
total_supply: totalSupply
|
|
7069
|
-
};
|
|
7070
|
-
}
|
|
7071
|
-
async getTokenInfo(collection, tokenId) {
|
|
7072
|
-
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7073
|
-
const client = createPublicClient21({ transport: http21(this.rpcUrl) });
|
|
7074
|
-
const [owner, tokenUri] = await Promise.all([
|
|
7075
|
-
client.readContract({ address: collection, abi: ERC721_ABI, functionName: "ownerOf", args: [tokenId] }).catch((e) => {
|
|
7076
|
-
throw DefiError.rpcError(`[${this.protocolName}] ownerOf failed: ${e}`);
|
|
7077
|
-
}),
|
|
7078
|
-
client.readContract({ address: collection, abi: ERC721_ABI, functionName: "tokenURI", args: [tokenId] }).catch(() => void 0)
|
|
7079
|
-
]);
|
|
7080
|
-
return {
|
|
7081
|
-
collection,
|
|
7082
|
-
token_id: tokenId,
|
|
7083
|
-
owner,
|
|
7084
|
-
token_uri: tokenUri
|
|
7085
|
-
};
|
|
7086
|
-
}
|
|
7087
|
-
async getBalance(owner, collection) {
|
|
7088
|
-
if (!this.rpcUrl) throw DefiError.rpcError("No RPC URL configured");
|
|
7089
|
-
const client = createPublicClient21({ transport: http21(this.rpcUrl) });
|
|
7090
|
-
return client.readContract({ address: collection, abi: ERC721_ABI, functionName: "balanceOf", args: [owner] }).catch((e) => {
|
|
7091
|
-
throw DefiError.rpcError(`[${this.protocolName}] balanceOf failed: ${e}`);
|
|
7092
|
-
});
|
|
7093
|
-
}
|
|
7094
|
-
};
|
|
7095
6753
|
function createDex(entry, rpcUrl) {
|
|
7096
6754
|
switch (entry.interface) {
|
|
7097
6755
|
case "uniswap_v3":
|
|
@@ -7155,19 +6813,6 @@ function createVault(entry, rpcUrl) {
|
|
|
7155
6813
|
throw DefiError.unsupported(`Vault interface '${entry.interface}' not yet implemented`);
|
|
7156
6814
|
}
|
|
7157
6815
|
}
|
|
7158
|
-
function createLiquidStaking(entry, rpcUrl) {
|
|
7159
|
-
switch (entry.interface) {
|
|
7160
|
-
case "kinetiq_staking":
|
|
7161
|
-
return new KinetiqAdapter(entry, rpcUrl);
|
|
7162
|
-
case "sthype_staking":
|
|
7163
|
-
return new StHypeAdapter(entry, rpcUrl);
|
|
7164
|
-
case "hyperbeat_lst":
|
|
7165
|
-
case "kintsu":
|
|
7166
|
-
return new GenericLstAdapter(entry, rpcUrl);
|
|
7167
|
-
default:
|
|
7168
|
-
return new GenericLstAdapter(entry, rpcUrl);
|
|
7169
|
-
}
|
|
7170
|
-
}
|
|
7171
6816
|
function createGauge(entry, rpcUrl) {
|
|
7172
6817
|
if (entry.interface === "hybra" || entry.contracts?.["gauge_manager"]) {
|
|
7173
6818
|
return new HybraGaugeAdapter(entry, rpcUrl);
|
|
@@ -7181,19 +6826,6 @@ function createGauge(entry, rpcUrl) {
|
|
|
7181
6826
|
throw DefiError.unsupported(`Gauge interface '${entry.interface}' not supported`);
|
|
7182
6827
|
}
|
|
7183
6828
|
}
|
|
7184
|
-
function createMasterChef(entry, rpcUrl) {
|
|
7185
|
-
return new MasterChefAdapter(entry, rpcUrl);
|
|
7186
|
-
}
|
|
7187
|
-
function createNft(entry, rpcUrl) {
|
|
7188
|
-
switch (entry.interface) {
|
|
7189
|
-
case "erc721":
|
|
7190
|
-
return new ERC721Adapter(entry, rpcUrl);
|
|
7191
|
-
case "marketplace":
|
|
7192
|
-
throw DefiError.unsupported(`NFT marketplace '${entry.name}' is not queryable as ERC-721. Use a specific collection address.`);
|
|
7193
|
-
default:
|
|
7194
|
-
throw DefiError.unsupported(`NFT interface '${entry.interface}' not supported`);
|
|
7195
|
-
}
|
|
7196
|
-
}
|
|
7197
6829
|
function createOracleFromLending(entry, rpcUrl) {
|
|
7198
6830
|
switch (entry.interface) {
|
|
7199
6831
|
case "aave_v3":
|
|
@@ -7267,61 +6899,131 @@ var DexSpotPrice = class {
|
|
|
7267
6899
|
}
|
|
7268
6900
|
};
|
|
7269
6901
|
|
|
7270
|
-
// src/commands/
|
|
7271
|
-
function
|
|
7272
|
-
|
|
7273
|
-
|
|
6902
|
+
// src/commands/lp.ts
|
|
6903
|
+
function resolveAccount(optOwner) {
|
|
6904
|
+
if (optOwner) return optOwner;
|
|
6905
|
+
const walletAddr = process.env["DEFI_WALLET_ADDRESS"];
|
|
6906
|
+
if (walletAddr) return walletAddr;
|
|
6907
|
+
const privateKey = process.env["DEFI_PRIVATE_KEY"];
|
|
6908
|
+
if (privateKey) return privateKeyToAccount2(privateKey).address;
|
|
6909
|
+
throw new Error("--address, DEFI_WALLET_ADDRESS, or DEFI_PRIVATE_KEY is required");
|
|
6910
|
+
}
|
|
6911
|
+
function resolvePoolAddress(registry, protocolSlug, pool) {
|
|
6912
|
+
if (pool.startsWith("0x")) return pool;
|
|
6913
|
+
return registry.resolvePool(protocolSlug, pool).address;
|
|
6914
|
+
}
|
|
6915
|
+
function registerLP(parent, getOpts, makeExecutor2) {
|
|
6916
|
+
const lp = parent.command("lp").description("Unified LP operations: discover, add, farm, claim, remove, positions");
|
|
6917
|
+
lp.command("discover").description("Scan all protocols for fee + emission pools (gauges, farming, LB rewards)").option("--protocol <protocol>", "Filter to a single protocol slug").option("--emission-only", "Only show emission (gauge/farming) pools, skip fee-only").action(async (opts) => {
|
|
7274
6918
|
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7275
6919
|
const registry = Registry.loadEmbedded();
|
|
7276
6920
|
const chain = registry.getChain(chainName);
|
|
7277
|
-
const
|
|
7278
|
-
const
|
|
7279
|
-
const
|
|
7280
|
-
const
|
|
7281
|
-
|
|
7282
|
-
|
|
6921
|
+
const rpcUrl = chain.effectiveRpcUrl();
|
|
6922
|
+
const allProtocols = registry.getProtocolsForChain(chainName);
|
|
6923
|
+
const protocols = opts.protocol ? [registry.getProtocol(opts.protocol)] : allProtocols;
|
|
6924
|
+
const results = [];
|
|
6925
|
+
await Promise.allSettled(
|
|
6926
|
+
protocols.map(async (protocol) => {
|
|
6927
|
+
try {
|
|
6928
|
+
if (["solidly_v2", "solidly_cl", "algebra_v3", "hybra"].includes(protocol.interface)) {
|
|
6929
|
+
const adapter = createGauge(protocol, rpcUrl);
|
|
6930
|
+
if (adapter.discoverGaugedPools) {
|
|
6931
|
+
const pools = await adapter.discoverGaugedPools();
|
|
6932
|
+
for (const p of pools) {
|
|
6933
|
+
results.push({
|
|
6934
|
+
protocol: protocol.slug,
|
|
6935
|
+
pool: p.pool,
|
|
6936
|
+
pair: `${p.token0}/${p.token1}`,
|
|
6937
|
+
type: "EMISSION",
|
|
6938
|
+
source: "gauge"
|
|
6939
|
+
});
|
|
6940
|
+
}
|
|
6941
|
+
}
|
|
6942
|
+
}
|
|
6943
|
+
if (protocol.interface === "algebra_v3" && protocol.contracts?.["farming_center"]) {
|
|
6944
|
+
const adapter = createKittenSwapFarming(protocol, rpcUrl);
|
|
6945
|
+
const pools = await adapter.discoverFarmingPools();
|
|
6946
|
+
for (const p of pools) {
|
|
6947
|
+
results.push({
|
|
6948
|
+
protocol: protocol.slug,
|
|
6949
|
+
pool: p.pool,
|
|
6950
|
+
type: "EMISSION",
|
|
6951
|
+
source: "farming",
|
|
6952
|
+
total_reward: p.totalReward.toString(),
|
|
6953
|
+
bonus_reward: p.bonusReward.toString(),
|
|
6954
|
+
active: p.active
|
|
6955
|
+
});
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6958
|
+
if (protocol.interface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
|
|
6959
|
+
const adapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
6960
|
+
const pools = await adapter.discoverRewardedPools();
|
|
6961
|
+
for (const p of pools) {
|
|
6962
|
+
if (!opts.emissionOnly || !p.stopped) {
|
|
6963
|
+
results.push({
|
|
6964
|
+
protocol: protocol.slug,
|
|
6965
|
+
pool: p.pool,
|
|
6966
|
+
pair: `${p.symbolX}/${p.symbolY}`,
|
|
6967
|
+
type: "EMISSION",
|
|
6968
|
+
source: "lb_hooks",
|
|
6969
|
+
stopped: p.stopped,
|
|
6970
|
+
moePerDay: p.moePerDay,
|
|
6971
|
+
aprPercent: p.aprPercent,
|
|
6972
|
+
rangeTvlUsd: p.rangeTvlUsd,
|
|
6973
|
+
isTopPool: p.isTopPool
|
|
6974
|
+
});
|
|
6975
|
+
}
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
} catch {
|
|
6979
|
+
}
|
|
6980
|
+
})
|
|
6981
|
+
);
|
|
6982
|
+
if (opts.emissionOnly) {
|
|
6983
|
+
printOutput(results.filter((r) => r.type === "EMISSION"), getOpts());
|
|
6984
|
+
} else {
|
|
6985
|
+
printOutput(results, getOpts());
|
|
6986
|
+
}
|
|
7283
6987
|
});
|
|
7284
|
-
|
|
6988
|
+
lp.command("add").description("Add liquidity to a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--amount-a <amount>", "Amount of token A in wei").requiredOption("--amount-b <amount>", "Amount of token B in wei").option("--pool <name_or_address>", "Pool name (e.g. WHYPE/USDC) or address").option("--recipient <address>", "Recipient address").option("--tick-lower <tick>", "Lower tick for concentrated LP (default: full range)").option("--tick-upper <tick>", "Upper tick for concentrated LP (default: full range)").option("--range <percent>", "\xB1N% concentrated range around current price (e.g. --range 2)").action(async (opts) => {
|
|
7285
6989
|
const executor = makeExecutor2();
|
|
7286
6990
|
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7287
6991
|
const registry = Registry.loadEmbedded();
|
|
7288
6992
|
const chain = registry.getChain(chainName);
|
|
7289
6993
|
const protocol = registry.getProtocol(opts.protocol);
|
|
7290
6994
|
const adapter = createDex(protocol, chain.effectiveRpcUrl());
|
|
7291
|
-
const
|
|
7292
|
-
const
|
|
7293
|
-
const recipient = opts.recipient ?? process.env
|
|
7294
|
-
const
|
|
6995
|
+
const tokenA = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
|
|
6996
|
+
const tokenB = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
|
|
6997
|
+
const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
|
|
6998
|
+
const poolAddr = opts.pool ? resolvePoolAddress(registry, opts.protocol, opts.pool) : void 0;
|
|
6999
|
+
const tx = await adapter.buildAddLiquidity({
|
|
7295
7000
|
protocol: protocol.name,
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
recipient
|
|
7001
|
+
token_a: tokenA,
|
|
7002
|
+
token_b: tokenB,
|
|
7003
|
+
amount_a: BigInt(opts.amountA),
|
|
7004
|
+
amount_b: BigInt(opts.amountB),
|
|
7005
|
+
recipient,
|
|
7006
|
+
tick_lower: opts.tickLower !== void 0 ? parseInt(opts.tickLower) : void 0,
|
|
7007
|
+
tick_upper: opts.tickUpper !== void 0 ? parseInt(opts.tickUpper) : void 0,
|
|
7008
|
+
range_pct: opts.range !== void 0 ? parseFloat(opts.range) : void 0,
|
|
7009
|
+
pool: poolAddr
|
|
7301
7010
|
});
|
|
7302
7011
|
const result = await executor.execute(tx);
|
|
7303
7012
|
printOutput(result, getOpts());
|
|
7304
7013
|
});
|
|
7305
|
-
|
|
7014
|
+
lp.command("farm").description("Add liquidity and auto-stake into gauge/farming for emissions").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--amount-a <amount>", "Amount of token A in wei").requiredOption("--amount-b <amount>", "Amount of token B in wei").option("--pool <name_or_address>", "Pool name (e.g. WHYPE/USDC) or address").option("--gauge <address>", "Gauge address (required for solidly/hybra if not resolved automatically)").option("--recipient <address>", "Recipient / owner address").option("--tick-lower <tick>", "Lower tick for concentrated LP").option("--tick-upper <tick>", "Upper tick for concentrated LP").option("--range <percent>", "\xB1N% concentrated range around current price").action(async (opts) => {
|
|
7306
7015
|
const executor = makeExecutor2();
|
|
7307
7016
|
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7308
7017
|
const registry = Registry.loadEmbedded();
|
|
7309
7018
|
const chain = registry.getChain(chainName);
|
|
7310
7019
|
const protocol = registry.getProtocol(opts.protocol);
|
|
7311
|
-
const
|
|
7020
|
+
const rpcUrl = chain.effectiveRpcUrl();
|
|
7021
|
+
const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
|
|
7022
|
+
const poolAddr = opts.pool ? resolvePoolAddress(registry, opts.protocol, opts.pool) : void 0;
|
|
7312
7023
|
const tokenA = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
|
|
7313
7024
|
const tokenB = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
|
|
7314
|
-
const
|
|
7315
|
-
|
|
7316
|
-
if (opts.pool) {
|
|
7317
|
-
if (opts.pool.startsWith("0x")) {
|
|
7318
|
-
poolAddr = opts.pool;
|
|
7319
|
-
} else {
|
|
7320
|
-
const poolInfo = registry.resolvePool(opts.protocol, opts.pool);
|
|
7321
|
-
poolAddr = poolInfo.address;
|
|
7322
|
-
}
|
|
7323
|
-
}
|
|
7324
|
-
const tx = await adapter.buildAddLiquidity({
|
|
7025
|
+
const dexAdapter = createDex(protocol, rpcUrl);
|
|
7026
|
+
const addTx = await dexAdapter.buildAddLiquidity({
|
|
7325
7027
|
protocol: protocol.name,
|
|
7326
7028
|
token_a: tokenA,
|
|
7327
7029
|
token_b: tokenB,
|
|
@@ -7333,143 +7035,202 @@ function registerDex(parent, getOpts, makeExecutor2) {
|
|
|
7333
7035
|
range_pct: opts.range !== void 0 ? parseFloat(opts.range) : void 0,
|
|
7334
7036
|
pool: poolAddr
|
|
7335
7037
|
});
|
|
7336
|
-
|
|
7337
|
-
|
|
7038
|
+
process.stderr.write("Step 1/2: Adding liquidity...\n");
|
|
7039
|
+
const addResult = await executor.execute(addTx);
|
|
7040
|
+
printOutput({ step: "lp_add", ...addResult }, getOpts());
|
|
7041
|
+
if (addResult.status !== "confirmed" && addResult.status !== "simulated") {
|
|
7042
|
+
process.stderr.write("Step 2/2: Skipped \u2014 LP add did not succeed.\n");
|
|
7043
|
+
return;
|
|
7044
|
+
}
|
|
7045
|
+
const mintedTokenId = addResult.details?.minted_token_id ? BigInt(addResult.details.minted_token_id) : void 0;
|
|
7046
|
+
const iface = protocol.interface;
|
|
7047
|
+
if (iface === "algebra_v3" && protocol.contracts?.["farming_center"]) {
|
|
7048
|
+
if (!mintedTokenId) {
|
|
7049
|
+
process.stderr.write("Step 2/2: Skipped staking (no tokenId \u2014 run in --broadcast mode to get minted NFT)\n");
|
|
7050
|
+
return;
|
|
7051
|
+
}
|
|
7052
|
+
if (!poolAddr) throw new Error("--pool is required for lp farm with KittenSwap (needed for farming center)");
|
|
7053
|
+
process.stderr.write("Step 2/2: Staking into KittenSwap eternal farming...\n");
|
|
7054
|
+
const farmAdapter = createKittenSwapFarming(protocol, rpcUrl);
|
|
7055
|
+
const stakeTx = await farmAdapter.buildEnterFarming(mintedTokenId, poolAddr, recipient);
|
|
7056
|
+
const stakeResult = await executor.execute(stakeTx);
|
|
7057
|
+
printOutput({ step: "stake_farming", ...stakeResult }, getOpts());
|
|
7058
|
+
return;
|
|
7059
|
+
}
|
|
7060
|
+
if (["solidly_v2", "solidly_cl", "hybra"].includes(iface)) {
|
|
7061
|
+
if (!mintedTokenId && iface !== "solidly_v2") {
|
|
7062
|
+
process.stderr.write("Step 2/2: Skipped staking (no tokenId \u2014 run in --broadcast mode to get minted NFT)\n");
|
|
7063
|
+
return;
|
|
7064
|
+
}
|
|
7065
|
+
let gaugeAddr = opts.gauge;
|
|
7066
|
+
if (!gaugeAddr && poolAddr) {
|
|
7067
|
+
try {
|
|
7068
|
+
const gaugeAdapter2 = createGauge(protocol, rpcUrl);
|
|
7069
|
+
if (gaugeAdapter2.resolveGauge) {
|
|
7070
|
+
gaugeAddr = await gaugeAdapter2.resolveGauge(poolAddr);
|
|
7071
|
+
}
|
|
7072
|
+
} catch {
|
|
7073
|
+
}
|
|
7074
|
+
}
|
|
7075
|
+
if (!gaugeAddr) throw new Error("--gauge <address> is required for staking (could not auto-resolve gauge)");
|
|
7076
|
+
process.stderr.write("Step 2/2: Staking into gauge...\n");
|
|
7077
|
+
const gaugeAdapter = createGauge(protocol, rpcUrl);
|
|
7078
|
+
const tokenIdArg = mintedTokenId;
|
|
7079
|
+
const amountArg = iface === "solidly_v2" ? BigInt("115792089237316195423570985008687907853269984665640564039457584007913129639935") : 0n;
|
|
7080
|
+
const lpTokenArg = iface === "solidly_v2" ? poolAddr : void 0;
|
|
7081
|
+
const stakeTx = await gaugeAdapter.buildDeposit(gaugeAddr, amountArg, tokenIdArg, lpTokenArg);
|
|
7082
|
+
const stakeResult = await executor.execute(stakeTx);
|
|
7083
|
+
printOutput({ step: "stake_gauge", ...stakeResult }, getOpts());
|
|
7084
|
+
return;
|
|
7085
|
+
}
|
|
7086
|
+
if (iface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
|
|
7087
|
+
process.stderr.write("Step 2/2: Merchant Moe LB hooks handle rewards automatically \u2014 no staking needed.\n");
|
|
7088
|
+
return;
|
|
7089
|
+
}
|
|
7090
|
+
process.stderr.write("Step 2/2: No staking adapter found for this protocol interface \u2014 liquidity added only.\n");
|
|
7338
7091
|
});
|
|
7339
|
-
|
|
7092
|
+
lp.command("claim").description("Claim rewards from a pool (fee or emission)").requiredOption("--protocol <protocol>", "Protocol slug").option("--pool <address>", "Pool address (required for farming/LB)").option("--gauge <address>", "Gauge contract address (required for solidly/hybra)").option("--token-id <id>", "NFT tokenId (for CL gauge or farming positions)").option("--bins <binIds>", "Comma-separated bin IDs (for Merchant Moe LB)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
|
|
7340
7093
|
const executor = makeExecutor2();
|
|
7341
7094
|
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7342
7095
|
const registry = Registry.loadEmbedded();
|
|
7343
7096
|
const chain = registry.getChain(chainName);
|
|
7097
|
+
const rpcUrl = chain.effectiveRpcUrl();
|
|
7344
7098
|
const protocol = registry.getProtocol(opts.protocol);
|
|
7345
|
-
const
|
|
7099
|
+
const account = resolveAccount(opts.address);
|
|
7100
|
+
const iface = protocol.interface;
|
|
7101
|
+
if (iface === "algebra_v3" && protocol.contracts?.["farming_center"]) {
|
|
7102
|
+
if (!opts.pool) throw new Error("--pool is required for KittenSwap farming claim");
|
|
7103
|
+
if (!opts.tokenId) throw new Error("--token-id is required for KittenSwap farming claim");
|
|
7104
|
+
const adapter = createKittenSwapFarming(protocol, rpcUrl);
|
|
7105
|
+
const tx = await adapter.buildCollectRewards(
|
|
7106
|
+
BigInt(opts.tokenId),
|
|
7107
|
+
opts.pool,
|
|
7108
|
+
account
|
|
7109
|
+
);
|
|
7110
|
+
const result = await executor.execute(tx);
|
|
7111
|
+
printOutput(result, getOpts());
|
|
7112
|
+
return;
|
|
7113
|
+
}
|
|
7114
|
+
if (iface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
|
|
7115
|
+
if (!opts.pool) throw new Error("--pool is required for Merchant Moe LB claim");
|
|
7116
|
+
const adapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
7117
|
+
const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
|
|
7118
|
+
const tx = await adapter.buildClaimRewards(account, opts.pool, binIds);
|
|
7119
|
+
const result = await executor.execute(tx);
|
|
7120
|
+
printOutput(result, getOpts());
|
|
7121
|
+
return;
|
|
7122
|
+
}
|
|
7123
|
+
if (["solidly_v2", "solidly_cl", "algebra_v3", "hybra"].includes(iface)) {
|
|
7124
|
+
if (!opts.gauge) throw new Error("--gauge is required for gauge claim");
|
|
7125
|
+
const adapter = createGauge(protocol, rpcUrl);
|
|
7126
|
+
let tx;
|
|
7127
|
+
if (opts.tokenId) {
|
|
7128
|
+
if (!adapter.buildClaimRewardsByTokenId) throw new Error(`${protocol.name} does not support NFT-based claim`);
|
|
7129
|
+
tx = await adapter.buildClaimRewardsByTokenId(opts.gauge, BigInt(opts.tokenId));
|
|
7130
|
+
} else {
|
|
7131
|
+
tx = await adapter.buildClaimRewards(opts.gauge, account);
|
|
7132
|
+
}
|
|
7133
|
+
const result = await executor.execute(tx);
|
|
7134
|
+
printOutput(result, getOpts());
|
|
7135
|
+
return;
|
|
7136
|
+
}
|
|
7137
|
+
throw new Error(`No claim method found for protocol interface '${iface}'`);
|
|
7138
|
+
});
|
|
7139
|
+
lp.command("remove").description("Auto-unstake (if staked) and remove liquidity from a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-a <token>", "First token symbol or address").requiredOption("--token-b <token>", "Second token symbol or address").requiredOption("--liquidity <amount>", "Liquidity amount to remove in wei").option("--pool <address>", "Pool address (needed to resolve gauge)").option("--gauge <address>", "Gauge contract address (for solidly/hybra unstake)").option("--token-id <id>", "NFT tokenId (for CL gauge or farming positions)").option("--recipient <address>", "Recipient address").action(async (opts) => {
|
|
7140
|
+
const executor = makeExecutor2();
|
|
7141
|
+
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7142
|
+
const registry = Registry.loadEmbedded();
|
|
7143
|
+
const chain = registry.getChain(chainName);
|
|
7144
|
+
const rpcUrl = chain.effectiveRpcUrl();
|
|
7145
|
+
const protocol = registry.getProtocol(opts.protocol);
|
|
7146
|
+
const iface = protocol.interface;
|
|
7147
|
+
const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
|
|
7346
7148
|
const tokenA = opts.tokenA.startsWith("0x") ? opts.tokenA : registry.resolveToken(chainName, opts.tokenA).address;
|
|
7347
7149
|
const tokenB = opts.tokenB.startsWith("0x") ? opts.tokenB : registry.resolveToken(chainName, opts.tokenB).address;
|
|
7348
|
-
const
|
|
7349
|
-
|
|
7150
|
+
const poolAddr = opts.pool ? opts.pool : void 0;
|
|
7151
|
+
let didUnstake = false;
|
|
7152
|
+
if (iface === "algebra_v3" && protocol.contracts?.["farming_center"] && opts.tokenId && poolAddr) {
|
|
7153
|
+
process.stderr.write("Step 1/2: Exiting KittenSwap farming...\n");
|
|
7154
|
+
const farmAdapter = createKittenSwapFarming(protocol, rpcUrl);
|
|
7155
|
+
const exitTx = await farmAdapter.buildExitFarming(BigInt(opts.tokenId), poolAddr);
|
|
7156
|
+
const exitResult = await executor.execute(exitTx);
|
|
7157
|
+
printOutput({ step: "unstake_farming", ...exitResult }, getOpts());
|
|
7158
|
+
if (exitResult.status !== "confirmed" && exitResult.status !== "simulated") {
|
|
7159
|
+
process.stderr.write("Step 2/2: Skipped \u2014 unstake did not succeed.\n");
|
|
7160
|
+
return;
|
|
7161
|
+
}
|
|
7162
|
+
didUnstake = true;
|
|
7163
|
+
} else if (["solidly_v2", "solidly_cl", "hybra"].includes(iface)) {
|
|
7164
|
+
let gaugeAddr = opts.gauge;
|
|
7165
|
+
if (!gaugeAddr && poolAddr) {
|
|
7166
|
+
try {
|
|
7167
|
+
const gaugeAdapter = createGauge(protocol, rpcUrl);
|
|
7168
|
+
if (gaugeAdapter.resolveGauge) {
|
|
7169
|
+
gaugeAddr = await gaugeAdapter.resolveGauge(poolAddr);
|
|
7170
|
+
}
|
|
7171
|
+
} catch {
|
|
7172
|
+
}
|
|
7173
|
+
}
|
|
7174
|
+
if (gaugeAddr) {
|
|
7175
|
+
process.stderr.write("Step 1/2: Withdrawing from gauge...\n");
|
|
7176
|
+
const gaugeAdapter = createGauge(protocol, rpcUrl);
|
|
7177
|
+
const tokenId = opts.tokenId ? BigInt(opts.tokenId) : void 0;
|
|
7178
|
+
const withdrawTx = await gaugeAdapter.buildWithdraw(gaugeAddr, BigInt(opts.liquidity), tokenId);
|
|
7179
|
+
const withdrawResult = await executor.execute(withdrawTx);
|
|
7180
|
+
printOutput({ step: "unstake_gauge", ...withdrawResult }, getOpts());
|
|
7181
|
+
if (withdrawResult.status !== "confirmed" && withdrawResult.status !== "simulated") {
|
|
7182
|
+
process.stderr.write("Step 2/2: Skipped \u2014 unstake did not succeed.\n");
|
|
7183
|
+
return;
|
|
7184
|
+
}
|
|
7185
|
+
didUnstake = true;
|
|
7186
|
+
}
|
|
7187
|
+
}
|
|
7188
|
+
if (!didUnstake) {
|
|
7189
|
+
process.stderr.write("Step 1/2: No staking detected \u2014 skipping unstake.\n");
|
|
7190
|
+
}
|
|
7191
|
+
process.stderr.write("Step 2/2: Removing liquidity...\n");
|
|
7192
|
+
const dexAdapter = createDex(protocol, rpcUrl);
|
|
7193
|
+
const removeTx = await dexAdapter.buildRemoveLiquidity({
|
|
7350
7194
|
protocol: protocol.name,
|
|
7351
7195
|
token_a: tokenA,
|
|
7352
7196
|
token_b: tokenB,
|
|
7353
7197
|
liquidity: BigInt(opts.liquidity),
|
|
7354
7198
|
recipient
|
|
7355
7199
|
});
|
|
7356
|
-
const
|
|
7357
|
-
printOutput(
|
|
7200
|
+
const removeResult = await executor.execute(removeTx);
|
|
7201
|
+
printOutput({ step: "lp_remove", ...removeResult }, getOpts());
|
|
7358
7202
|
});
|
|
7359
|
-
|
|
7203
|
+
lp.command("positions").description("Show all LP positions across protocols").option("--protocol <protocol>", "Filter to a single protocol slug").option("--pool <address>", "Filter to a specific pool address").option("--bins <binIds>", "Comma-separated bin IDs (for Merchant Moe LB, auto-detected if omitted)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
|
|
7360
7204
|
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7361
7205
|
const registry = Registry.loadEmbedded();
|
|
7362
7206
|
const chain = registry.getChain(chainName);
|
|
7363
|
-
const
|
|
7364
|
-
const
|
|
7365
|
-
const
|
|
7207
|
+
const rpcUrl = chain.effectiveRpcUrl();
|
|
7208
|
+
const user = resolveAccount(opts.address);
|
|
7209
|
+
const allProtocols = registry.getProtocolsForChain(chainName);
|
|
7210
|
+
const protocols = opts.protocol ? [registry.getProtocol(opts.protocol)] : allProtocols;
|
|
7366
7211
|
const results = [];
|
|
7367
|
-
await Promise.
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
}
|
|
7390
|
-
function registerGauge(parent, getOpts, makeExecutor2) {
|
|
7391
|
-
const gauge = parent.command("gauge").description("Gauge operations: find, deposit, withdraw, claim, earned");
|
|
7392
|
-
gauge.command("discover").description("Find all pools with emission gauges (scans V2 + CL factories)").requiredOption("--protocol <protocol>", "Protocol slug").action(async (opts) => {
|
|
7393
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7394
|
-
const registry = Registry.loadEmbedded();
|
|
7395
|
-
const chain = registry.getChain(chainName);
|
|
7396
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
7397
|
-
const adapter = createGauge(protocol, chain.effectiveRpcUrl());
|
|
7398
|
-
if (!adapter.discoverGaugedPools) throw new Error(`${protocol.name} does not support gauge discovery`);
|
|
7399
|
-
const pools = await adapter.discoverGaugedPools();
|
|
7400
|
-
printOutput(pools, getOpts());
|
|
7401
|
-
});
|
|
7402
|
-
gauge.command("find").description("Find gauge address for a pool via voter contract").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pool <address>", "Pool address").action(async (opts) => {
|
|
7403
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7404
|
-
const registry = Registry.loadEmbedded();
|
|
7405
|
-
const chain = registry.getChain(chainName);
|
|
7406
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
7407
|
-
const adapter = createGauge(protocol, chain.effectiveRpcUrl());
|
|
7408
|
-
if (!adapter.resolveGauge) throw new Error(`${protocol.name} does not support gauge lookup`);
|
|
7409
|
-
const gaugeAddr = await adapter.resolveGauge(opts.pool);
|
|
7410
|
-
printOutput({ pool: opts.pool, gauge: gaugeAddr, protocol: protocol.name }, getOpts());
|
|
7411
|
-
});
|
|
7412
|
-
gauge.command("earned").description("Check pending rewards for a gauge").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--gauge <address>", "Gauge contract address").option("--token-id <id>", "NFT tokenId (for CL gauges like Hybra)").action(async (opts) => {
|
|
7413
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7414
|
-
const registry = Registry.loadEmbedded();
|
|
7415
|
-
const chain = registry.getChain(chainName);
|
|
7416
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
7417
|
-
const adapter = createGauge(protocol, chain.effectiveRpcUrl());
|
|
7418
|
-
if (opts.tokenId) {
|
|
7419
|
-
if (!adapter.getPendingRewardsByTokenId) throw new Error(`${protocol.name} does not support NFT rewards`);
|
|
7420
|
-
const earned = await adapter.getPendingRewardsByTokenId(opts.gauge, BigInt(opts.tokenId));
|
|
7421
|
-
printOutput({ gauge: opts.gauge, token_id: opts.tokenId, earned: earned.toString() }, getOpts());
|
|
7422
|
-
} else {
|
|
7423
|
-
const account = resolveAccount();
|
|
7424
|
-
if (!account) throw new Error("DEFI_WALLET_ADDRESS or DEFI_PRIVATE_KEY required");
|
|
7425
|
-
const rewards = await adapter.getPendingRewards(opts.gauge, account);
|
|
7426
|
-
printOutput(rewards.map((r) => ({ token: r.token, amount: r.amount.toString() })), getOpts());
|
|
7427
|
-
}
|
|
7428
|
-
});
|
|
7429
|
-
gauge.command("deposit").description("Deposit LP tokens or NFT into a gauge").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--gauge <address>", "Gauge contract address").option("--amount <amount>", "LP token amount in wei (for V2 gauges)").option("--token-id <id>", "NFT tokenId (for CL gauges like Hybra)").action(async (opts) => {
|
|
7430
|
-
const executor = makeExecutor2();
|
|
7431
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7432
|
-
const registry = Registry.loadEmbedded();
|
|
7433
|
-
const chain = registry.getChain(chainName);
|
|
7434
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
7435
|
-
const adapter = createGauge(protocol, chain.effectiveRpcUrl());
|
|
7436
|
-
const amount = opts.amount ? BigInt(opts.amount) : 0n;
|
|
7437
|
-
const tokenId = opts.tokenId ? BigInt(opts.tokenId) : void 0;
|
|
7438
|
-
const tx = await adapter.buildDeposit(opts.gauge, amount, tokenId);
|
|
7439
|
-
const result = await executor.execute(tx);
|
|
7440
|
-
printOutput(result, getOpts());
|
|
7441
|
-
});
|
|
7442
|
-
gauge.command("withdraw").description("Withdraw LP tokens or NFT from a gauge").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--gauge <address>", "Gauge contract address").option("--amount <amount>", "LP token amount in wei (for V2 gauges)").option("--token-id <id>", "NFT tokenId (for CL gauges like Hybra)").action(async (opts) => {
|
|
7443
|
-
const executor = makeExecutor2();
|
|
7444
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7445
|
-
const registry = Registry.loadEmbedded();
|
|
7446
|
-
const chain = registry.getChain(chainName);
|
|
7447
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
7448
|
-
const adapter = createGauge(protocol, chain.effectiveRpcUrl());
|
|
7449
|
-
const amount = opts.amount ? BigInt(opts.amount) : 0n;
|
|
7450
|
-
const tokenId = opts.tokenId ? BigInt(opts.tokenId) : void 0;
|
|
7451
|
-
const tx = await adapter.buildWithdraw(opts.gauge, amount, tokenId);
|
|
7452
|
-
const result = await executor.execute(tx);
|
|
7453
|
-
printOutput(result, getOpts());
|
|
7454
|
-
});
|
|
7455
|
-
gauge.command("claim").description("Claim earned rewards from a gauge").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--gauge <address>", "Gauge contract address").option("--token-id <id>", "NFT tokenId (for CL gauges like Hybra)").action(async (opts) => {
|
|
7456
|
-
const executor = makeExecutor2();
|
|
7457
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7458
|
-
const registry = Registry.loadEmbedded();
|
|
7459
|
-
const chain = registry.getChain(chainName);
|
|
7460
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
7461
|
-
const adapter = createGauge(protocol, chain.effectiveRpcUrl());
|
|
7462
|
-
if (opts.tokenId) {
|
|
7463
|
-
if (!adapter.buildClaimRewardsByTokenId) throw new Error(`${protocol.name} does not support NFT claim`);
|
|
7464
|
-
const tx = await adapter.buildClaimRewardsByTokenId(opts.gauge, BigInt(opts.tokenId));
|
|
7465
|
-
const result = await executor.execute(tx);
|
|
7466
|
-
printOutput(result, getOpts());
|
|
7467
|
-
} else {
|
|
7468
|
-
const account = resolveAccount();
|
|
7469
|
-
const tx = await adapter.buildClaimRewards(opts.gauge, account);
|
|
7470
|
-
const result = await executor.execute(tx);
|
|
7471
|
-
printOutput(result, getOpts());
|
|
7472
|
-
}
|
|
7212
|
+
await Promise.allSettled(
|
|
7213
|
+
protocols.map(async (protocol) => {
|
|
7214
|
+
try {
|
|
7215
|
+
if (protocol.interface === "uniswap_v2" && protocol.contracts?.["lb_factory"]) {
|
|
7216
|
+
if (!opts.pool) return;
|
|
7217
|
+
const adapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
7218
|
+
const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
|
|
7219
|
+
const positions = await adapter.getUserPositions(user, opts.pool, binIds);
|
|
7220
|
+
for (const pos of positions) {
|
|
7221
|
+
results.push({
|
|
7222
|
+
protocol: protocol.slug,
|
|
7223
|
+
type: "lb",
|
|
7224
|
+
pool: opts.pool,
|
|
7225
|
+
...pos
|
|
7226
|
+
});
|
|
7227
|
+
}
|
|
7228
|
+
}
|
|
7229
|
+
} catch {
|
|
7230
|
+
}
|
|
7231
|
+
})
|
|
7232
|
+
);
|
|
7233
|
+
printOutput(results, getOpts());
|
|
7473
7234
|
});
|
|
7474
7235
|
}
|
|
7475
7236
|
|
|
@@ -7631,44 +7392,6 @@ function registerCdp(parent, getOpts, makeExecutor2) {
|
|
|
7631
7392
|
});
|
|
7632
7393
|
}
|
|
7633
7394
|
|
|
7634
|
-
// src/commands/staking.ts
|
|
7635
|
-
function registerStaking(parent, getOpts, makeExecutor2) {
|
|
7636
|
-
const staking = parent.command("staking").description("Liquid staking: stake, unstake, info");
|
|
7637
|
-
staking.command("stake").description("Stake tokens via liquid staking").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--amount <amount>", "Amount in wei").option("--recipient <address>", "Recipient address").action(async (opts) => {
|
|
7638
|
-
const executor = makeExecutor2();
|
|
7639
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7640
|
-
const registry = Registry.loadEmbedded();
|
|
7641
|
-
const chain = registry.getChain(chainName);
|
|
7642
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
7643
|
-
const adapter = createLiquidStaking(protocol, chain.effectiveRpcUrl());
|
|
7644
|
-
const recipient = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
|
|
7645
|
-
const tx = await adapter.buildStake({ protocol: protocol.name, amount: BigInt(opts.amount), recipient });
|
|
7646
|
-
const result = await executor.execute(tx);
|
|
7647
|
-
printOutput(result, getOpts());
|
|
7648
|
-
});
|
|
7649
|
-
staking.command("unstake").description("Unstake tokens").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--amount <amount>", "Amount in wei").option("--recipient <address>", "Recipient address").action(async (opts) => {
|
|
7650
|
-
const executor = makeExecutor2();
|
|
7651
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7652
|
-
const registry = Registry.loadEmbedded();
|
|
7653
|
-
const chain = registry.getChain(chainName);
|
|
7654
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
7655
|
-
const adapter = createLiquidStaking(protocol, chain.effectiveRpcUrl());
|
|
7656
|
-
const recipient = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
|
|
7657
|
-
const tx = await adapter.buildUnstake({ protocol: protocol.name, amount: BigInt(opts.amount), recipient });
|
|
7658
|
-
const result = await executor.execute(tx);
|
|
7659
|
-
printOutput(result, getOpts());
|
|
7660
|
-
});
|
|
7661
|
-
staking.command("info").description("Show staking info and rates").requiredOption("--protocol <protocol>", "Protocol slug").action(async (opts) => {
|
|
7662
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
7663
|
-
const registry = Registry.loadEmbedded();
|
|
7664
|
-
const chain = registry.getChain(chainName);
|
|
7665
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
7666
|
-
const adapter = createLiquidStaking(protocol, chain.effectiveRpcUrl());
|
|
7667
|
-
const info = await adapter.getInfo();
|
|
7668
|
-
printOutput(info, getOpts());
|
|
7669
|
-
});
|
|
7670
|
-
}
|
|
7671
|
-
|
|
7672
7395
|
// src/commands/vault.ts
|
|
7673
7396
|
function registerVault(parent, getOpts, makeExecutor2) {
|
|
7674
7397
|
const vault = parent.command("vault").description("Vault operations: deposit, withdraw, info");
|
|
@@ -9412,53 +9135,6 @@ async function runAllChains(registry, patterns, oracleThreshold, stableThreshold
|
|
|
9412
9135
|
};
|
|
9413
9136
|
}
|
|
9414
9137
|
|
|
9415
|
-
// src/commands/arb.ts
|
|
9416
|
-
function registerArb(parent, getOpts, makeExecutor2) {
|
|
9417
|
-
parent.command("arb").description("Detect arbitrage opportunities across DEXes").option("--token-in <token>", "Base token (default: WHYPE)", "WHYPE").option("--token-out <token>", "Quote token (default: USDC)", "USDC").option("--amount <amount>", "Test amount in wei", "1000000000000000000").option("--execute", "Execute best arb (default: analysis only)").option("--min-profit <bps>", "Min profit in bps to execute", "10").action(async (opts) => {
|
|
9418
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
9419
|
-
const registry = Registry.loadEmbedded();
|
|
9420
|
-
const chain = registry.getChain(chainName);
|
|
9421
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
9422
|
-
const tokenIn = opts.tokenIn.startsWith("0x") ? opts.tokenIn : registry.resolveToken(chainName, opts.tokenIn).address;
|
|
9423
|
-
const tokenOut = opts.tokenOut.startsWith("0x") ? opts.tokenOut : registry.resolveToken(chainName, opts.tokenOut).address;
|
|
9424
|
-
const amountIn = BigInt(opts.amount);
|
|
9425
|
-
const dexProtocols = registry.getProtocolsByCategory("dex").filter((p) => p.chain === chainName);
|
|
9426
|
-
const quotes = [];
|
|
9427
|
-
for (const p of dexProtocols) {
|
|
9428
|
-
try {
|
|
9429
|
-
const adapter = createDex(p, rpcUrl);
|
|
9430
|
-
const buyQuote = await adapter.quote({ protocol: p.name, token_in: tokenIn, token_out: tokenOut, amount_in: amountIn });
|
|
9431
|
-
if (buyQuote.amount_out === 0n) continue;
|
|
9432
|
-
const sellQuote = await adapter.quote({ protocol: p.name, token_in: tokenOut, token_out: tokenIn, amount_in: buyQuote.amount_out });
|
|
9433
|
-
const profitBps = Number((sellQuote.amount_out - amountIn) * 10000n / amountIn);
|
|
9434
|
-
quotes.push({ protocol: p.name, buy: buyQuote.amount_out, sell: sellQuote.amount_out, profit_bps: profitBps });
|
|
9435
|
-
} catch {
|
|
9436
|
-
}
|
|
9437
|
-
}
|
|
9438
|
-
const opportunities = [];
|
|
9439
|
-
for (let i = 0; i < quotes.length; i++) {
|
|
9440
|
-
for (let j = 0; j < quotes.length; j++) {
|
|
9441
|
-
if (i === j) continue;
|
|
9442
|
-
const buyAmount = quotes[i].buy;
|
|
9443
|
-
const sellAmount = quotes[j].sell;
|
|
9444
|
-
if (sellAmount > amountIn) {
|
|
9445
|
-
const profitBps = Number((sellAmount - amountIn) * 10000n / amountIn);
|
|
9446
|
-
opportunities.push({ buy_on: quotes[i].protocol, sell_on: quotes[j].protocol, profit_bps: profitBps });
|
|
9447
|
-
}
|
|
9448
|
-
}
|
|
9449
|
-
}
|
|
9450
|
-
opportunities.sort((a, b) => b.profit_bps - a.profit_bps);
|
|
9451
|
-
printOutput({
|
|
9452
|
-
chain: chainName,
|
|
9453
|
-
token_in: tokenIn,
|
|
9454
|
-
token_out: tokenOut,
|
|
9455
|
-
amount_in: amountIn,
|
|
9456
|
-
single_dex: quotes,
|
|
9457
|
-
cross_dex_opportunities: opportunities.slice(0, 5)
|
|
9458
|
-
}, getOpts());
|
|
9459
|
-
});
|
|
9460
|
-
}
|
|
9461
|
-
|
|
9462
9138
|
// src/commands/positions.ts
|
|
9463
9139
|
import { encodeFunctionData as encodeFunctionData30, parseAbi as parseAbi34 } from "viem";
|
|
9464
9140
|
var ERC20_ABI6 = parseAbi34([
|
|
@@ -10071,207 +9747,6 @@ function registerWhales(parent, getOpts) {
|
|
|
10071
9747
|
});
|
|
10072
9748
|
}
|
|
10073
9749
|
|
|
10074
|
-
// src/commands/compare.ts
|
|
10075
|
-
import { spawnSync } from "child_process";
|
|
10076
|
-
function round25(x) {
|
|
10077
|
-
return Math.round(x * 100) / 100;
|
|
10078
|
-
}
|
|
10079
|
-
async function fetchPerpRates() {
|
|
10080
|
-
let result = spawnSync("perp", ["--json", "arb", "scan", "--rates"], { encoding: "utf8", timeout: 3e4 });
|
|
10081
|
-
if (result.error || result.status !== 0) {
|
|
10082
|
-
result = spawnSync("npx", ["-y", "perp-cli@latest", "--json", "arb", "scan", "--rates"], {
|
|
10083
|
-
encoding: "utf8",
|
|
10084
|
-
timeout: 6e4
|
|
10085
|
-
});
|
|
10086
|
-
}
|
|
10087
|
-
if (result.error || result.status !== 0) {
|
|
10088
|
-
throw new Error("perp-cli not found or failed");
|
|
10089
|
-
}
|
|
10090
|
-
let data;
|
|
10091
|
-
try {
|
|
10092
|
-
data = JSON.parse(result.stdout);
|
|
10093
|
-
} catch {
|
|
10094
|
-
throw new Error("perp JSON parse error");
|
|
10095
|
-
}
|
|
10096
|
-
const d = data;
|
|
10097
|
-
const symbolsRaw = d["data"]?.["symbols"] ?? d["symbols"];
|
|
10098
|
-
const symbols = Array.isArray(symbolsRaw) ? symbolsRaw : [];
|
|
10099
|
-
const results = [];
|
|
10100
|
-
for (const sym of symbols) {
|
|
10101
|
-
const symbol = sym["symbol"] ?? "?";
|
|
10102
|
-
const maxSpread = sym["maxSpreadAnnual"] ?? 0;
|
|
10103
|
-
const longEx = sym["longExchange"] ?? "?";
|
|
10104
|
-
const shortEx = sym["shortExchange"] ?? "?";
|
|
10105
|
-
if (Math.abs(maxSpread) > 0) {
|
|
10106
|
-
results.push({
|
|
10107
|
-
type: "perp_funding",
|
|
10108
|
-
asset: symbol,
|
|
10109
|
-
apy: round25(maxSpread),
|
|
10110
|
-
detail: `long ${longEx} / short ${shortEx}`,
|
|
10111
|
-
risk: Math.abs(maxSpread) > 50 ? "high" : Math.abs(maxSpread) > 20 ? "medium" : "low",
|
|
10112
|
-
source: "perp-cli"
|
|
10113
|
-
});
|
|
10114
|
-
}
|
|
10115
|
-
const rates = Array.isArray(sym["rates"]) ? sym["rates"] : [];
|
|
10116
|
-
for (const rate of rates) {
|
|
10117
|
-
const exchange = rate["exchange"] ?? "?";
|
|
10118
|
-
const annual = rate["annualizedPct"] ?? 0;
|
|
10119
|
-
if (Math.abs(annual) > 1) {
|
|
10120
|
-
results.push({
|
|
10121
|
-
type: "perp_rate",
|
|
10122
|
-
asset: symbol,
|
|
10123
|
-
apy: round25(annual),
|
|
10124
|
-
detail: exchange,
|
|
10125
|
-
risk: Math.abs(annual) > 50 ? "high" : Math.abs(annual) > 20 ? "medium" : "low",
|
|
10126
|
-
source: "perp-cli"
|
|
10127
|
-
});
|
|
10128
|
-
}
|
|
10129
|
-
}
|
|
10130
|
-
}
|
|
10131
|
-
return results;
|
|
10132
|
-
}
|
|
10133
|
-
async function fetchLendingRates(registry, asset) {
|
|
10134
|
-
const chainKeys = Array.from(registry.chains.keys());
|
|
10135
|
-
const tasks = chainKeys.map(async (ck) => {
|
|
10136
|
-
try {
|
|
10137
|
-
const chain = registry.getChain(ck);
|
|
10138
|
-
const chainName = chain.name.toLowerCase();
|
|
10139
|
-
let assetAddr;
|
|
10140
|
-
try {
|
|
10141
|
-
assetAddr = registry.resolveToken(chainName, asset).address;
|
|
10142
|
-
} catch {
|
|
10143
|
-
return [];
|
|
10144
|
-
}
|
|
10145
|
-
const protos = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending && p.interface === "aave_v3");
|
|
10146
|
-
if (protos.length === 0) return [];
|
|
10147
|
-
const rpc = chain.effectiveRpcUrl();
|
|
10148
|
-
const rates = [];
|
|
10149
|
-
for (const proto of protos) {
|
|
10150
|
-
try {
|
|
10151
|
-
const lending = createLending(proto, rpc);
|
|
10152
|
-
const r = await lending.getRates(assetAddr);
|
|
10153
|
-
if (r.supply_apy > 0) {
|
|
10154
|
-
rates.push({
|
|
10155
|
-
type: "lending_supply",
|
|
10156
|
-
asset,
|
|
10157
|
-
apy: round25(r.supply_apy * 100),
|
|
10158
|
-
detail: `${r.protocol} (${chain.name})`,
|
|
10159
|
-
risk: "low",
|
|
10160
|
-
source: "defi-cli"
|
|
10161
|
-
});
|
|
10162
|
-
}
|
|
10163
|
-
} catch {
|
|
10164
|
-
}
|
|
10165
|
-
}
|
|
10166
|
-
return rates;
|
|
10167
|
-
} catch {
|
|
10168
|
-
return [];
|
|
10169
|
-
}
|
|
10170
|
-
});
|
|
10171
|
-
const nested = await Promise.all(tasks);
|
|
10172
|
-
return nested.flat();
|
|
10173
|
-
}
|
|
10174
|
-
function registerCompare(parent, getOpts) {
|
|
10175
|
-
parent.command("compare").description("Compare all yield sources: perp funding vs lending APY vs staking").option("--asset <token>", "Token symbol to compare (e.g. USDC, ETH)", "USDC").option("--no-perps", "Exclude perp funding rates").option("--no-lending", "Exclude lending rates").option("--min-apy <pct>", "Minimum absolute APY to show", "1.0").action(async (opts) => {
|
|
10176
|
-
try {
|
|
10177
|
-
const registry = Registry.loadEmbedded();
|
|
10178
|
-
const asset = opts.asset ?? "USDC";
|
|
10179
|
-
const includePerps = opts.perps !== false;
|
|
10180
|
-
const includeLending = opts.lending !== false;
|
|
10181
|
-
const minApy = parseFloat(opts.minApy ?? "1.0");
|
|
10182
|
-
const t0 = Date.now();
|
|
10183
|
-
const opportunities = [];
|
|
10184
|
-
if (includePerps) {
|
|
10185
|
-
try {
|
|
10186
|
-
const perpData = await fetchPerpRates();
|
|
10187
|
-
for (const opp of perpData) {
|
|
10188
|
-
const apy = Math.abs(opp["apy"] ?? 0);
|
|
10189
|
-
if (apy >= minApy) opportunities.push(opp);
|
|
10190
|
-
}
|
|
10191
|
-
} catch {
|
|
10192
|
-
}
|
|
10193
|
-
}
|
|
10194
|
-
if (includeLending) {
|
|
10195
|
-
const lendingData = await fetchLendingRates(registry, asset);
|
|
10196
|
-
for (const opp of lendingData) {
|
|
10197
|
-
const apy = Math.abs(opp["apy"] ?? 0);
|
|
10198
|
-
if (apy >= minApy) opportunities.push(opp);
|
|
10199
|
-
}
|
|
10200
|
-
}
|
|
10201
|
-
opportunities.sort((a, b) => {
|
|
10202
|
-
const aApy = Math.abs(a["apy"] ?? 0);
|
|
10203
|
-
const bApy = Math.abs(b["apy"] ?? 0);
|
|
10204
|
-
return bApy - aApy;
|
|
10205
|
-
});
|
|
10206
|
-
const scanMs = Date.now() - t0;
|
|
10207
|
-
printOutput(
|
|
10208
|
-
{
|
|
10209
|
-
asset,
|
|
10210
|
-
scan_duration_ms: scanMs,
|
|
10211
|
-
total_opportunities: opportunities.length,
|
|
10212
|
-
opportunities
|
|
10213
|
-
},
|
|
10214
|
-
getOpts()
|
|
10215
|
-
);
|
|
10216
|
-
} catch (err) {
|
|
10217
|
-
printOutput({ error: String(err) }, getOpts());
|
|
10218
|
-
process.exit(1);
|
|
10219
|
-
}
|
|
10220
|
-
});
|
|
10221
|
-
}
|
|
10222
|
-
|
|
10223
|
-
// src/commands/swap.ts
|
|
10224
|
-
var ODOS_API = "https://api.odos.xyz";
|
|
10225
|
-
function registerSwap(parent, getOpts, makeExecutor2) {
|
|
10226
|
-
parent.command("swap").description("Aggregator swap: best price across all DEXes (ODOS)").requiredOption("--token-in <token>", "Input token symbol or address").requiredOption("--token-out <token>", "Output token symbol or address").requiredOption("--amount <amount>", "Amount of input token in wei").option("--slippage <bps>", "Slippage tolerance in basis points", "50").option("--recipient <address>", "Recipient address").action(async (opts) => {
|
|
10227
|
-
const executor = makeExecutor2();
|
|
10228
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
10229
|
-
const registry = Registry.loadEmbedded();
|
|
10230
|
-
const chain = registry.getChain(chainName);
|
|
10231
|
-
const tokenIn = opts.tokenIn.startsWith("0x") ? opts.tokenIn : registry.resolveToken(chainName, opts.tokenIn).address;
|
|
10232
|
-
const tokenOut = opts.tokenOut.startsWith("0x") ? opts.tokenOut : registry.resolveToken(chainName, opts.tokenOut).address;
|
|
10233
|
-
const sender = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
|
|
10234
|
-
try {
|
|
10235
|
-
const quoteRes = await fetch(`${ODOS_API}/sor/quote/v2`, {
|
|
10236
|
-
method: "POST",
|
|
10237
|
-
headers: { "Content-Type": "application/json" },
|
|
10238
|
-
body: JSON.stringify({
|
|
10239
|
-
chainId: chain.chain_id,
|
|
10240
|
-
inputTokens: [{ tokenAddress: tokenIn, amount: opts.amount }],
|
|
10241
|
-
outputTokens: [{ tokenAddress: tokenOut, proportion: 1 }],
|
|
10242
|
-
slippageLimitPercent: parseInt(opts.slippage) / 100,
|
|
10243
|
-
userAddr: sender
|
|
10244
|
-
})
|
|
10245
|
-
});
|
|
10246
|
-
const quote = await quoteRes.json();
|
|
10247
|
-
if (!quote.pathId) {
|
|
10248
|
-
printOutput({ error: "No ODOS route found", quote }, getOpts());
|
|
10249
|
-
return;
|
|
10250
|
-
}
|
|
10251
|
-
const assembleRes = await fetch(`${ODOS_API}/sor/assemble`, {
|
|
10252
|
-
method: "POST",
|
|
10253
|
-
headers: { "Content-Type": "application/json" },
|
|
10254
|
-
body: JSON.stringify({ pathId: quote.pathId, userAddr: sender })
|
|
10255
|
-
});
|
|
10256
|
-
const assembled = await assembleRes.json();
|
|
10257
|
-
if (assembled.transaction) {
|
|
10258
|
-
const tx = {
|
|
10259
|
-
description: `ODOS swap ${tokenIn} \u2192 ${tokenOut}`,
|
|
10260
|
-
to: assembled.transaction.to,
|
|
10261
|
-
data: assembled.transaction.data,
|
|
10262
|
-
value: BigInt(assembled.transaction.value ?? 0)
|
|
10263
|
-
};
|
|
10264
|
-
const result = await executor.execute(tx);
|
|
10265
|
-
printOutput({ ...result, odos_quote: quote }, getOpts());
|
|
10266
|
-
} else {
|
|
10267
|
-
printOutput({ error: "ODOS assembly failed", assembled }, getOpts());
|
|
10268
|
-
}
|
|
10269
|
-
} catch (e) {
|
|
10270
|
-
printOutput({ error: `ODOS API error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
|
|
10271
|
-
}
|
|
10272
|
-
});
|
|
10273
|
-
}
|
|
10274
|
-
|
|
10275
9750
|
// src/commands/bridge.ts
|
|
10276
9751
|
var LIFI_API = "https://li.quest/v1";
|
|
10277
9752
|
var DLN_API = "https://dln.debridge.finance/v1.0/dln/order";
|
|
@@ -10497,249 +9972,208 @@ function registerBridge(parent, getOpts) {
|
|
|
10497
9972
|
});
|
|
10498
9973
|
}
|
|
10499
9974
|
|
|
10500
|
-
// src/commands/
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
const entry = nftProtocols[0] ?? { name: "ERC721", slug: "erc721", category: "nft", interface: "erc721", chain: chainName, contracts: { collection: opts.collection } };
|
|
10523
|
-
try {
|
|
10524
|
-
const adapter = createNft(entry, chain.effectiveRpcUrl());
|
|
10525
|
-
const info = await adapter.getTokenInfo(opts.collection, BigInt(opts.tokenId));
|
|
10526
|
-
printOutput(info, getOpts());
|
|
10527
|
-
} catch (e) {
|
|
10528
|
-
printOutput({ error: e instanceof Error ? e.message : String(e) }, getOpts());
|
|
10529
|
-
}
|
|
10530
|
-
});
|
|
10531
|
-
nft.command("balance").description("Check how many NFTs an address holds in a collection").requiredOption("--collection <address>", "NFT collection contract address").requiredOption("--owner <address>", "Owner address to query").action(async (opts) => {
|
|
10532
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
10533
|
-
const registry = Registry.loadEmbedded();
|
|
10534
|
-
const chain = registry.getChain(chainName);
|
|
10535
|
-
const nftProtocols = registry.getProtocolsByCategory("nft").filter((p) => p.chain === chainName);
|
|
10536
|
-
const entry = nftProtocols[0] ?? { name: "ERC721", slug: "erc721", category: "nft", interface: "erc721", chain: chainName, contracts: { collection: opts.collection } };
|
|
10537
|
-
try {
|
|
10538
|
-
const adapter = createNft(entry, chain.effectiveRpcUrl());
|
|
10539
|
-
const balance = await adapter.getBalance(opts.owner, opts.collection);
|
|
10540
|
-
printOutput({ collection: opts.collection, owner: opts.owner, balance }, getOpts());
|
|
10541
|
-
} catch (e) {
|
|
10542
|
-
printOutput({ error: e instanceof Error ? e.message : String(e) }, getOpts());
|
|
10543
|
-
}
|
|
10544
|
-
});
|
|
10545
|
-
nft.command("uri").description("Get token URI for a specific NFT").requiredOption("--collection <address>", "NFT collection contract address").requiredOption("--token-id <id>", "Token ID").action(async (opts) => {
|
|
10546
|
-
const chainName = parent.opts().chain ?? "hyperevm";
|
|
10547
|
-
const registry = Registry.loadEmbedded();
|
|
10548
|
-
const chain = registry.getChain(chainName);
|
|
10549
|
-
const nftProtocols = registry.getProtocolsByCategory("nft").filter((p) => p.chain === chainName);
|
|
10550
|
-
const entry = nftProtocols[0] ?? { name: "ERC721", slug: "erc721", category: "nft", interface: "erc721", chain: chainName, contracts: { collection: opts.collection } };
|
|
10551
|
-
try {
|
|
10552
|
-
const adapter = createNft(entry, chain.effectiveRpcUrl());
|
|
10553
|
-
const info = await adapter.getTokenInfo(opts.collection, BigInt(opts.tokenId));
|
|
10554
|
-
printOutput({ collection: opts.collection, token_id: opts.tokenId, token_uri: info.token_uri }, getOpts());
|
|
10555
|
-
} catch (e) {
|
|
10556
|
-
printOutput({ error: e instanceof Error ? e.message : String(e) }, getOpts());
|
|
10557
|
-
}
|
|
9975
|
+
// src/commands/swap.ts
|
|
9976
|
+
var CHAIN_NAMES = {
|
|
9977
|
+
hyperevm: { kyber: "hyperevm", openocean: "hyperevm" },
|
|
9978
|
+
mantle: { openocean: "mantle" }
|
|
9979
|
+
};
|
|
9980
|
+
var KYBER_API = "https://aggregator-api.kyberswap.com";
|
|
9981
|
+
async function kyberGetQuote(chain, tokenIn, tokenOut, amountIn) {
|
|
9982
|
+
const params = new URLSearchParams({ tokenIn, tokenOut, amountIn });
|
|
9983
|
+
const url = `${KYBER_API}/${chain}/api/v1/routes?${params}`;
|
|
9984
|
+
const res = await fetch(url, { headers: { "x-client-id": "defi-cli" } });
|
|
9985
|
+
if (!res.ok) throw new Error(`KyberSwap quote failed: ${res.status} ${await res.text()}`);
|
|
9986
|
+
const json = await res.json();
|
|
9987
|
+
const data = json.data;
|
|
9988
|
+
if (!data?.routeSummary) throw new Error(`KyberSwap: no route found`);
|
|
9989
|
+
return data;
|
|
9990
|
+
}
|
|
9991
|
+
async function kyberBuildTx(chain, routeSummary, sender, recipient, slippageTolerance) {
|
|
9992
|
+
const url = `${KYBER_API}/${chain}/api/v1/route/build`;
|
|
9993
|
+
const res = await fetch(url, {
|
|
9994
|
+
method: "POST",
|
|
9995
|
+
headers: { "Content-Type": "application/json", "x-client-id": "defi-cli" },
|
|
9996
|
+
body: JSON.stringify({ routeSummary, sender, recipient, slippageTolerance })
|
|
10558
9997
|
});
|
|
9998
|
+
if (!res.ok) throw new Error(`KyberSwap build failed: ${res.status} ${await res.text()}`);
|
|
9999
|
+
const json = await res.json();
|
|
10000
|
+
const data = json.data;
|
|
10001
|
+
if (!data) throw new Error("KyberSwap: no build data");
|
|
10002
|
+
return {
|
|
10003
|
+
to: String(data.routerAddress),
|
|
10004
|
+
data: String(data.data),
|
|
10005
|
+
value: String(data.value ?? "0x0")
|
|
10006
|
+
};
|
|
10559
10007
|
}
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
const chain = registry.getChain(chainName ?? "hyperevm");
|
|
10570
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10571
|
-
const adapter = createMasterChef(protocol, rpcUrl);
|
|
10572
|
-
const tx = await adapter.buildDeposit(
|
|
10573
|
-
protocol.contracts?.["masterchef"],
|
|
10574
|
-
BigInt(opts.amount),
|
|
10575
|
-
BigInt(opts.pid)
|
|
10576
|
-
);
|
|
10577
|
-
const result = await executor.execute(tx);
|
|
10578
|
-
printOutput(result, getOpts());
|
|
10579
|
-
});
|
|
10580
|
-
farm.command("withdraw").description("Withdraw LP tokens from a MasterChef farm").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pid <pid>", "Farm pool ID").requiredOption("--amount <amount>", "LP token amount in wei").action(async (opts) => {
|
|
10581
|
-
const executor = makeExecutor2();
|
|
10582
|
-
const registry = Registry.loadEmbedded();
|
|
10583
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10584
|
-
const chainName = parent.opts().chain;
|
|
10585
|
-
const chain = registry.getChain(chainName ?? "hyperevm");
|
|
10586
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10587
|
-
const adapter = createMasterChef(protocol, rpcUrl);
|
|
10588
|
-
const tx = await adapter.buildWithdrawPid(
|
|
10589
|
-
BigInt(opts.pid),
|
|
10590
|
-
BigInt(opts.amount)
|
|
10591
|
-
);
|
|
10592
|
-
const result = await executor.execute(tx);
|
|
10593
|
-
printOutput(result, getOpts());
|
|
10594
|
-
});
|
|
10595
|
-
farm.command("claim").description("Claim pending rewards from a MasterChef farm").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pid <pid>", "Farm pool ID").action(async (opts) => {
|
|
10596
|
-
const executor = makeExecutor2();
|
|
10597
|
-
const registry = Registry.loadEmbedded();
|
|
10598
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10599
|
-
const chainName = parent.opts().chain;
|
|
10600
|
-
const chain = registry.getChain(chainName ?? "hyperevm");
|
|
10601
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10602
|
-
const adapter = createMasterChef(protocol, rpcUrl);
|
|
10603
|
-
const tx = await adapter.buildClaimRewardsPid(
|
|
10604
|
-
BigInt(opts.pid)
|
|
10605
|
-
);
|
|
10606
|
-
const result = await executor.execute(tx);
|
|
10607
|
-
printOutput(result, getOpts());
|
|
10608
|
-
});
|
|
10609
|
-
farm.command("info").description("Show pending rewards and farm info").requiredOption("--protocol <protocol>", "Protocol slug").option("--pid <pid>", "Farm pool ID (optional)").option("--address <address>", "Wallet address to query (defaults to DEFI_WALLET_ADDRESS env)").action(async (opts) => {
|
|
10610
|
-
const registry = Registry.loadEmbedded();
|
|
10611
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10612
|
-
const chainName = parent.opts().chain;
|
|
10613
|
-
const chain = registry.getChain(chainName ?? "hyperevm");
|
|
10614
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10615
|
-
const adapter = createMasterChef(protocol, rpcUrl);
|
|
10616
|
-
const walletAddress = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
|
|
10617
|
-
if (!walletAddress) {
|
|
10618
|
-
throw new Error("--address or DEFI_WALLET_ADDRESS required");
|
|
10619
|
-
}
|
|
10620
|
-
const masterchef = protocol.contracts?.["masterchef"];
|
|
10621
|
-
const rewards = await adapter.getPendingRewards(masterchef, walletAddress);
|
|
10622
|
-
printOutput(rewards, getOpts());
|
|
10008
|
+
var OPENOCEAN_API = "https://open-api.openocean.finance/v4";
|
|
10009
|
+
async function openoceanSwap(chain, inTokenAddress, outTokenAddress, amountIn, slippagePct, account) {
|
|
10010
|
+
const params = new URLSearchParams({
|
|
10011
|
+
inTokenAddress,
|
|
10012
|
+
outTokenAddress,
|
|
10013
|
+
amount: amountIn,
|
|
10014
|
+
gasPrice: "0.1",
|
|
10015
|
+
slippage: slippagePct,
|
|
10016
|
+
account
|
|
10623
10017
|
});
|
|
10018
|
+
const url = `${OPENOCEAN_API}/${chain}/swap?${params}`;
|
|
10019
|
+
const res = await fetch(url);
|
|
10020
|
+
if (!res.ok) throw new Error(`OpenOcean swap failed: ${res.status} ${await res.text()}`);
|
|
10021
|
+
const json = await res.json();
|
|
10022
|
+
const data = json.data;
|
|
10023
|
+
if (!data) throw new Error("OpenOcean: no swap data");
|
|
10024
|
+
return {
|
|
10025
|
+
to: String(data.to),
|
|
10026
|
+
data: String(data.data),
|
|
10027
|
+
value: String(data.value ?? "0x0"),
|
|
10028
|
+
outAmount: String(data.outAmount ?? "0")
|
|
10029
|
+
};
|
|
10624
10030
|
}
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
const
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
10645
|
-
printOutput(result, getOpts());
|
|
10646
|
-
});
|
|
10647
|
-
farming.command("exit").description("Exit farming: unstake an NFT position").requiredOption("--protocol <protocol>", "Protocol slug (e.g. kittenswap)").requiredOption("--pool <address>", "Pool address").requiredOption("--token-id <id>", "NFT position token ID").action(async (opts) => {
|
|
10648
|
-
const executor = makeExecutor2();
|
|
10649
|
-
const registry = Registry.loadEmbedded();
|
|
10650
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10651
|
-
const chainName = parent.opts().chain;
|
|
10652
|
-
const chain = registry.getChain(chainName ?? "hyperevm");
|
|
10653
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10654
|
-
const adapter = createKittenSwapFarming(protocol, rpcUrl);
|
|
10655
|
-
const tx = await adapter.buildExitFarming(
|
|
10656
|
-
BigInt(opts.tokenId),
|
|
10657
|
-
opts.pool
|
|
10658
|
-
);
|
|
10659
|
-
const result = await executor.execute(tx);
|
|
10660
|
-
printOutput(result, getOpts());
|
|
10661
|
-
});
|
|
10662
|
-
farming.command("rewards").description("Collect + claim farming rewards for a staked position (collectRewards + claimReward multicall)").requiredOption("--protocol <protocol>", "Protocol slug (e.g. kittenswap)").requiredOption("--pool <address>", "Pool address").requiredOption("--token-id <id>", "NFT position token ID").option("--owner <address>", "Owner address to receive claimed rewards (defaults to DEFI_WALLET_ADDRESS or private key address)").action(async (opts) => {
|
|
10663
|
-
const executor = makeExecutor2();
|
|
10664
|
-
const registry = Registry.loadEmbedded();
|
|
10665
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10666
|
-
const chainName = parent.opts().chain;
|
|
10667
|
-
const chain = registry.getChain(chainName ?? "hyperevm");
|
|
10668
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10669
|
-
const adapter = createKittenSwapFarming(protocol, rpcUrl);
|
|
10670
|
-
const owner = resolveOwner(opts.owner);
|
|
10671
|
-
const tx = await adapter.buildCollectRewards(
|
|
10672
|
-
BigInt(opts.tokenId),
|
|
10673
|
-
opts.pool,
|
|
10674
|
-
owner
|
|
10675
|
-
);
|
|
10676
|
-
const result = await executor.execute(tx);
|
|
10677
|
-
printOutput(result, getOpts());
|
|
10678
|
-
});
|
|
10679
|
-
farming.command("claim").description("Claim accumulated farming rewards (KITTEN + WHYPE) without changing position").requiredOption("--protocol <protocol>", "Protocol slug (e.g. kittenswap)").option("--owner <address>", "Owner address to receive rewards (defaults to DEFI_WALLET_ADDRESS or private key address)").action(async (opts) => {
|
|
10031
|
+
var LIQD_API = "https://api.liqd.ag/v2";
|
|
10032
|
+
var LIQD_ROUTER = "0x744489ee3d540777a66f2cf297479745e0852f7a";
|
|
10033
|
+
async function liquidSwapRoute(tokenIn, tokenOut, amountIn, slippagePct) {
|
|
10034
|
+
const params = new URLSearchParams({ tokenIn, tokenOut, amountIn, slippage: slippagePct });
|
|
10035
|
+
const url = `${LIQD_API}/route?${params}`;
|
|
10036
|
+
const res = await fetch(url);
|
|
10037
|
+
if (!res.ok) throw new Error(`LiquidSwap route failed: ${res.status} ${await res.text()}`);
|
|
10038
|
+
const json = await res.json();
|
|
10039
|
+
const execution = json.execution;
|
|
10040
|
+
if (!execution) throw new Error("LiquidSwap: no execution data in response");
|
|
10041
|
+
const details = json.details;
|
|
10042
|
+
return {
|
|
10043
|
+
to: String(execution.to ?? LIQD_ROUTER),
|
|
10044
|
+
data: String(execution.calldata),
|
|
10045
|
+
value: String(execution.value ?? "0x0"),
|
|
10046
|
+
outAmount: String(details?.amountOut ?? json.amountOut ?? "0")
|
|
10047
|
+
};
|
|
10048
|
+
}
|
|
10049
|
+
function registerSwap(parent, getOpts, makeExecutor2) {
|
|
10050
|
+
parent.command("swap").description("Swap tokens via DEX aggregator (KyberSwap, OpenOcean, LiquidSwap)").requiredOption("--from <token>", "Input token symbol or address").requiredOption("--to <token>", "Output token symbol or address").requiredOption("--amount <amount>", "Amount of input token in wei").option("--provider <name>", "Aggregator: kyber, openocean, liquid", "kyber").option("--slippage <bps>", "Slippage tolerance in bps", "50").action(async (opts) => {
|
|
10680
10051
|
const executor = makeExecutor2();
|
|
10052
|
+
const chainName = parent.opts().chain ?? "hyperevm";
|
|
10681
10053
|
const registry = Registry.loadEmbedded();
|
|
10682
|
-
const
|
|
10683
|
-
const
|
|
10684
|
-
const
|
|
10685
|
-
const
|
|
10686
|
-
const
|
|
10687
|
-
|
|
10688
|
-
|
|
10689
|
-
|
|
10690
|
-
|
|
10691
|
-
|
|
10692
|
-
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10703
|
-
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
|
|
10722
|
-
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
}
|
|
10728
|
-
|
|
10054
|
+
const provider = opts.provider.toLowerCase();
|
|
10055
|
+
const slippageBps = parseInt(opts.slippage, 10);
|
|
10056
|
+
const fromAddr = opts.from.startsWith("0x") ? opts.from : registry.resolveToken(chainName, opts.from).address;
|
|
10057
|
+
const toAddr = opts.to.startsWith("0x") ? opts.to : registry.resolveToken(chainName, opts.to).address;
|
|
10058
|
+
const wallet = process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
|
|
10059
|
+
if (provider === "kyber") {
|
|
10060
|
+
const chainNames = CHAIN_NAMES[chainName];
|
|
10061
|
+
if (!chainNames?.kyber) {
|
|
10062
|
+
printOutput({ error: `KyberSwap: unsupported chain '${chainName}'. Supported: hyperevm` }, getOpts());
|
|
10063
|
+
return;
|
|
10064
|
+
}
|
|
10065
|
+
const kyberChain = chainNames.kyber;
|
|
10066
|
+
try {
|
|
10067
|
+
const quoteData = await kyberGetQuote(kyberChain, fromAddr, toAddr, opts.amount);
|
|
10068
|
+
const routeSummary = quoteData.routeSummary;
|
|
10069
|
+
const amountOut = String(routeSummary.amountOut ?? "0");
|
|
10070
|
+
const txData = await kyberBuildTx(
|
|
10071
|
+
kyberChain,
|
|
10072
|
+
routeSummary,
|
|
10073
|
+
wallet,
|
|
10074
|
+
wallet,
|
|
10075
|
+
slippageBps
|
|
10076
|
+
);
|
|
10077
|
+
const tx = {
|
|
10078
|
+
description: `KyberSwap: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
|
|
10079
|
+
to: txData.to,
|
|
10080
|
+
data: txData.data,
|
|
10081
|
+
value: txData.value.startsWith("0x") ? BigInt(txData.value) : BigInt(txData.value || 0),
|
|
10082
|
+
approvals: [{ token: fromAddr, spender: txData.to, amount: BigInt(opts.amount) }]
|
|
10083
|
+
};
|
|
10084
|
+
const result = await executor.execute(tx);
|
|
10085
|
+
printOutput({
|
|
10086
|
+
provider: "kyber",
|
|
10087
|
+
chain: kyberChain,
|
|
10088
|
+
from_token: fromAddr,
|
|
10089
|
+
to_token: toAddr,
|
|
10090
|
+
amount_in: opts.amount,
|
|
10091
|
+
amount_out: amountOut,
|
|
10092
|
+
router: txData.to,
|
|
10093
|
+
...result
|
|
10094
|
+
}, getOpts());
|
|
10095
|
+
} catch (e) {
|
|
10096
|
+
printOutput({ error: `KyberSwap error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
|
|
10097
|
+
}
|
|
10098
|
+
return;
|
|
10099
|
+
}
|
|
10100
|
+
if (provider === "openocean") {
|
|
10101
|
+
const chainNames = CHAIN_NAMES[chainName];
|
|
10102
|
+
if (!chainNames) {
|
|
10103
|
+
printOutput({ error: `OpenOcean: unsupported chain '${chainName}'. Supported: ${Object.keys(CHAIN_NAMES).join(", ")}` }, getOpts());
|
|
10104
|
+
return;
|
|
10105
|
+
}
|
|
10106
|
+
const ooChain = chainNames.openocean;
|
|
10107
|
+
const fromToken = opts.from.startsWith("0x") ? registry.tokens.get(chainName)?.find((t) => t.address.toLowerCase() === opts.from.toLowerCase()) : registry.tokens.get(chainName)?.find((t) => t.symbol.toLowerCase() === opts.from.toLowerCase());
|
|
10108
|
+
const fromDecimals = fromToken?.decimals ?? 18;
|
|
10109
|
+
const humanAmount = (Number(opts.amount) / 10 ** fromDecimals).toString();
|
|
10110
|
+
const slippagePct = (slippageBps / 100).toFixed(2);
|
|
10111
|
+
try {
|
|
10112
|
+
const swap = await openoceanSwap(
|
|
10113
|
+
ooChain,
|
|
10114
|
+
fromAddr,
|
|
10115
|
+
toAddr,
|
|
10116
|
+
humanAmount,
|
|
10117
|
+
slippagePct,
|
|
10118
|
+
wallet
|
|
10119
|
+
);
|
|
10120
|
+
const tx = {
|
|
10121
|
+
description: `OpenOcean: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
|
|
10122
|
+
to: swap.to,
|
|
10123
|
+
data: swap.data,
|
|
10124
|
+
value: swap.value.startsWith("0x") ? BigInt(swap.value) : BigInt(swap.value || 0),
|
|
10125
|
+
approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }]
|
|
10126
|
+
};
|
|
10127
|
+
const result = await executor.execute(tx);
|
|
10128
|
+
printOutput({
|
|
10129
|
+
provider: "openocean",
|
|
10130
|
+
chain: ooChain,
|
|
10131
|
+
from_token: fromAddr,
|
|
10132
|
+
to_token: toAddr,
|
|
10133
|
+
amount_in: opts.amount,
|
|
10134
|
+
amount_out: swap.outAmount,
|
|
10135
|
+
router: swap.to,
|
|
10136
|
+
...result
|
|
10137
|
+
}, getOpts());
|
|
10138
|
+
} catch (e) {
|
|
10139
|
+
printOutput({ error: `OpenOcean error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
|
|
10140
|
+
}
|
|
10141
|
+
return;
|
|
10142
|
+
}
|
|
10143
|
+
if (provider === "liquid") {
|
|
10144
|
+
if (chainName !== "hyperevm") {
|
|
10145
|
+
printOutput({ error: `LiquidSwap only supports hyperevm, got '${chainName}'` }, getOpts());
|
|
10146
|
+
return;
|
|
10147
|
+
}
|
|
10148
|
+
const slippagePct = (slippageBps / 100).toFixed(2);
|
|
10149
|
+
try {
|
|
10150
|
+
const route = await liquidSwapRoute(fromAddr, toAddr, opts.amount, slippagePct);
|
|
10151
|
+
const tx = {
|
|
10152
|
+
description: `LiquidSwap: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
|
|
10153
|
+
to: route.to,
|
|
10154
|
+
data: route.data,
|
|
10155
|
+
value: route.value.startsWith("0x") ? BigInt(route.value) : BigInt(route.value || 0),
|
|
10156
|
+
approvals: [{ token: fromAddr, spender: route.to, amount: BigInt(opts.amount) }]
|
|
10157
|
+
};
|
|
10158
|
+
const result = await executor.execute(tx);
|
|
10159
|
+
printOutput({
|
|
10160
|
+
provider: "liquid",
|
|
10161
|
+
chain: chainName,
|
|
10162
|
+
from_token: fromAddr,
|
|
10163
|
+
to_token: toAddr,
|
|
10164
|
+
amount_in: opts.amount,
|
|
10165
|
+
amount_out: route.outAmount,
|
|
10166
|
+
router: route.to,
|
|
10167
|
+
...result
|
|
10168
|
+
}, getOpts());
|
|
10169
|
+
} catch (e) {
|
|
10170
|
+
printOutput({ error: `LiquidSwap error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
|
|
10171
|
+
}
|
|
10172
|
+
return;
|
|
10173
|
+
}
|
|
10174
|
+
printOutput({ error: `Unknown provider '${opts.provider}'. Choose: kyber, openocean, liquid` }, getOpts());
|
|
10729
10175
|
});
|
|
10730
10176
|
}
|
|
10731
|
-
function resolveOwner(optOwner) {
|
|
10732
|
-
if (optOwner) return optOwner;
|
|
10733
|
-
const walletAddr = process.env["DEFI_WALLET_ADDRESS"];
|
|
10734
|
-
if (walletAddr) return walletAddr;
|
|
10735
|
-
const privateKey = process.env["DEFI_PRIVATE_KEY"];
|
|
10736
|
-
if (privateKey) {
|
|
10737
|
-
return privateKeyToAccount3(privateKey).address;
|
|
10738
|
-
}
|
|
10739
|
-
throw new Error(
|
|
10740
|
-
"--owner, DEFI_WALLET_ADDRESS, or DEFI_PRIVATE_KEY is required to resolve reward recipient"
|
|
10741
|
-
);
|
|
10742
|
-
}
|
|
10743
10177
|
|
|
10744
10178
|
// src/commands/setup.ts
|
|
10745
10179
|
import pc2 from "picocolors";
|
|
@@ -10789,8 +10223,8 @@ function isValidPrivateKey(s) {
|
|
|
10789
10223
|
}
|
|
10790
10224
|
async function deriveAddress(privateKey) {
|
|
10791
10225
|
try {
|
|
10792
|
-
const { privateKeyToAccount:
|
|
10793
|
-
const account =
|
|
10226
|
+
const { privateKeyToAccount: privateKeyToAccount3 } = await import("viem/accounts");
|
|
10227
|
+
const account = privateKeyToAccount3(privateKey);
|
|
10794
10228
|
return account.address;
|
|
10795
10229
|
} catch {
|
|
10796
10230
|
return null;
|
|
@@ -10885,107 +10319,6 @@ function registerSetup(program2) {
|
|
|
10885
10319
|
});
|
|
10886
10320
|
}
|
|
10887
10321
|
|
|
10888
|
-
// src/commands/lb.ts
|
|
10889
|
-
function registerLB(parent, getOpts, makeExecutor2) {
|
|
10890
|
-
const lb = parent.command("lb").description("Merchant Moe Liquidity Book: add/remove liquidity, rewards, positions");
|
|
10891
|
-
lb.command("add").description("Add liquidity to a Liquidity Book pair").requiredOption("--protocol <protocol>", "Protocol slug (e.g. merchantmoe-mantle)").requiredOption("--pool <address>", "LB pair address").requiredOption("--token-x <address>", "Token X address").requiredOption("--token-y <address>", "Token Y address").requiredOption("--bin-step <step>", "Bin step of the pair").option("--amount-x <wei>", "Amount of token X in wei", "0").option("--amount-y <wei>", "Amount of token Y in wei", "0").option("--bins <N>", "Number of bins on each side of active bin", "5").option("--active-id <id>", "Active bin id (defaults to on-chain query)").option("--recipient <address>", "Recipient address").action(async (opts) => {
|
|
10892
|
-
const executor = makeExecutor2();
|
|
10893
|
-
const registry = Registry.loadEmbedded();
|
|
10894
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10895
|
-
const chainName = parent.opts().chain;
|
|
10896
|
-
const chain = registry.getChain(chainName ?? "mantle");
|
|
10897
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10898
|
-
const adapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
10899
|
-
const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
|
|
10900
|
-
const tx = await adapter.buildAddLiquidity({
|
|
10901
|
-
pool: opts.pool,
|
|
10902
|
-
tokenX: opts.tokenX,
|
|
10903
|
-
tokenY: opts.tokenY,
|
|
10904
|
-
binStep: parseInt(opts.binStep),
|
|
10905
|
-
amountX: BigInt(opts.amountX),
|
|
10906
|
-
amountY: BigInt(opts.amountY),
|
|
10907
|
-
numBins: parseInt(opts.bins),
|
|
10908
|
-
activeIdDesired: opts.activeId ? parseInt(opts.activeId) : void 0,
|
|
10909
|
-
recipient
|
|
10910
|
-
});
|
|
10911
|
-
const result = await executor.execute(tx);
|
|
10912
|
-
printOutput(result, getOpts());
|
|
10913
|
-
});
|
|
10914
|
-
lb.command("remove").description("Remove liquidity from Liquidity Book bins").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--token-x <address>", "Token X address").requiredOption("--token-y <address>", "Token Y address").requiredOption("--bin-step <step>", "Bin step of the pair").requiredOption("--bins <binIds>", "Comma-separated bin IDs to remove from").requiredOption("--amounts <amounts>", "Comma-separated LB token amounts to remove per bin (wei)").option("--recipient <address>", "Recipient address").action(async (opts) => {
|
|
10915
|
-
const executor = makeExecutor2();
|
|
10916
|
-
const registry = Registry.loadEmbedded();
|
|
10917
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10918
|
-
const adapter = createMerchantMoeLB(protocol);
|
|
10919
|
-
const recipient = opts.recipient ?? process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
|
|
10920
|
-
const binIds = opts.bins.split(",").map((s) => parseInt(s.trim()));
|
|
10921
|
-
const amounts = opts.amounts.split(",").map((s) => BigInt(s.trim()));
|
|
10922
|
-
const tx = await adapter.buildRemoveLiquidity({
|
|
10923
|
-
tokenX: opts.tokenX,
|
|
10924
|
-
tokenY: opts.tokenY,
|
|
10925
|
-
binStep: parseInt(opts.binStep),
|
|
10926
|
-
binIds,
|
|
10927
|
-
amounts,
|
|
10928
|
-
recipient
|
|
10929
|
-
});
|
|
10930
|
-
const result = await executor.execute(tx);
|
|
10931
|
-
printOutput(result, getOpts());
|
|
10932
|
-
});
|
|
10933
|
-
lb.command("rewards").description("Show pending MOE rewards for a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pool <address>", "LB pair address").option("--bins <binIds>", "Comma-separated bin IDs to check (auto-detected from rewarder range if omitted)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
|
|
10934
|
-
const registry = Registry.loadEmbedded();
|
|
10935
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10936
|
-
const chainName = parent.opts().chain;
|
|
10937
|
-
const chain = registry.getChain(chainName ?? "mantle");
|
|
10938
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10939
|
-
const adapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
10940
|
-
const user = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
|
|
10941
|
-
if (!user) throw new Error("--address or DEFI_WALLET_ADDRESS required");
|
|
10942
|
-
const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
|
|
10943
|
-
const rewards = await adapter.getPendingRewards(user, opts.pool, binIds);
|
|
10944
|
-
printOutput(rewards, getOpts());
|
|
10945
|
-
});
|
|
10946
|
-
lb.command("claim").description("Claim pending MOE rewards from a pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pool <address>", "LB pair address").option("--bins <binIds>", "Comma-separated bin IDs to claim from (auto-detected from rewarder range if omitted)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
|
|
10947
|
-
const executor = makeExecutor2();
|
|
10948
|
-
const registry = Registry.loadEmbedded();
|
|
10949
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10950
|
-
const chainName = parent.opts().chain;
|
|
10951
|
-
const chain = registry.getChain(chainName ?? "mantle");
|
|
10952
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10953
|
-
const adapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
10954
|
-
const user = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
|
|
10955
|
-
if (!user) throw new Error("--address or DEFI_WALLET_ADDRESS required");
|
|
10956
|
-
const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
|
|
10957
|
-
const tx = await adapter.buildClaimRewards(user, opts.pool, binIds);
|
|
10958
|
-
const result = await executor.execute(tx);
|
|
10959
|
-
printOutput(result, getOpts());
|
|
10960
|
-
});
|
|
10961
|
-
lb.command("discover").description("Find all rewarded LB pools on chain").requiredOption("--protocol <protocol>", "Protocol slug").option("--active-only", "Only show non-stopped pools").action(async (opts) => {
|
|
10962
|
-
const registry = Registry.loadEmbedded();
|
|
10963
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10964
|
-
const chainName = parent.opts().chain;
|
|
10965
|
-
const chain = registry.getChain(chainName ?? "mantle");
|
|
10966
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10967
|
-
const adapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
10968
|
-
let pools = await adapter.discoverRewardedPools();
|
|
10969
|
-
if (opts.activeOnly) {
|
|
10970
|
-
pools = pools.filter((p) => !p.stopped);
|
|
10971
|
-
}
|
|
10972
|
-
printOutput(pools, getOpts());
|
|
10973
|
-
});
|
|
10974
|
-
lb.command("positions").description("Show user positions per bin in a LB pool").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--pool <address>", "LB pair address").option("--bins <binIds>", "Comma-separated bin IDs to query (auto-detected from rewarder range or active \xB1 50 if omitted)").option("--address <address>", "Wallet address (defaults to DEFI_WALLET_ADDRESS)").action(async (opts) => {
|
|
10975
|
-
const registry = Registry.loadEmbedded();
|
|
10976
|
-
const protocol = registry.getProtocol(opts.protocol);
|
|
10977
|
-
const chainName = parent.opts().chain;
|
|
10978
|
-
const chain = registry.getChain(chainName ?? "mantle");
|
|
10979
|
-
const rpcUrl = chain.effectiveRpcUrl();
|
|
10980
|
-
const adapter = createMerchantMoeLB(protocol, rpcUrl);
|
|
10981
|
-
const user = opts.address ?? process.env["DEFI_WALLET_ADDRESS"];
|
|
10982
|
-
if (!user) throw new Error("--address or DEFI_WALLET_ADDRESS required");
|
|
10983
|
-
const binIds = opts.bins ? opts.bins.split(",").map((s) => parseInt(s.trim())) : void 0;
|
|
10984
|
-
const positions = await adapter.getUserPositions(user, opts.pool, binIds);
|
|
10985
|
-
printOutput(positions, getOpts());
|
|
10986
|
-
});
|
|
10987
|
-
}
|
|
10988
|
-
|
|
10989
10322
|
// src/cli.ts
|
|
10990
10323
|
var _require = createRequire(import.meta.url);
|
|
10991
10324
|
var _pkg = _require("../package.json");
|
|
@@ -10999,8 +10332,8 @@ var BANNER = `
|
|
|
10999
10332
|
|
|
11000
10333
|
2 chains \xB7 21 protocols \xB7 by HypurrQuant
|
|
11001
10334
|
|
|
11002
|
-
|
|
11003
|
-
|
|
10335
|
+
Lending, LP provision, farming, gauges, vaults,
|
|
10336
|
+
yield comparison \u2014 all from your terminal.
|
|
11004
10337
|
`;
|
|
11005
10338
|
var program = new Command().name("defi").description("DeFi CLI \u2014 Multi-chain DeFi toolkit").version(_pkg.version).addHelpText("before", BANNER).option("--json", "Output as JSON").option("--ndjson", "Output as newline-delimited JSON").option("--fields <fields>", "Select specific output fields (comma-separated)").option("--chain <chain>", "Target chain", "hyperevm").option("--dry-run", "Dry-run mode (default, no broadcast)", true).option("--broadcast", "Actually broadcast the transaction");
|
|
11006
10339
|
function getOutputMode() {
|
|
@@ -11015,37 +10348,23 @@ function makeExecutor() {
|
|
|
11015
10348
|
}
|
|
11016
10349
|
registerStatus(program, getOutputMode);
|
|
11017
10350
|
registerSchema(program, getOutputMode);
|
|
11018
|
-
|
|
11019
|
-
registerGauge(program, getOutputMode, makeExecutor);
|
|
10351
|
+
registerLP(program, getOutputMode, makeExecutor);
|
|
11020
10352
|
registerLending(program, getOutputMode, makeExecutor);
|
|
11021
10353
|
registerCdp(program, getOutputMode, makeExecutor);
|
|
11022
|
-
registerStaking(program, getOutputMode, makeExecutor);
|
|
11023
10354
|
registerVault(program, getOutputMode, makeExecutor);
|
|
11024
10355
|
registerYield(program, getOutputMode, makeExecutor);
|
|
11025
10356
|
registerPortfolio(program, getOutputMode);
|
|
11026
10357
|
registerMonitor(program, getOutputMode);
|
|
11027
10358
|
registerAlert(program, getOutputMode);
|
|
11028
10359
|
registerScan(program, getOutputMode);
|
|
11029
|
-
registerArb(program, getOutputMode, makeExecutor);
|
|
11030
10360
|
registerPositions(program, getOutputMode);
|
|
11031
10361
|
registerPrice(program, getOutputMode);
|
|
11032
10362
|
registerWallet(program, getOutputMode);
|
|
11033
10363
|
registerToken(program, getOutputMode, makeExecutor);
|
|
11034
10364
|
registerWhales(program, getOutputMode);
|
|
11035
|
-
registerCompare(program, getOutputMode);
|
|
11036
|
-
registerSwap(program, getOutputMode, makeExecutor);
|
|
11037
10365
|
registerBridge(program, getOutputMode);
|
|
11038
|
-
|
|
11039
|
-
registerFarm(program, getOutputMode, makeExecutor);
|
|
11040
|
-
registerFarming(program, getOutputMode, makeExecutor);
|
|
11041
|
-
registerLB(program, getOutputMode, makeExecutor);
|
|
10366
|
+
registerSwap(program, getOutputMode, makeExecutor);
|
|
11042
10367
|
registerSetup(program);
|
|
11043
|
-
program.command("agent").description("Agent mode: read JSON commands from stdin (for AI agents)").action(async () => {
|
|
11044
|
-
const executor = makeExecutor();
|
|
11045
|
-
process.stderr.write("Agent mode: reading JSON commands from stdin...\n");
|
|
11046
|
-
process.stderr.write("Agent mode not yet fully implemented in TS port.\n");
|
|
11047
|
-
process.exit(1);
|
|
11048
|
-
});
|
|
11049
10368
|
|
|
11050
10369
|
// src/landing.ts
|
|
11051
10370
|
import pc3 from "picocolors";
|
|
@@ -11152,7 +10471,7 @@ async function showLandingPage(isJson) {
|
|
|
11152
10471
|
console.log(" Commands:");
|
|
11153
10472
|
console.log(pc3.dim(" defi status Protocol overview"));
|
|
11154
10473
|
console.log(pc3.dim(" defi lending rates Compare lending APYs"));
|
|
11155
|
-
console.log(pc3.dim(" defi
|
|
10474
|
+
console.log(pc3.dim(" defi lp discover Find LP farming pools"));
|
|
11156
10475
|
console.log(pc3.dim(" defi portfolio View all positions"));
|
|
11157
10476
|
console.log(pc3.dim(" defi scan Exploit detection"));
|
|
11158
10477
|
console.log(pc3.dim(" defi --help Full command list"));
|
|
@@ -11225,30 +10544,22 @@ async function main() {
|
|
|
11225
10544
|
const knownSubcommands = /* @__PURE__ */ new Set([
|
|
11226
10545
|
"status",
|
|
11227
10546
|
"schema",
|
|
11228
|
-
"
|
|
11229
|
-
"gauge",
|
|
10547
|
+
"lp",
|
|
11230
10548
|
"lending",
|
|
11231
10549
|
"cdp",
|
|
11232
|
-
"staking",
|
|
11233
10550
|
"vault",
|
|
11234
10551
|
"yield",
|
|
11235
10552
|
"portfolio",
|
|
11236
10553
|
"monitor",
|
|
11237
10554
|
"alert",
|
|
11238
10555
|
"scan",
|
|
11239
|
-
"arb",
|
|
11240
10556
|
"positions",
|
|
11241
10557
|
"price",
|
|
11242
10558
|
"wallet",
|
|
11243
10559
|
"token",
|
|
11244
10560
|
"whales",
|
|
11245
|
-
"compare",
|
|
11246
|
-
"swap",
|
|
11247
10561
|
"bridge",
|
|
11248
|
-
"
|
|
11249
|
-
"farm",
|
|
11250
|
-
"farming",
|
|
11251
|
-
"lb",
|
|
10562
|
+
"swap",
|
|
11252
10563
|
"agent",
|
|
11253
10564
|
"setup",
|
|
11254
10565
|
"init"
|