hufi-cli 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -8
- package/dist/cli.js +171 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,16 +5,16 @@ CLI tool for the [hu.fi](https://hu.finance) DeFi platform.
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
bun install -g hufi
|
|
8
|
+
bun install -g hufi
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Or run without installing:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
bunx hufi
|
|
14
|
+
bunx hufi <command>
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
All examples below assume global install. Otherwise replace `hufi` with `bunx hufi
|
|
17
|
+
All examples below assume global install. Otherwise replace `hufi` with `bunx hufi`.
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
20
20
|
|
|
@@ -76,6 +76,8 @@ hufi campaign leaderboard --address 0x... # leaderboard
|
|
|
76
76
|
|
|
77
77
|
Requires staked HMT, gas, and fund tokens (USDT/USDC). Creates an escrow contract on-chain.
|
|
78
78
|
|
|
79
|
+
Before broadcasting, the CLI validates the campaign type-specific target, checks the 6-hour to 100-day duration window, verifies your minimum HMT stake when possible, inspects fund token balance, and estimates gas for approval plus creation.
|
|
80
|
+
|
|
79
81
|
```bash
|
|
80
82
|
# Market Making
|
|
81
83
|
hufi campaign create \
|
|
@@ -112,11 +114,16 @@ Running `campaign status/join/progress/leaderboard` without `-a` shows help.
|
|
|
112
114
|
|
|
113
115
|
```bash
|
|
114
116
|
hufi exchange register --name mexc --api-key <key> --secret-key <secret>
|
|
117
|
+
hufi exchange register --name bitmart --api-key <key> --secret-key <secret> --bitmart-memo <memo>
|
|
115
118
|
hufi exchange list
|
|
116
|
-
hufi exchange revalidate
|
|
117
|
-
hufi exchange delete
|
|
119
|
+
hufi exchange revalidate mexc
|
|
120
|
+
hufi exchange delete mexc
|
|
118
121
|
```
|
|
119
122
|
|
|
123
|
+
`exchange register` expects the CCXT exchange name in `--name` and accepts `--bitmart-memo` for Bitmart accounts that require an extra memo value.
|
|
124
|
+
|
|
125
|
+
You must run `hufi auth login` before `exchange register`, `exchange list`, `exchange delete`, or `exchange revalidate`.
|
|
126
|
+
|
|
120
127
|
### staking
|
|
121
128
|
|
|
122
129
|
| Command | Description |
|
|
@@ -131,8 +138,8 @@ hufi exchange delete --name mexc
|
|
|
131
138
|
hufi staking deposit # show address QR code
|
|
132
139
|
hufi staking status # check your staking
|
|
133
140
|
hufi staking status --address 0x... # check another address
|
|
134
|
-
hufi staking stake
|
|
135
|
-
hufi staking unstake
|
|
141
|
+
hufi staking stake 1000 # stake 1000 HMT
|
|
142
|
+
hufi staking unstake 500 # unstake 500 HMT
|
|
136
143
|
hufi staking withdraw # withdraw unlocked tokens
|
|
137
144
|
```
|
|
138
145
|
|
|
@@ -158,7 +165,7 @@ hufi dashboard --export json
|
|
|
158
165
|
| `-V, --version` | Show version |
|
|
159
166
|
| `-h, --help` | Show help |
|
|
160
167
|
|
|
161
|
-
|
|
168
|
+
Most command actions support `--json` for machine-readable output. Help output remains text, and commands that intentionally show help when required arguments are missing do not emit a JSON error envelope.
|
|
162
169
|
|
|
163
170
|
## Configuration
|
|
164
171
|
|
|
@@ -181,6 +188,7 @@ bun install # install deps
|
|
|
181
188
|
bun run dev -- --help # run from source
|
|
182
189
|
bun run build # build to dist/cli.js
|
|
183
190
|
bun test # unit tests
|
|
191
|
+
./test-cli.sh # full CLI integration coverage
|
|
184
192
|
bun run test:cli # integration tests
|
|
185
193
|
bun run typecheck # type check
|
|
186
194
|
```
|
package/dist/cli.js
CHANGED
|
@@ -23063,9 +23063,33 @@ function requireAuthAddress() {
|
|
|
23063
23063
|
}
|
|
23064
23064
|
|
|
23065
23065
|
// src/commands/exchange.ts
|
|
23066
|
+
var authHintByAction = {
|
|
23067
|
+
register: "Run: hufi auth login --private-key <key> before registering exchange API keys.",
|
|
23068
|
+
list: "Run: hufi auth login --private-key <key> before listing exchange API keys.",
|
|
23069
|
+
delete: "Run: hufi auth login --private-key <key> before deleting exchange API keys.",
|
|
23070
|
+
revalidate: "Run: hufi auth login --private-key <key> before revalidating exchange API keys."
|
|
23071
|
+
};
|
|
23072
|
+
function isUnauthorizedError(err) {
|
|
23073
|
+
return err instanceof ApiError && err.status === 401;
|
|
23074
|
+
}
|
|
23075
|
+
function formatExchangeCommandErrorMessage(action, err) {
|
|
23076
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
23077
|
+
const commandTarget = action === "list" ? "exchange API keys" : "exchange API key";
|
|
23078
|
+
if (isUnauthorizedError(err)) {
|
|
23079
|
+
return `Failed to ${action} ${commandTarget}: ${message}. ${authHintByAction[action]}`;
|
|
23080
|
+
}
|
|
23081
|
+
return `Failed to ${action} ${commandTarget}: ${message}`;
|
|
23082
|
+
}
|
|
23083
|
+
function formatRevalidateText(exchangeName, result) {
|
|
23084
|
+
const lines = [`${exchangeName}: ${result.is_valid ? "valid" : "invalid"}`];
|
|
23085
|
+
if ((result.missing_permissions?.length ?? 0) > 0) {
|
|
23086
|
+
lines.push(`Missing permissions: ${result.missing_permissions?.join(", ")}`);
|
|
23087
|
+
}
|
|
23088
|
+
return lines;
|
|
23089
|
+
}
|
|
23066
23090
|
function createExchangeCommand() {
|
|
23067
23091
|
const exchange = new Command("exchange").description("Exchange API key management");
|
|
23068
|
-
exchange.command("register").description("Register a read-only exchange API key").requiredOption("-n, --name <name>", "
|
|
23092
|
+
exchange.command("register").description("Register a read-only exchange API key").requiredOption("-n, --name <name>", "CCXT exchange name (e.g. binance, mexc)").requiredOption("--api-key <key>", "Read-only API key").requiredOption("--secret-key <key>", "Read-only API secret").option("--bitmart-memo <memo>", "Bitmart memo (only for bitmart)").option("--json", "Output as JSON").action(async (opts) => {
|
|
23069
23093
|
const { baseUrl, accessToken } = requireAuthToken();
|
|
23070
23094
|
try {
|
|
23071
23095
|
const result = await registerExchangeApiKey(baseUrl, accessToken, opts.name, opts.apiKey, opts.secretKey, opts.bitmartMemo);
|
|
@@ -23078,8 +23102,7 @@ function createExchangeCommand() {
|
|
|
23078
23102
|
}
|
|
23079
23103
|
}
|
|
23080
23104
|
} catch (err) {
|
|
23081
|
-
|
|
23082
|
-
printText(`Failed to register exchange API key: ${message}`);
|
|
23105
|
+
printText(formatExchangeCommandErrorMessage("register", err));
|
|
23083
23106
|
process.exitCode = 1;
|
|
23084
23107
|
}
|
|
23085
23108
|
});
|
|
@@ -23100,42 +23123,49 @@ function createExchangeCommand() {
|
|
|
23100
23123
|
}
|
|
23101
23124
|
}
|
|
23102
23125
|
} catch (err) {
|
|
23103
|
-
|
|
23104
|
-
printText(`Failed to list exchange API keys: ${message}`);
|
|
23126
|
+
printText(formatExchangeCommandErrorMessage("list", err));
|
|
23105
23127
|
process.exitCode = 1;
|
|
23106
23128
|
}
|
|
23107
23129
|
});
|
|
23108
|
-
exchange.command("delete").description("Delete API keys for an exchange").
|
|
23130
|
+
exchange.command("delete [name]").description("Delete API keys for an exchange").usage("[name] [options]").option("-n, --name <name>", "Exchange name (e.g. mexc, bybit)").option("--json", "Output as JSON").action(async (nameArg, opts) => {
|
|
23131
|
+
const exchangeName = nameArg ?? opts.name;
|
|
23132
|
+
if (!exchangeName) {
|
|
23133
|
+
printText("Exchange name is required. Usage: hufi exchange delete <name>");
|
|
23134
|
+
process.exitCode = 1;
|
|
23135
|
+
return;
|
|
23136
|
+
}
|
|
23109
23137
|
const { baseUrl, accessToken } = requireAuthToken();
|
|
23110
23138
|
try {
|
|
23111
|
-
await deleteExchangeApiKey(baseUrl, accessToken,
|
|
23139
|
+
await deleteExchangeApiKey(baseUrl, accessToken, exchangeName);
|
|
23112
23140
|
if (opts.json) {
|
|
23113
|
-
printJson({ deleted: true, exchange_name:
|
|
23141
|
+
printJson({ deleted: true, exchange_name: exchangeName });
|
|
23114
23142
|
} else {
|
|
23115
|
-
printText(`Deleted API keys for ${
|
|
23143
|
+
printText(`Deleted API keys for ${exchangeName}.`);
|
|
23116
23144
|
}
|
|
23117
23145
|
} catch (err) {
|
|
23118
|
-
|
|
23119
|
-
printText(`Failed to delete exchange API keys: ${message}`);
|
|
23146
|
+
printText(formatExchangeCommandErrorMessage("delete", err));
|
|
23120
23147
|
process.exitCode = 1;
|
|
23121
23148
|
}
|
|
23122
23149
|
});
|
|
23123
|
-
exchange.command("revalidate").description("Revalidate exchange API key").
|
|
23150
|
+
exchange.command("revalidate [name]").description("Revalidate exchange API key").usage("[name] [options]").option("-n, --name <name>", "Exchange name (e.g. mexc, bybit)").option("--json", "Output as JSON").action(async (nameArg, opts) => {
|
|
23151
|
+
const exchangeName = nameArg ?? opts.name;
|
|
23152
|
+
if (!exchangeName) {
|
|
23153
|
+
printText("Exchange name is required. Usage: hufi exchange revalidate <name>");
|
|
23154
|
+
process.exitCode = 1;
|
|
23155
|
+
return;
|
|
23156
|
+
}
|
|
23124
23157
|
const { baseUrl, accessToken } = requireAuthToken();
|
|
23125
23158
|
try {
|
|
23126
|
-
const result = await revalidateExchangeApiKey(baseUrl, accessToken,
|
|
23159
|
+
const result = await revalidateExchangeApiKey(baseUrl, accessToken, exchangeName);
|
|
23127
23160
|
if (opts.json) {
|
|
23128
23161
|
printJson(result);
|
|
23129
23162
|
} else {
|
|
23130
|
-
const
|
|
23131
|
-
|
|
23132
|
-
if (result.missing_permissions.length > 0) {
|
|
23133
|
-
printText(`Missing permissions: ${result.missing_permissions.join(", ")}`);
|
|
23163
|
+
for (const line of formatRevalidateText(exchangeName, result)) {
|
|
23164
|
+
printText(line);
|
|
23134
23165
|
}
|
|
23135
23166
|
}
|
|
23136
23167
|
} catch (err) {
|
|
23137
|
-
|
|
23138
|
-
printText(`Failed to revalidate exchange API key: ${message}`);
|
|
23168
|
+
printText(formatExchangeCommandErrorMessage("revalidate", err));
|
|
23139
23169
|
process.exitCode = 1;
|
|
23140
23170
|
}
|
|
23141
23171
|
});
|
|
@@ -24971,6 +25001,56 @@ function formatCampaignTimestamp(value) {
|
|
|
24971
25001
|
return "-";
|
|
24972
25002
|
return value.replace("T", " ").replace(/\.\d+Z$/, "").replace(/Z$/, "");
|
|
24973
25003
|
}
|
|
25004
|
+
function formatUtcTimestamp(value) {
|
|
25005
|
+
if (typeof value !== "string")
|
|
25006
|
+
return "-";
|
|
25007
|
+
const date = new Date(value);
|
|
25008
|
+
if (Number.isNaN(date.getTime()))
|
|
25009
|
+
return String(value);
|
|
25010
|
+
return date.toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
25011
|
+
}
|
|
25012
|
+
function formatMetricValue(value) {
|
|
25013
|
+
if (value === null || value === undefined)
|
|
25014
|
+
return "-";
|
|
25015
|
+
if (typeof value === "number") {
|
|
25016
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(4).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
|
|
25017
|
+
}
|
|
25018
|
+
if (typeof value === "string") {
|
|
25019
|
+
const trimmed = value.trim();
|
|
25020
|
+
if (trimmed && /^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
25021
|
+
const num = Number(trimmed);
|
|
25022
|
+
if (!Number.isNaN(num)) {
|
|
25023
|
+
return formatMetricValue(num);
|
|
25024
|
+
}
|
|
25025
|
+
}
|
|
25026
|
+
return value;
|
|
25027
|
+
}
|
|
25028
|
+
return JSON.stringify(value);
|
|
25029
|
+
}
|
|
25030
|
+
function printAlignedMetric(label, value, labelWidth = 14) {
|
|
25031
|
+
printText(` ${label.padEnd(labelWidth)} ${formatMetricValue(value)}`);
|
|
25032
|
+
}
|
|
25033
|
+
function printCampaignProgressCard(result) {
|
|
25034
|
+
const myMeta = result.my_meta && typeof result.my_meta === "object" ? result.my_meta : {};
|
|
25035
|
+
const totalMeta = result.total_meta && typeof result.total_meta === "object" ? result.total_meta : {};
|
|
25036
|
+
const myScore = Number(result.my_score ?? 0);
|
|
25037
|
+
const totalScore = Number(totalMeta.total_score ?? 0);
|
|
25038
|
+
const scoreShare = totalScore > 0 && Number.isFinite(myScore) ? `${(myScore / totalScore * 100).toFixed(2)}%` : "-";
|
|
25039
|
+
printText("Campaign Progress");
|
|
25040
|
+
printText("-----------------");
|
|
25041
|
+
printText("[Window]");
|
|
25042
|
+
printAlignedMetric("From", formatUtcTimestamp(result.from));
|
|
25043
|
+
printAlignedMetric("To", formatUtcTimestamp(result.to));
|
|
25044
|
+
printText("");
|
|
25045
|
+
printText("[Mine]");
|
|
25046
|
+
printAlignedMetric("Score", result.my_score);
|
|
25047
|
+
printAlignedMetric("Token balance", myMeta.token_balance);
|
|
25048
|
+
printAlignedMetric("Score share", scoreShare);
|
|
25049
|
+
printText("");
|
|
25050
|
+
printText("[Totals]");
|
|
25051
|
+
printAlignedMetric("Total score", totalMeta.total_score);
|
|
25052
|
+
printAlignedMetric("Total balance", totalMeta.total_balance);
|
|
25053
|
+
}
|
|
24974
25054
|
function formatTokenAmount(value, decimals, displayDecimals = 2) {
|
|
24975
25055
|
const amount = new bignumber_default(value).dividedBy(new bignumber_default(10).pow(decimals));
|
|
24976
25056
|
const rounded = amount.decimalPlaces(displayDecimals, bignumber_default.ROUND_HALF_UP);
|
|
@@ -24986,6 +25066,34 @@ function getLauncherUrl() {
|
|
|
24986
25066
|
const config = loadConfig();
|
|
24987
25067
|
return (config.launcherApiUrl ?? "https://cl.hu.finance").replace(/\/+$/, "");
|
|
24988
25068
|
}
|
|
25069
|
+
function printCampaignSummary(campaign, options = {}) {
|
|
25070
|
+
const indent = options.indent ?? "";
|
|
25071
|
+
const joinedTag = options.joinedTag ?? "";
|
|
25072
|
+
const showLauncher = options.showLauncher ?? true;
|
|
25073
|
+
const exchange = String(campaign.exchange_name ?? "?");
|
|
25074
|
+
const symbol = String(campaign.symbol ?? "?");
|
|
25075
|
+
const type = String(campaign.type ?? "?");
|
|
25076
|
+
const chainId = String(campaign.chain_id ?? "-");
|
|
25077
|
+
const address = String(campaign.address ?? campaign.escrow_address ?? "-");
|
|
25078
|
+
const status = String(campaign.status ?? "-");
|
|
25079
|
+
const startDate = typeof campaign.start_date === "string" ? campaign.start_date : undefined;
|
|
25080
|
+
const endDate = typeof campaign.end_date === "string" ? campaign.end_date : undefined;
|
|
25081
|
+
const launcher = typeof campaign.launcher === "string" ? campaign.launcher : undefined;
|
|
25082
|
+
const decimals = Number(campaign.fund_token_decimals ?? 0);
|
|
25083
|
+
const fundAmount = new bignumber_default(String(campaign.fund_amount ?? 0));
|
|
25084
|
+
const balanceNum = new bignumber_default(String(campaign.balance ?? 0));
|
|
25085
|
+
const pct = fundAmount.gt(0) ? balanceNum.dividedBy(fundAmount).times(100).toFixed(1) : "0.0";
|
|
25086
|
+
const fundTokenSymbol = String(campaign.fund_token_symbol ?? campaign.fund_token ?? "-");
|
|
25087
|
+
printText(`${indent}${exchange} ${symbol} (${type})${joinedTag}`);
|
|
25088
|
+
printText(`${indent} chain: ${chainId}`);
|
|
25089
|
+
printText(`${indent} address: ${address}`);
|
|
25090
|
+
printText(`${indent} status: ${status}`);
|
|
25091
|
+
printText(`${indent} duration: ${formatCampaignTimestamp(startDate)} ~ ${formatCampaignTimestamp(endDate)}`);
|
|
25092
|
+
printText(`${indent} funded: ${formatTokenAmount(String(campaign.fund_amount ?? 0), decimals)} ${fundTokenSymbol} paid: ${formatTokenAmount(String(campaign.amount_paid ?? 0), decimals)} balance: ${formatTokenAmount(String(campaign.balance ?? 0), decimals)} (${pct}%)`);
|
|
25093
|
+
if (showLauncher && launcher) {
|
|
25094
|
+
printText(`${indent} launcher: ${launcher}`);
|
|
25095
|
+
}
|
|
25096
|
+
}
|
|
24989
25097
|
function createCampaignCommand() {
|
|
24990
25098
|
const campaign = new Command("campaign").description("Campaign management commands");
|
|
24991
25099
|
campaign.command("list").description("List available campaigns").option("-c, --chain-id <id>", "Chain ID (default: from config)", Number, getDefaultChainId()).option("-s, --status <status>", "Filter by status (active, completed, cancelled, to_cancel)", "active").option("--page <n>", "Page number", Number, 1).option("--page-size <n>", "Items per page", Number, 20).option("-l, --limit <n>", "Max results", Number, 20).option("--json", "Output as JSON").action(async (opts) => {
|
|
@@ -25017,16 +25125,10 @@ function createCampaignCommand() {
|
|
|
25017
25125
|
const key = `${c.chain_id}:${c.address}`;
|
|
25018
25126
|
const joined = joinedKeys.has(key);
|
|
25019
25127
|
const tag = joined ? " [JOINED]" : "";
|
|
25020
|
-
|
|
25021
|
-
|
|
25022
|
-
|
|
25023
|
-
|
|
25024
|
-
printText(` ${c.exchange_name} ${c.symbol} (${c.type})${tag}`);
|
|
25025
|
-
printText(` chain: ${c.chain_id}`);
|
|
25026
|
-
printText(` address: ${c.address}`);
|
|
25027
|
-
printText(` status: ${c.status}`);
|
|
25028
|
-
printText(` duration: ${formatCampaignTimestamp(c.start_date)} ~ ${formatCampaignTimestamp(c.end_date)}`);
|
|
25029
|
-
printText(` funded: ${formatTokenAmount(c.fund_amount, decimals)} ${c.fund_token_symbol} paid: ${formatTokenAmount(c.amount_paid, decimals)} balance: ${formatTokenAmount(c.balance, decimals)} (${pct}%)`);
|
|
25128
|
+
printCampaignSummary(c, {
|
|
25129
|
+
indent: " ",
|
|
25130
|
+
joinedTag: tag
|
|
25131
|
+
});
|
|
25030
25132
|
printText("");
|
|
25031
25133
|
}
|
|
25032
25134
|
if (opts.status === "active") {
|
|
@@ -25078,10 +25180,24 @@ function createCampaignCommand() {
|
|
|
25078
25180
|
if (campaigns.length === 0) {
|
|
25079
25181
|
printText("No joined campaigns found.");
|
|
25080
25182
|
} else {
|
|
25081
|
-
printText(`Joined campaigns (${campaigns.length})
|
|
25183
|
+
printText(`Joined campaigns (${campaigns.length}):
|
|
25184
|
+
`);
|
|
25082
25185
|
for (const c of campaigns) {
|
|
25083
|
-
const
|
|
25084
|
-
|
|
25186
|
+
const record = c;
|
|
25187
|
+
const hasListMetadata = Boolean(record.exchange_name ?? record.symbol ?? record.type ?? record.address ?? record.escrow_address);
|
|
25188
|
+
if (hasListMetadata) {
|
|
25189
|
+
printCampaignSummary(record, {
|
|
25190
|
+
indent: " ",
|
|
25191
|
+
showLauncher: typeof record.launcher === "string"
|
|
25192
|
+
});
|
|
25193
|
+
} else {
|
|
25194
|
+
const exchange = String(record.exchange_name ?? "").trim();
|
|
25195
|
+
const symbol = String(record.symbol ?? "").trim();
|
|
25196
|
+
const exchangeSymbol = [exchange, symbol].filter(Boolean).join(" ");
|
|
25197
|
+
const label = record.campaign_name ?? record.name ?? (exchangeSymbol || undefined) ?? record.address ?? record.escrow_address ?? c.id ?? "(unnamed campaign)";
|
|
25198
|
+
printText(` - ${label}`);
|
|
25199
|
+
}
|
|
25200
|
+
printText("");
|
|
25085
25201
|
}
|
|
25086
25202
|
}
|
|
25087
25203
|
}
|
|
@@ -25171,9 +25287,12 @@ function createCampaignCommand() {
|
|
|
25171
25287
|
const r = result;
|
|
25172
25288
|
if (r.message) {
|
|
25173
25289
|
printText(String(r.message));
|
|
25290
|
+
} else if ("from" in r || "to" in r || "my_score" in r || "my_meta" in r || "total_meta" in r) {
|
|
25291
|
+
printCampaignProgressCard(r);
|
|
25174
25292
|
} else {
|
|
25175
25293
|
for (const [key, value] of Object.entries(r)) {
|
|
25176
|
-
|
|
25294
|
+
const displayValue = value !== null && typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
25295
|
+
printText(` ${key}: ${displayValue}`);
|
|
25177
25296
|
}
|
|
25178
25297
|
}
|
|
25179
25298
|
}
|
|
@@ -25406,11 +25525,17 @@ function createStakingCommand() {
|
|
|
25406
25525
|
process.exitCode = 1;
|
|
25407
25526
|
}
|
|
25408
25527
|
});
|
|
25409
|
-
staking.command("stake").description("Stake HMT tokens").
|
|
25528
|
+
staking.command("stake [amount]").description("Stake HMT tokens").usage("[amount] [options]").option("-a, --amount <amount>", "Amount of HMT to stake").option("-c, --chain-id <id>", "Chain ID (default: from config)", Number, getDefaultChainId()).option("--json", "Output as JSON").action(async (amountArg, opts) => {
|
|
25529
|
+
const amount = amountArg ?? opts.amount;
|
|
25530
|
+
if (!amount) {
|
|
25531
|
+
printText("Amount is required. Usage: hufi staking stake <amount>");
|
|
25532
|
+
process.exitCode = 1;
|
|
25533
|
+
return;
|
|
25534
|
+
}
|
|
25410
25535
|
const privateKey = requireKey();
|
|
25411
25536
|
try {
|
|
25412
|
-
printText(`Staking ${
|
|
25413
|
-
const hash2 = await stakeHMT(privateKey,
|
|
25537
|
+
printText(`Staking ${amount} HMT on chain ${opts.chainId}...`);
|
|
25538
|
+
const hash2 = await stakeHMT(privateKey, amount, opts.chainId);
|
|
25414
25539
|
if (opts.json) {
|
|
25415
25540
|
printJson({ txHash: hash2 });
|
|
25416
25541
|
} else {
|
|
@@ -25423,11 +25548,17 @@ function createStakingCommand() {
|
|
|
25423
25548
|
process.exitCode = 1;
|
|
25424
25549
|
}
|
|
25425
25550
|
});
|
|
25426
|
-
staking.command("unstake").description("Initiate unstaking (tokens will be locked for the lock period)").
|
|
25551
|
+
staking.command("unstake [amount]").description("Initiate unstaking (tokens will be locked for the lock period)").usage("[amount] [options]").option("-a, --amount <amount>", "Amount of HMT to unstake").option("-c, --chain-id <id>", "Chain ID (default: from config)", Number, getDefaultChainId()).option("--json", "Output as JSON").action(async (amountArg, opts) => {
|
|
25552
|
+
const amount = amountArg ?? opts.amount;
|
|
25553
|
+
if (!amount) {
|
|
25554
|
+
printText("Amount is required. Usage: hufi staking unstake <amount>");
|
|
25555
|
+
process.exitCode = 1;
|
|
25556
|
+
return;
|
|
25557
|
+
}
|
|
25427
25558
|
const privateKey = requireKey();
|
|
25428
25559
|
try {
|
|
25429
|
-
printText(`Unstaking ${
|
|
25430
|
-
const hash2 = await unstakeHMT(privateKey,
|
|
25560
|
+
printText(`Unstaking ${amount} HMT on chain ${opts.chainId}...`);
|
|
25561
|
+
const hash2 = await unstakeHMT(privateKey, amount, opts.chainId);
|
|
25431
25562
|
if (opts.json) {
|
|
25432
25563
|
printJson({ txHash: hash2 });
|
|
25433
25564
|
} else {
|
|
@@ -25600,7 +25731,7 @@ function createDashboardCommand() {
|
|
|
25600
25731
|
|
|
25601
25732
|
// src/cli.ts
|
|
25602
25733
|
var program2 = new Command;
|
|
25603
|
-
program2.name("hufi").description("CLI tool for hu.fi DeFi platform").version("1.0.
|
|
25734
|
+
program2.name("hufi").description("CLI tool for hu.fi DeFi platform").version("1.0.2").option("--config-file <path>", "Custom config file path (default: ~/.hufi-cli/config.json)").option("--key-file <path>", "Custom key file path (default: ~/.hufi-cli/key.json)").hook("preAction", (thisCommand) => {
|
|
25604
25735
|
const opts = thisCommand.opts();
|
|
25605
25736
|
if (opts.configFile) {
|
|
25606
25737
|
setConfigFile(opts.configFile);
|