hufi-cli 1.0.0 → 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 +18 -8
- package/dist/cli.js +186 -50
- 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
|
|
|
@@ -70,10 +70,14 @@ hufi campaign progress --address 0x... --watch --interval 3000
|
|
|
70
70
|
hufi campaign leaderboard --address 0x... # leaderboard
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
`campaign list` and `campaign get` print exact campaign timestamps and round token balances for human-readable text output.
|
|
74
|
+
|
|
73
75
|
#### Campaign Create
|
|
74
76
|
|
|
75
77
|
Requires staked HMT, gas, and fund tokens (USDT/USDC). Creates an escrow contract on-chain.
|
|
76
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
|
+
|
|
77
81
|
```bash
|
|
78
82
|
# Market Making
|
|
79
83
|
hufi campaign create \
|
|
@@ -110,11 +114,16 @@ Running `campaign status/join/progress/leaderboard` without `-a` shows help.
|
|
|
110
114
|
|
|
111
115
|
```bash
|
|
112
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>
|
|
113
118
|
hufi exchange list
|
|
114
|
-
hufi exchange revalidate
|
|
115
|
-
hufi exchange delete
|
|
119
|
+
hufi exchange revalidate mexc
|
|
120
|
+
hufi exchange delete mexc
|
|
116
121
|
```
|
|
117
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
|
+
|
|
118
127
|
### staking
|
|
119
128
|
|
|
120
129
|
| Command | Description |
|
|
@@ -129,8 +138,8 @@ hufi exchange delete --name mexc
|
|
|
129
138
|
hufi staking deposit # show address QR code
|
|
130
139
|
hufi staking status # check your staking
|
|
131
140
|
hufi staking status --address 0x... # check another address
|
|
132
|
-
hufi staking stake
|
|
133
|
-
hufi staking unstake
|
|
141
|
+
hufi staking stake 1000 # stake 1000 HMT
|
|
142
|
+
hufi staking unstake 500 # unstake 500 HMT
|
|
134
143
|
hufi staking withdraw # withdraw unlocked tokens
|
|
135
144
|
```
|
|
136
145
|
|
|
@@ -156,7 +165,7 @@ hufi dashboard --export json
|
|
|
156
165
|
| `-V, --version` | Show version |
|
|
157
166
|
| `-h, --help` | Show help |
|
|
158
167
|
|
|
159
|
-
|
|
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.
|
|
160
169
|
|
|
161
170
|
## Configuration
|
|
162
171
|
|
|
@@ -179,6 +188,7 @@ bun install # install deps
|
|
|
179
188
|
bun run dev -- --help # run from source
|
|
180
189
|
bun run build # build to dist/cli.js
|
|
181
190
|
bun test # unit tests
|
|
191
|
+
./test-cli.sh # full CLI integration coverage
|
|
182
192
|
bun run test:cli # integration tests
|
|
183
193
|
bun run typecheck # type check
|
|
184
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
|
});
|
|
@@ -24966,6 +24996,66 @@ async function runWatchLoop(fn, options = {}) {
|
|
|
24966
24996
|
}
|
|
24967
24997
|
|
|
24968
24998
|
// src/commands/campaign.ts
|
|
24999
|
+
function formatCampaignTimestamp(value) {
|
|
25000
|
+
if (!value)
|
|
25001
|
+
return "-";
|
|
25002
|
+
return value.replace("T", " ").replace(/\.\d+Z$/, "").replace(/Z$/, "");
|
|
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
|
+
}
|
|
25054
|
+
function formatTokenAmount(value, decimals, displayDecimals = 2) {
|
|
25055
|
+
const amount = new bignumber_default(value).dividedBy(new bignumber_default(10).pow(decimals));
|
|
25056
|
+
const rounded = amount.decimalPlaces(displayDecimals, bignumber_default.ROUND_HALF_UP);
|
|
25057
|
+
return rounded.toFormat().replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
|
|
25058
|
+
}
|
|
24969
25059
|
function formatCampaignCreateProgress(confirmations) {
|
|
24970
25060
|
if (confirmations <= 0) {
|
|
24971
25061
|
return "Transaction submitted. Waiting for confirmations...";
|
|
@@ -24976,6 +25066,34 @@ function getLauncherUrl() {
|
|
|
24976
25066
|
const config = loadConfig();
|
|
24977
25067
|
return (config.launcherApiUrl ?? "https://cl.hu.finance").replace(/\/+$/, "");
|
|
24978
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
|
+
}
|
|
24979
25097
|
function createCampaignCommand() {
|
|
24980
25098
|
const campaign = new Command("campaign").description("Campaign management commands");
|
|
24981
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) => {
|
|
@@ -25007,20 +25125,10 @@ function createCampaignCommand() {
|
|
|
25007
25125
|
const key = `${c.chain_id}:${c.address}`;
|
|
25008
25126
|
const joined = joinedKeys.has(key);
|
|
25009
25127
|
const tag = joined ? " [JOINED]" : "";
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
};
|
|
25015
|
-
const fundAmount = new bignumber_default(c.fund_amount);
|
|
25016
|
-
const balanceNum = new bignumber_default(c.balance);
|
|
25017
|
-
const pct = fundAmount.gt(0) ? balanceNum.dividedBy(fundAmount).times(100).toFixed(1) : "0.0";
|
|
25018
|
-
printText(` ${c.exchange_name} ${c.symbol} (${c.type})${tag}`);
|
|
25019
|
-
printText(` chain: ${c.chain_id}`);
|
|
25020
|
-
printText(` address: ${c.address}`);
|
|
25021
|
-
printText(` status: ${c.status}`);
|
|
25022
|
-
printText(` duration: ${c.start_date?.split("T")[0] ?? "-"} ~ ${c.end_date?.split("T")[0] ?? "-"}`);
|
|
25023
|
-
printText(` funded: ${fmt(c.fund_amount)} ${c.fund_token_symbol} paid: ${fmt(c.amount_paid)} balance: ${fmt(c.balance)} (${pct}%)`);
|
|
25128
|
+
printCampaignSummary(c, {
|
|
25129
|
+
indent: " ",
|
|
25130
|
+
joinedTag: tag
|
|
25131
|
+
});
|
|
25024
25132
|
printText("");
|
|
25025
25133
|
}
|
|
25026
25134
|
if (opts.status === "active") {
|
|
@@ -25048,12 +25156,11 @@ function createCampaignCommand() {
|
|
|
25048
25156
|
printText(` chain: ${c.chain_id}`);
|
|
25049
25157
|
printText(` status: ${c.status}`);
|
|
25050
25158
|
const showDecimals = c.fund_token_decimals ?? 0;
|
|
25051
|
-
|
|
25052
|
-
printText(`
|
|
25053
|
-
printText(`
|
|
25054
|
-
printText(`
|
|
25055
|
-
printText(`
|
|
25056
|
-
printText(` end: ${c.end_date}`);
|
|
25159
|
+
printText(` funded: ${formatTokenAmount(c.fund_amount, showDecimals)} ${c.fund_token_symbol}`);
|
|
25160
|
+
printText(` balance: ${formatTokenAmount(c.balance, showDecimals)} ${c.fund_token_symbol}`);
|
|
25161
|
+
printText(` paid: ${formatTokenAmount(c.amount_paid, showDecimals)} ${c.fund_token_symbol}`);
|
|
25162
|
+
printText(` start: ${formatCampaignTimestamp(c.start_date)}`);
|
|
25163
|
+
printText(` end: ${formatCampaignTimestamp(c.end_date)}`);
|
|
25057
25164
|
printText(` launcher: ${c.launcher}`);
|
|
25058
25165
|
}
|
|
25059
25166
|
} catch (err) {
|
|
@@ -25073,10 +25180,24 @@ function createCampaignCommand() {
|
|
|
25073
25180
|
if (campaigns.length === 0) {
|
|
25074
25181
|
printText("No joined campaigns found.");
|
|
25075
25182
|
} else {
|
|
25076
|
-
printText(`Joined campaigns (${campaigns.length})
|
|
25183
|
+
printText(`Joined campaigns (${campaigns.length}):
|
|
25184
|
+
`);
|
|
25077
25185
|
for (const c of campaigns) {
|
|
25078
|
-
const
|
|
25079
|
-
|
|
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("");
|
|
25080
25201
|
}
|
|
25081
25202
|
}
|
|
25082
25203
|
}
|
|
@@ -25166,9 +25287,12 @@ function createCampaignCommand() {
|
|
|
25166
25287
|
const r = result;
|
|
25167
25288
|
if (r.message) {
|
|
25168
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);
|
|
25169
25292
|
} else {
|
|
25170
25293
|
for (const [key, value] of Object.entries(r)) {
|
|
25171
|
-
|
|
25294
|
+
const displayValue = value !== null && typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
25295
|
+
printText(` ${key}: ${displayValue}`);
|
|
25172
25296
|
}
|
|
25173
25297
|
}
|
|
25174
25298
|
}
|
|
@@ -25401,11 +25525,17 @@ function createStakingCommand() {
|
|
|
25401
25525
|
process.exitCode = 1;
|
|
25402
25526
|
}
|
|
25403
25527
|
});
|
|
25404
|
-
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
|
+
}
|
|
25405
25535
|
const privateKey = requireKey();
|
|
25406
25536
|
try {
|
|
25407
|
-
printText(`Staking ${
|
|
25408
|
-
const hash2 = await stakeHMT(privateKey,
|
|
25537
|
+
printText(`Staking ${amount} HMT on chain ${opts.chainId}...`);
|
|
25538
|
+
const hash2 = await stakeHMT(privateKey, amount, opts.chainId);
|
|
25409
25539
|
if (opts.json) {
|
|
25410
25540
|
printJson({ txHash: hash2 });
|
|
25411
25541
|
} else {
|
|
@@ -25418,11 +25548,17 @@ function createStakingCommand() {
|
|
|
25418
25548
|
process.exitCode = 1;
|
|
25419
25549
|
}
|
|
25420
25550
|
});
|
|
25421
|
-
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
|
+
}
|
|
25422
25558
|
const privateKey = requireKey();
|
|
25423
25559
|
try {
|
|
25424
|
-
printText(`Unstaking ${
|
|
25425
|
-
const hash2 = await unstakeHMT(privateKey,
|
|
25560
|
+
printText(`Unstaking ${amount} HMT on chain ${opts.chainId}...`);
|
|
25561
|
+
const hash2 = await unstakeHMT(privateKey, amount, opts.chainId);
|
|
25426
25562
|
if (opts.json) {
|
|
25427
25563
|
printJson({ txHash: hash2 });
|
|
25428
25564
|
} else {
|
|
@@ -25595,7 +25731,7 @@ function createDashboardCommand() {
|
|
|
25595
25731
|
|
|
25596
25732
|
// src/cli.ts
|
|
25597
25733
|
var program2 = new Command;
|
|
25598
|
-
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) => {
|
|
25599
25735
|
const opts = thisCommand.opts();
|
|
25600
25736
|
if (opts.configFile) {
|
|
25601
25737
|
setConfigFile(opts.configFile);
|