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