@bankr/cli 0.3.13 → 0.3.32
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 +34 -10
- package/dist/cli.js +34 -18
- package/dist/commands/balances.js +3 -3
- package/dist/commands/cancel.js +1 -3
- package/dist/commands/club.js +6 -16
- package/dist/commands/config.js +1 -2
- package/dist/commands/fees.js +79 -64
- package/dist/commands/files.js +17 -39
- package/dist/commands/launch.d.ts +4 -0
- package/dist/commands/launch.js +56 -18
- package/dist/commands/llm.d.ts +10 -0
- package/dist/commands/llm.js +510 -99
- package/dist/commands/login.d.ts +14 -0
- package/dist/commands/login.js +57 -57
- package/dist/commands/portfolio.js +4 -4
- package/dist/commands/profile.js +6 -5
- package/dist/commands/prompt.js +36 -26
- package/dist/commands/sign.js +9 -16
- package/dist/commands/sounds.js +6 -15
- package/dist/commands/status.js +2 -4
- package/dist/commands/submit.js +13 -24
- package/dist/commands/swap.js +11 -11
- package/dist/commands/tokens.js +4 -4
- package/dist/commands/transfer.js +10 -10
- package/dist/commands/update.js +2 -2
- package/dist/commands/webhooks.js +27 -67
- package/dist/commands/whoami.js +4 -4
- package/dist/commands/x402.d.ts +3 -0
- package/dist/commands/x402.js +95 -115
- package/dist/lib/api.d.ts +17 -3
- package/dist/lib/api.js +34 -43
- package/dist/lib/explorer.js +1 -0
- package/dist/lib/output.d.ts +5 -0
- package/dist/lib/output.js +18 -0
- package/dist/lib/passthrough.d.ts +21 -0
- package/dist/lib/passthrough.js +26 -0
- package/dist/lib/tokens.d.ts +0 -5
- package/dist/lib/tokens.js +0 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,12 @@ bankr agent -c "compare them"
|
|
|
73
73
|
|
|
74
74
|
# Continue a specific thread
|
|
75
75
|
bankr agent --thread thr_ABC123 "tell me more"
|
|
76
|
+
|
|
77
|
+
# Max Mode: pick the model. Only the two newest releases of each line, and
|
|
78
|
+
# only frontier / flagship / balanced lines, are accepted (the same set the
|
|
79
|
+
# web picker shows); anything else is rejected and the error lists every
|
|
80
|
+
# valid id.
|
|
81
|
+
bankr agent --model claude-opus-5 "audit this contract"
|
|
76
82
|
```
|
|
77
83
|
|
|
78
84
|
### Token Launch
|
|
@@ -108,26 +114,44 @@ bankr agent cancel job_ABC123DEF456
|
|
|
108
114
|
Launch Claude Code / OpenCode routed through the Bankr gateway:
|
|
109
115
|
|
|
110
116
|
```bash
|
|
111
|
-
# Claude Code
|
|
112
|
-
|
|
117
|
+
# Claude Code — `bankr claude` is a top-level alias for `bankr llm claude`
|
|
118
|
+
# (auto-translates dotted model IDs to the Anthropic wire format)
|
|
119
|
+
bankr claude --model claude-opus-5
|
|
120
|
+
|
|
121
|
+
# Or use dotted form — bankr claude rewrites it to claude-opus-4-8
|
|
122
|
+
bankr claude --model claude-opus-4.8
|
|
113
123
|
|
|
114
|
-
#
|
|
115
|
-
|
|
124
|
+
# The [1m] suffix is optional via the gateway, but MUST be quoted if used
|
|
125
|
+
# (zsh reads [1m] as a glob)
|
|
126
|
+
bankr claude --model "claude-opus-5[1m]"
|
|
116
127
|
|
|
117
128
|
# OpenCode (use the bankr/ prefix with the gateway-canonical dotted form)
|
|
118
129
|
bankr llm opencode -m bankr/claude-opus-4.8
|
|
119
130
|
```
|
|
120
131
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
`
|
|
124
|
-
(`claude-opus-4.8`), Claude Code silently falls back to its default —
|
|
125
|
-
the flag appears honored but the real request is a different model.
|
|
132
|
+
Because `claude` is a command, it shadows the default prompt fallthrough:
|
|
133
|
+
`bankr claude ...` launches Claude Code rather than prompting the agent. Use
|
|
134
|
+
`bankr agent "..."` to send the agent a prompt starting with that word.
|
|
126
135
|
|
|
127
|
-
|
|
136
|
+
**Claude Code model format**: Claude Code's `--model` flag expects Anthropic
|
|
137
|
+
wire IDs, which use dashes rather than dots in the version (`claude-opus-5`,
|
|
138
|
+
`claude-sonnet-5`, `claude-opus-4-8`, `claude-haiku-4-5`). If you pass the
|
|
139
|
+
gateway-canonical dotted form (`claude-opus-4.8`), Claude Code silently falls
|
|
140
|
+
back to its default — the flag appears honored but the real request is a
|
|
141
|
+
different model.
|
|
142
|
+
|
|
143
|
+
`bankr claude` translates dotted → dashed for you. If you use older
|
|
128
144
|
versions of the CLI (or invoke `claude` directly via `ANTHROPIC_BASE_URL`),
|
|
129
145
|
pass the dashed form explicitly.
|
|
130
146
|
|
|
147
|
+
**1M context tier**: the gateway strips Claude Code's `[1m]` suffix and enables
|
|
148
|
+
the 1M beta from the model's own context window, so it's **optional here** —
|
|
149
|
+
`claude-opus-5` and `claude-opus-5[1m]` send an identical upstream request. If
|
|
150
|
+
you do use it, **quote it**: in zsh (the macOS default) `[1m]` is a glob
|
|
151
|
+
character class, so an unquoted id aborts with `zsh: no matches found:
|
|
152
|
+
claude-opus-5[1m]` before the CLI ever runs. Bash passes it through literally,
|
|
153
|
+
so the same command can work on one machine and fail on another.
|
|
154
|
+
|
|
131
155
|
### Configuration
|
|
132
156
|
|
|
133
157
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,8 @@ import { skillsCommand } from "./commands/capabilities.js";
|
|
|
10
10
|
import { cancelCommand } from "./commands/cancel.js";
|
|
11
11
|
import { configGetCommand, configSetCommand } from "./commands/config.js";
|
|
12
12
|
import { setConfigPath } from "./lib/config.js";
|
|
13
|
+
import { getErrorMessage } from "./lib/errors.js";
|
|
14
|
+
import { passthroughArgs } from "./lib/passthrough.js";
|
|
13
15
|
import { claudeCommand, creditsCommand, creditsAddCommand, creditsAutoCommand, modelsCommand, opencodeCommand, setupClaudeCodeCommand, setupCursorCommand, setupOpenclawCommand, setupOpenCodeCommand, } from "./commands/llm.js";
|
|
14
16
|
import { feesCommand, feesClaimCommand, feesClaimWalletCommand, } from "./commands/fees.js";
|
|
15
17
|
import { launchCommand } from "./commands/launch.js";
|
|
@@ -44,14 +46,12 @@ async function readPromptInput() {
|
|
|
44
46
|
}
|
|
45
47
|
const text = Buffer.concat(chunks).toString().trim();
|
|
46
48
|
if (!text) {
|
|
47
|
-
output.
|
|
48
|
-
process.exit(1);
|
|
49
|
+
output.fatal("No input received from stdin");
|
|
49
50
|
}
|
|
50
51
|
return text;
|
|
51
52
|
}
|
|
52
53
|
if (output.isNonInteractive()) {
|
|
53
|
-
output.
|
|
54
|
-
process.exit(1);
|
|
54
|
+
output.fatal('No prompt provided. Pass it as an argument: bankr agent "your prompt"');
|
|
55
55
|
}
|
|
56
56
|
// Interactive input — shell expansion does not apply here
|
|
57
57
|
const text = await input({
|
|
@@ -304,8 +304,7 @@ const agentCmd = program
|
|
|
304
304
|
const joined = text.join(" ").trim();
|
|
305
305
|
if (!joined) {
|
|
306
306
|
if (output.isNonInteractive()) {
|
|
307
|
-
output.
|
|
308
|
-
process.exit(1);
|
|
307
|
+
output.fatal('No prompt provided. Pass it as an argument: bankr agent "your prompt"');
|
|
309
308
|
}
|
|
310
309
|
agentCmd.help();
|
|
311
310
|
return;
|
|
@@ -340,18 +339,20 @@ agentCmd
|
|
|
340
339
|
program.commandsGroup("Tokens");
|
|
341
340
|
program
|
|
342
341
|
.command("launch")
|
|
343
|
-
.description("Launch a token on Base
|
|
342
|
+
.description("Launch a token on Base, Robinhood Chain or Arbitrum (interactive wizard)")
|
|
344
343
|
.optionsGroup("Token")
|
|
345
344
|
.option("--name <name>", "Token name")
|
|
346
345
|
.option("--symbol <symbol>", "Token symbol (default: auto-generated from name)")
|
|
347
346
|
.option("--image <url>", "Token image URL")
|
|
348
|
-
.option("--chain <chain>", "Chain to launch on: base, robinhood (default: base)")
|
|
347
|
+
.option("--chain <chain>", "Chain to launch on: base, robinhood, arbitrum (default: base)")
|
|
348
|
+
.option("--no-vesting", "Turn off the default 15% creator vesting (sell 100% of supply into the pool)")
|
|
349
349
|
.optionsGroup("Links")
|
|
350
350
|
.option("--tweet <url>", "Associated tweet URL")
|
|
351
351
|
.option("--website <url>", "Project website URL")
|
|
352
352
|
.optionsGroup("Fees")
|
|
353
353
|
.option("--fee <recipient>", "Fee recipient (username, ENS, or address)")
|
|
354
354
|
.option("--fee-type <type>", "Fee recipient type: x, farcaster, ens, wallet (default: x)")
|
|
355
|
+
.option("--quote-only-fees", "Collect all creator fees in the pool's quote token (e.g. WETH) instead of a mix of your token and the quote token")
|
|
355
356
|
.optionsGroup("")
|
|
356
357
|
.option("-y, --yes", "Skip confirmation prompt")
|
|
357
358
|
.option("--simulate", "Simulate the deploy without broadcasting a transaction")
|
|
@@ -361,10 +362,12 @@ program
|
|
|
361
362
|
symbol: opts.symbol,
|
|
362
363
|
image: opts.image,
|
|
363
364
|
chain: opts.chain,
|
|
365
|
+
vesting: opts.vesting,
|
|
364
366
|
tweet: opts.tweet,
|
|
365
367
|
website: opts.website,
|
|
366
368
|
fee: opts.fee,
|
|
367
369
|
feeType: opts.feeType,
|
|
370
|
+
quoteOnlyFees: opts.quoteOnlyFees,
|
|
368
371
|
yes: opts.yes,
|
|
369
372
|
simulate: opts.simulate,
|
|
370
373
|
});
|
|
@@ -471,6 +474,7 @@ llmCmd
|
|
|
471
474
|
.command("models")
|
|
472
475
|
.description("List available models")
|
|
473
476
|
.option("--private", "Show only models that support private (:private) inference")
|
|
477
|
+
.option("--zdr", "Show only models that support zero data retention (:zdr)")
|
|
474
478
|
.action(modelsCommand);
|
|
475
479
|
const llmCreditsCmd = llmCmd
|
|
476
480
|
.command("credits")
|
|
@@ -498,6 +502,7 @@ llmSetupCmd
|
|
|
498
502
|
.command("openclaw")
|
|
499
503
|
.description("OpenClaw config")
|
|
500
504
|
.option("--install", "Write config to ~/.openclaw/openclaw.json")
|
|
505
|
+
.option("--images", "Also route image generation (gpt-image-2) through the gateway — takes over OpenClaw's 'openai' provider slot")
|
|
501
506
|
.action(setupOpenclawCommand);
|
|
502
507
|
llmSetupCmd
|
|
503
508
|
.command("opencode")
|
|
@@ -512,25 +517,38 @@ llmSetupCmd
|
|
|
512
517
|
.command("claude")
|
|
513
518
|
.description("Claude Code config")
|
|
514
519
|
.action(setupClaudeCodeCommand);
|
|
520
|
+
// `helpOption(false)` forwards `-h`/`--help` to the spawned tool instead of
|
|
521
|
+
// letting commander print ours — on a pure passthrough launcher the user
|
|
522
|
+
// almost always wants the tool's help. Use `bankr llm --help` for ours.
|
|
515
523
|
llmCmd
|
|
516
524
|
.command("claude")
|
|
517
525
|
.description("Run Claude Code via gateway")
|
|
518
526
|
.allowUnknownOption()
|
|
519
527
|
.allowExcessArguments()
|
|
528
|
+
.helpOption(false)
|
|
520
529
|
.action(async () => {
|
|
521
|
-
|
|
522
|
-
const args = idx >= 0 ? process.argv.slice(idx + 1) : [];
|
|
523
|
-
await claudeCommand(args);
|
|
530
|
+
await claudeCommand(passthroughArgs("claude"));
|
|
524
531
|
});
|
|
525
532
|
llmCmd
|
|
526
533
|
.command("opencode")
|
|
527
534
|
.description("Run OpenCode via gateway")
|
|
528
535
|
.allowUnknownOption()
|
|
529
536
|
.allowExcessArguments()
|
|
537
|
+
.helpOption(false)
|
|
530
538
|
.action(async () => {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
539
|
+
await opencodeCommand(passthroughArgs("opencode"));
|
|
540
|
+
});
|
|
541
|
+
// Top-level shorthand for the most-used launcher. This shadows the default
|
|
542
|
+
// prompt fallthrough, so `bankr claude ...` launches Claude Code rather than
|
|
543
|
+
// prompting the agent about Claude — ask the agent via `bankr agent "..."`.
|
|
544
|
+
program
|
|
545
|
+
.command("claude")
|
|
546
|
+
.description("Run Claude Code via gateway (alias for `bankr llm claude`)")
|
|
547
|
+
.allowUnknownOption()
|
|
548
|
+
.allowExcessArguments()
|
|
549
|
+
.helpOption(false)
|
|
550
|
+
.action(async () => {
|
|
551
|
+
await claudeCommand(passthroughArgs("claude"));
|
|
534
552
|
});
|
|
535
553
|
// Token search and info
|
|
536
554
|
program.commandsGroup("Tokens");
|
|
@@ -1030,8 +1048,7 @@ program
|
|
|
1030
1048
|
.action(async (text, opts) => {
|
|
1031
1049
|
if (text.length === 0) {
|
|
1032
1050
|
if (output.isNonInteractive()) {
|
|
1033
|
-
output.
|
|
1034
|
-
process.exit(1);
|
|
1051
|
+
output.fatal('No prompt provided. Pass it as an argument: bankr "your prompt"');
|
|
1035
1052
|
}
|
|
1036
1053
|
program.help();
|
|
1037
1054
|
return;
|
|
@@ -1048,8 +1065,7 @@ async function main() {
|
|
|
1048
1065
|
console.log();
|
|
1049
1066
|
process.exit(0);
|
|
1050
1067
|
}
|
|
1051
|
-
output.
|
|
1052
|
-
process.exit(1);
|
|
1068
|
+
output.fatal(getErrorMessage(err));
|
|
1053
1069
|
}
|
|
1054
1070
|
await checkForUpdate(pkg.version);
|
|
1055
1071
|
}
|
|
@@ -2,6 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import { getBalances, } from "../lib/api.js";
|
|
3
3
|
import * as output from "../lib/output.js";
|
|
4
4
|
import { formatUsd, formatBalance } from "../lib/output.js";
|
|
5
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
5
6
|
import { CHAIN_LABELS, VALID_CHAINS } from "../lib/chains.js";
|
|
6
7
|
const BRAND = chalk.hex("#FF613D");
|
|
7
8
|
const DIM = chalk.dim;
|
|
@@ -42,8 +43,7 @@ export async function balancesCommand(opts) {
|
|
|
42
43
|
if (chains) {
|
|
43
44
|
const invalid = chains.filter((c) => !VALID_CHAINS.has(c));
|
|
44
45
|
if (invalid.length > 0) {
|
|
45
|
-
output.
|
|
46
|
-
process.exit(1);
|
|
46
|
+
output.fatal(`Invalid chain${invalid.length > 1 ? "s" : ""}: ${invalid.join(", ")}. Valid chains: ${[...VALID_CHAINS].join(", ")}`);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
const spin = output.spinner("Fetching balances...");
|
|
@@ -53,7 +53,7 @@ export async function balancesCommand(opts) {
|
|
|
53
53
|
spin.succeed("Balances loaded");
|
|
54
54
|
}
|
|
55
55
|
catch (err) {
|
|
56
|
-
spin.fail(`Failed to fetch balances: ${err
|
|
56
|
+
spin.fail(`Failed to fetch balances: ${getErrorMessage(err)}`);
|
|
57
57
|
process.exit(1);
|
|
58
58
|
}
|
|
59
59
|
// JSON output mode
|
package/dist/commands/cancel.js
CHANGED
|
@@ -17,9 +17,7 @@ export async function cancelCommand(jobId) {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
catch (err) {
|
|
20
|
-
|
|
21
|
-
output.error(err.message);
|
|
22
|
-
process.exit(1);
|
|
20
|
+
output.failWith(spin, "Failed to cancel job", err);
|
|
23
21
|
}
|
|
24
22
|
}
|
|
25
23
|
//# sourceMappingURL=cancel.js.map
|
package/dist/commands/club.js
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { confirm } from "@inquirer/prompts";
|
|
2
|
-
import {
|
|
2
|
+
import { getApiUrl } from "../lib/config.js";
|
|
3
|
+
import { authHeaders } from "../lib/api.js";
|
|
3
4
|
import * as output from "../lib/output.js";
|
|
4
5
|
import { getErrorMessage } from "../lib/errors.js";
|
|
5
|
-
function authHeaders() {
|
|
6
|
-
return {
|
|
7
|
-
"X-API-Key": requireApiKey(),
|
|
8
|
-
"Content-Type": "application/json",
|
|
9
|
-
"User-Agent": CLI_USER_AGENT,
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
6
|
// Common Base tokens accepted as `--token` shortcuts. Lowercase to bypass
|
|
13
7
|
// viem's strict EIP-55 checksum on the API's isAddress() check; the route
|
|
14
8
|
// resolves to lowercase server-side anyway. Inlined here (not imported
|
|
@@ -79,8 +73,7 @@ export async function clubStatusCommand() {
|
|
|
79
73
|
}
|
|
80
74
|
catch (err) {
|
|
81
75
|
spinner.stop();
|
|
82
|
-
output.
|
|
83
|
-
process.exit(1);
|
|
76
|
+
output.fatal(getErrorMessage(err));
|
|
84
77
|
}
|
|
85
78
|
}
|
|
86
79
|
// ── Sign up ─────────────────────────────────────────────────────────────
|
|
@@ -95,8 +88,7 @@ export async function clubSignupCommand(opts) {
|
|
|
95
88
|
if (opts.token) {
|
|
96
89
|
const resolved = resolvePaymentToken(opts.token);
|
|
97
90
|
if (!resolved) {
|
|
98
|
-
output.
|
|
99
|
-
process.exit(1);
|
|
91
|
+
output.fatal(`Unknown token "${opts.token}". Use one of USDC, BNKR, ETH, or pass a 0x-prefixed Base address.`);
|
|
100
92
|
}
|
|
101
93
|
tokenAddress = resolved;
|
|
102
94
|
}
|
|
@@ -143,8 +135,7 @@ export async function clubSignupCommand(opts) {
|
|
|
143
135
|
output.dim("Run `bankr club` to see your status.");
|
|
144
136
|
}
|
|
145
137
|
catch (err) {
|
|
146
|
-
output.
|
|
147
|
-
process.exit(1);
|
|
138
|
+
output.fatal(getErrorMessage(err));
|
|
148
139
|
}
|
|
149
140
|
}
|
|
150
141
|
// ── Cancel ──────────────────────────────────────────────────────────────
|
|
@@ -187,8 +178,7 @@ export async function clubCancelCommand(opts) {
|
|
|
187
178
|
output.dim("You'll keep Bankr Club access until the end of the current billing period.");
|
|
188
179
|
}
|
|
189
180
|
catch (err) {
|
|
190
|
-
output.
|
|
191
|
-
process.exit(1);
|
|
181
|
+
output.fatal(getErrorMessage(err));
|
|
192
182
|
}
|
|
193
183
|
}
|
|
194
184
|
//# sourceMappingURL=club.js.map
|
package/dist/commands/config.js
CHANGED
|
@@ -12,8 +12,7 @@ function isValidKey(key) {
|
|
|
12
12
|
}
|
|
13
13
|
function requireValidKey(key) {
|
|
14
14
|
if (!isValidKey(key)) {
|
|
15
|
-
output.
|
|
16
|
-
process.exit(1);
|
|
15
|
+
output.fatal(`Unknown config key "${key}". Valid keys: ${ALLOWED_KEYS.join(", ")}`);
|
|
17
16
|
}
|
|
18
17
|
}
|
|
19
18
|
function displayValue(key, value) {
|
package/dist/commands/fees.js
CHANGED
|
@@ -6,6 +6,7 @@ import { emitCESP } from "../lib/cesp/engine.js";
|
|
|
6
6
|
import { requireApiKey } from "../lib/config.js";
|
|
7
7
|
import { txExplorer } from "../lib/explorer.js";
|
|
8
8
|
import * as output from "../lib/output.js";
|
|
9
|
+
import { getErrorMessage } from "../lib/errors.js";
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
// Constants
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
@@ -214,8 +215,13 @@ function renderDashboard(data) {
|
|
|
214
215
|
}
|
|
215
216
|
console.log(`${DIM(` Wallet: ${data.address}`)}`);
|
|
216
217
|
// Tokens can span launch chains ("chain" absent on pre-multichain rows).
|
|
218
|
+
const launchChainLabel = {
|
|
219
|
+
base: "Base",
|
|
220
|
+
robinhood: "Robinhood",
|
|
221
|
+
arbitrum: "Arbitrum",
|
|
222
|
+
};
|
|
217
223
|
const chainNames = [
|
|
218
|
-
...new Set(data.tokens.map((t) =>
|
|
224
|
+
...new Set(data.tokens.map((t) => launchChainLabel[t.chain ?? "base"] ?? "Base")),
|
|
219
225
|
].join(" + ") || "Base";
|
|
220
226
|
console.log(`${DIM(` Chain: ${chainNames} \u00b7 Tokens: ${data.tokens.length} \u00b7 Period: ${data.days} days`)}`);
|
|
221
227
|
if (data.tokens.length === 0) {
|
|
@@ -283,28 +289,33 @@ function renderDashboard(data) {
|
|
|
283
289
|
console.log(` ${BRAND_BOLD("\u2550".repeat(62))}`);
|
|
284
290
|
console.log("");
|
|
285
291
|
for (const token of data.tokens) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
292
|
+
// Numeraire leg = the side whose label differs from the launched token's
|
|
293
|
+
// symbol \u2014 stock-paired launches label it with the stock ticker, not WETH.
|
|
294
|
+
const tokenIsToken0 = token.token0Label === token.symbol;
|
|
295
|
+
const numeraireLabel = tokenIsToken0
|
|
296
|
+
? token.token1Label
|
|
297
|
+
: token.token0Label;
|
|
298
|
+
const claimableNumeraire = tokenIsToken0
|
|
291
299
|
? token.claimable.token1
|
|
292
300
|
: token.claimable.token0;
|
|
293
|
-
const
|
|
294
|
-
? token.
|
|
295
|
-
: token.
|
|
296
|
-
const
|
|
301
|
+
const claimableToken = tokenIsToken0
|
|
302
|
+
? token.claimable.token0
|
|
303
|
+
: token.claimable.token1;
|
|
304
|
+
const claimedNumeraire = tokenIsToken0
|
|
297
305
|
? token.claimed.token1
|
|
298
306
|
: token.claimed.token0;
|
|
299
|
-
const
|
|
307
|
+
const claimedToken = tokenIsToken0
|
|
308
|
+
? token.claimed.token0
|
|
309
|
+
: token.claimed.token1;
|
|
310
|
+
const tokenSymbol = tokenIsToken0 ? token.token0Label : token.token1Label;
|
|
300
311
|
const sourceLabel = token.source === "clanker" ? DIM(" [Clanker]") : DIM(" [Doppler]");
|
|
301
312
|
console.log(` ${BOLD(WHITE(token.name))} ${DIM("($" + token.symbol + ")")}${sourceLabel}`);
|
|
302
313
|
const shareInfo = token.source === "clanker"
|
|
303
314
|
? `${DIM("Creator")} ${DIM("\u00b7 Token:")} ${DIM(token.tokenAddress)}`
|
|
304
315
|
: `${DIM("Share:")} ${token.share} ${DIM("\u00b7 Token:")} ${DIM(token.tokenAddress)}`;
|
|
305
316
|
console.log(` ${shareInfo}`);
|
|
306
|
-
console.log(` ${DIM("Claimable:")} ${BRIGHT_GREEN(
|
|
307
|
-
console.log(` ${DIM("Claimed:")} ${WHITE(
|
|
317
|
+
console.log(` ${DIM("Claimable:")} ${BRIGHT_GREEN(claimableNumeraire.padStart(12))} ${numeraireLabel} ${DIM("|")} ${BRIGHT_GREEN(claimableToken.padStart(18))} ${tokenSymbol}`);
|
|
318
|
+
console.log(` ${DIM("Claimed:")} ${WHITE(claimedNumeraire.padStart(12))} ${numeraireLabel} ${DIM("|")} ${WHITE(claimedToken.padStart(18))} ${tokenSymbol} ${DIM(`(${token.claimed.count} claims)`)}`);
|
|
308
319
|
console.log("");
|
|
309
320
|
}
|
|
310
321
|
console.log(` ${BRAND_BOLD("\u2550".repeat(62))}`);
|
|
@@ -323,8 +334,7 @@ export async function feesCommand(address, opts) {
|
|
|
323
334
|
if (opts.days) {
|
|
324
335
|
days = parseInt(opts.days, 10);
|
|
325
336
|
if (isNaN(days) || days < MIN_DAYS || days > MAX_DAYS) {
|
|
326
|
-
output.
|
|
327
|
-
process.exit(1);
|
|
337
|
+
output.fatal(`Days must be between ${MIN_DAYS} and ${MAX_DAYS}`);
|
|
328
338
|
}
|
|
329
339
|
}
|
|
330
340
|
// --token flag: look up fees by token address (no wallet needed)
|
|
@@ -343,8 +353,7 @@ export async function feesCommand(address, opts) {
|
|
|
343
353
|
catch (err) {
|
|
344
354
|
spin.fail("Failed to fetch token fee data");
|
|
345
355
|
emitCESP("task.error");
|
|
346
|
-
output.
|
|
347
|
-
process.exit(1);
|
|
356
|
+
output.fatal(getErrorMessage(err));
|
|
348
357
|
}
|
|
349
358
|
return;
|
|
350
359
|
}
|
|
@@ -364,9 +373,7 @@ export async function feesCommand(address, opts) {
|
|
|
364
373
|
spin.succeed(`Wallet: ${walletAddress}`);
|
|
365
374
|
}
|
|
366
375
|
catch (err) {
|
|
367
|
-
|
|
368
|
-
output.error(err.message);
|
|
369
|
-
process.exit(1);
|
|
376
|
+
output.failWith(spin, "Failed to fetch wallet info", err);
|
|
370
377
|
}
|
|
371
378
|
}
|
|
372
379
|
// Fetch data
|
|
@@ -384,8 +391,7 @@ export async function feesCommand(address, opts) {
|
|
|
384
391
|
catch (err) {
|
|
385
392
|
spin.fail("Failed to fetch fee data");
|
|
386
393
|
emitCESP("task.error");
|
|
387
|
-
output.
|
|
388
|
-
process.exit(1);
|
|
394
|
+
output.fatal(getErrorMessage(err));
|
|
389
395
|
}
|
|
390
396
|
}
|
|
391
397
|
export async function feesClaimCommand(tokenAddress, opts) {
|
|
@@ -445,8 +451,7 @@ export async function feesClaimCommand(tokenAddress, opts) {
|
|
|
445
451
|
catch (err) {
|
|
446
452
|
spin.fail("Claim submission failed");
|
|
447
453
|
emitCESP("task.error");
|
|
448
|
-
output.
|
|
449
|
-
process.exit(1);
|
|
454
|
+
output.fatal(getErrorMessage(err));
|
|
450
455
|
}
|
|
451
456
|
}
|
|
452
457
|
// ---------------------------------------------------------------------------
|
|
@@ -459,8 +464,7 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
459
464
|
address = undefined;
|
|
460
465
|
}
|
|
461
466
|
if (output.isNonInteractive() && !opts.all) {
|
|
462
|
-
output.
|
|
463
|
-
process.exit(1);
|
|
467
|
+
output.fatal("Use --all to select all tokens in non-interactive mode, or run interactively to choose");
|
|
464
468
|
}
|
|
465
469
|
// Load .env from cwd if present (no dependency needed)
|
|
466
470
|
try {
|
|
@@ -498,13 +502,11 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
498
502
|
privateKey = process.env.BANKR_PRIVATE_KEY.trim();
|
|
499
503
|
}
|
|
500
504
|
else if (!address) {
|
|
501
|
-
output.
|
|
502
|
-
process.exit(1);
|
|
505
|
+
output.fatal("Address required when no private key is available. Provide an address argument or set BANKR_PRIVATE_KEY in .env");
|
|
503
506
|
}
|
|
504
507
|
else {
|
|
505
508
|
if (output.isNonInteractive()) {
|
|
506
|
-
output.
|
|
507
|
-
process.exit(1);
|
|
509
|
+
output.fatal("Private key required in non-interactive mode. Use --private-key or set BANKR_PRIVATE_KEY");
|
|
508
510
|
}
|
|
509
511
|
privateKey = await password({
|
|
510
512
|
message: "Enter private key for this wallet (0x...):",
|
|
@@ -523,8 +525,7 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
523
525
|
account = privateKeyToAccount(privateKey);
|
|
524
526
|
}
|
|
525
527
|
catch {
|
|
526
|
-
output.
|
|
527
|
-
process.exit(1);
|
|
528
|
+
output.fatal("Invalid private key format");
|
|
528
529
|
}
|
|
529
530
|
// Step 3: Resolve address — derive from key if not provided, verify match if provided
|
|
530
531
|
if (!address) {
|
|
@@ -533,12 +534,10 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
533
534
|
}
|
|
534
535
|
else {
|
|
535
536
|
if (!isAddress(address)) {
|
|
536
|
-
output.
|
|
537
|
-
process.exit(1);
|
|
537
|
+
output.fatal("Invalid EVM address");
|
|
538
538
|
}
|
|
539
539
|
if (account.address.toLowerCase() !== address.toLowerCase()) {
|
|
540
|
-
output.
|
|
541
|
-
process.exit(1);
|
|
540
|
+
output.fatal(`Private key does not match address ${address}. Derived: ${account.address}`);
|
|
542
541
|
}
|
|
543
542
|
}
|
|
544
543
|
console.log();
|
|
@@ -550,42 +549,62 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
550
549
|
const result = await getBeneficiaryFeesStreaming(address, (progress) => {
|
|
551
550
|
fetchSpin.text = `Scanning pools... ${progress.scanned}/${progress.total} (${progress.found ?? 0} with claimable fees)`;
|
|
552
551
|
});
|
|
553
|
-
|
|
552
|
+
// A time-budget stop leaves `scanned` below `totalLaunches`; report the
|
|
553
|
+
// number actually scanned so this line can't contradict the warning below.
|
|
554
|
+
const scanned = result.scanned ?? result.totalLaunches;
|
|
555
|
+
fetchSpin.succeed(`Scanned ${scanned} launches — ${result.poolsWithShares} pools with shares, ${result.tokens.length} with claimable fees`);
|
|
556
|
+
if (result.truncated) {
|
|
557
|
+
output.warn(`Scan stopped early at ${scanned} launches — this wallet has more than the server scans in one request. Claimable fees beyond that are not listed.`);
|
|
558
|
+
}
|
|
554
559
|
allTokens = result.tokens;
|
|
555
560
|
}
|
|
556
561
|
catch (err) {
|
|
557
|
-
|
|
558
|
-
output.error(err.message);
|
|
559
|
-
process.exit(1);
|
|
562
|
+
output.failWith(fetchSpin, "Failed to fetch fee data", err);
|
|
560
563
|
}
|
|
561
564
|
// Step 5: Filter to tokens with claimable fees (API already filters, but
|
|
562
565
|
// double-check for any rounding edge cases)
|
|
563
566
|
const parseAmount = (s) => parseFloat(s.replace(/^[<>]/, "")) || 0;
|
|
567
|
+
// Numeraire leg = the side whose label differs from the launched token's
|
|
568
|
+
// symbol — stock-paired launches label it with the stock ticker, not WETH.
|
|
569
|
+
const numeraireLegOf = (token) => {
|
|
570
|
+
const tokenIsToken0 = token.token0Label === token.symbol;
|
|
571
|
+
return {
|
|
572
|
+
label: tokenIsToken0 ? token.token1Label : token.token0Label,
|
|
573
|
+
claimable: tokenIsToken0
|
|
574
|
+
? token.claimable.token1
|
|
575
|
+
: token.claimable.token0,
|
|
576
|
+
tokenClaimable: tokenIsToken0
|
|
577
|
+
? token.claimable.token0
|
|
578
|
+
: token.claimable.token1,
|
|
579
|
+
};
|
|
580
|
+
};
|
|
564
581
|
const claimableTokens = allTokens.filter((token) => {
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
? token.claimable.token0
|
|
568
|
-
: token.claimable.token1;
|
|
569
|
-
const claimableToken = wethIsToken0
|
|
570
|
-
? token.claimable.token1
|
|
571
|
-
: token.claimable.token0;
|
|
572
|
-
return parseAmount(claimableWeth) > 0 || parseAmount(claimableToken) > 0;
|
|
582
|
+
const leg = numeraireLegOf(token);
|
|
583
|
+
return (parseAmount(leg.claimable) > 0 || parseAmount(leg.tokenClaimable) > 0);
|
|
573
584
|
});
|
|
574
585
|
if (claimableTokens.length === 0) {
|
|
575
586
|
console.log();
|
|
576
587
|
output.dim("No claimable fees found for this wallet.");
|
|
577
588
|
return;
|
|
578
589
|
}
|
|
579
|
-
// Show total claimable WETH
|
|
590
|
+
// Show total claimable WETH — WETH legs only; stock-paired numeraires are a
|
|
591
|
+
// different unit and get their own per-ticker lines.
|
|
580
592
|
const totalWeth = claimableTokens.reduce((sum, token) => {
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
? token.claimable.token0
|
|
584
|
-
: token.claimable.token1;
|
|
585
|
-
return sum + parseAmount(wethStr);
|
|
593
|
+
const leg = numeraireLegOf(token);
|
|
594
|
+
return leg.label === "WETH" ? sum + parseAmount(leg.claimable) : sum;
|
|
586
595
|
}, 0);
|
|
596
|
+
const nonWethTotals = new Map();
|
|
597
|
+
for (const token of claimableTokens) {
|
|
598
|
+
const leg = numeraireLegOf(token);
|
|
599
|
+
if (leg.label !== "WETH" && parseAmount(leg.claimable) > 0) {
|
|
600
|
+
nonWethTotals.set(leg.label, (nonWethTotals.get(leg.label) ?? 0) + parseAmount(leg.claimable));
|
|
601
|
+
}
|
|
602
|
+
}
|
|
587
603
|
console.log();
|
|
588
604
|
output.keyValue("Total claimable", `${totalWeth.toFixed(6)} WETH across ${claimableTokens.length} token${claimableTokens.length !== 1 ? "s" : ""}`);
|
|
605
|
+
for (const [label, amount] of nonWethTotals) {
|
|
606
|
+
output.keyValue("", `${amount.toFixed(6)} ${label} (stock-paired)`);
|
|
607
|
+
}
|
|
589
608
|
// Step 6: Select tokens
|
|
590
609
|
let selectedTokens;
|
|
591
610
|
if (opts.all) {
|
|
@@ -593,12 +612,9 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
593
612
|
}
|
|
594
613
|
else {
|
|
595
614
|
const choices = claimableTokens.map((token) => {
|
|
596
|
-
const
|
|
597
|
-
const claimableWeth = wethIsToken0
|
|
598
|
-
? token.claimable.token0
|
|
599
|
-
: token.claimable.token1;
|
|
615
|
+
const leg = numeraireLegOf(token);
|
|
600
616
|
return {
|
|
601
|
-
name: `${token.name} ($${token.symbol}) - ${
|
|
617
|
+
name: `${token.name} ($${token.symbol}) - ${leg.claimable} ${leg.label}`,
|
|
602
618
|
value: token.tokenAddress,
|
|
603
619
|
checked: true,
|
|
604
620
|
};
|
|
@@ -650,8 +666,7 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
650
666
|
output.warn(`${baseChainTokens.length - buildableTokens.length} token(s) missing initializer data — skipped`);
|
|
651
667
|
}
|
|
652
668
|
if (buildableTokens.length === 0) {
|
|
653
|
-
output.
|
|
654
|
-
process.exit(1);
|
|
669
|
+
output.fatal("No transactions could be built");
|
|
655
670
|
}
|
|
656
671
|
// Encode calldata locally (instant, no RPC)
|
|
657
672
|
const txData = buildableTokens.map((t) => ({
|
|
@@ -676,7 +691,7 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
676
691
|
return (gas * GAS_BUFFER) / 100n;
|
|
677
692
|
}
|
|
678
693
|
catch (err) {
|
|
679
|
-
output.warn(`${token.symbol}: gas estimate failed — ${err
|
|
694
|
+
output.warn(`${token.symbol}: gas estimate failed — ${getErrorMessage(err)}`);
|
|
680
695
|
return null;
|
|
681
696
|
}
|
|
682
697
|
}));
|
|
@@ -737,7 +752,7 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
737
752
|
pendingTxs.push({ token: tx.token, hash });
|
|
738
753
|
}
|
|
739
754
|
catch (err) {
|
|
740
|
-
sendSpin.fail(`${tx.token.symbol}: Send failed — ${err
|
|
755
|
+
sendSpin.fail(`${tx.token.symbol}: Send failed — ${getErrorMessage(err)}`);
|
|
741
756
|
emitCESP("task.error");
|
|
742
757
|
failCount++;
|
|
743
758
|
if (readyTxs.indexOf(tx) < readyTxs.length - 1) {
|
|
@@ -767,7 +782,7 @@ export async function feesClaimWalletCommand(address, opts) {
|
|
|
767
782
|
token,
|
|
768
783
|
hash,
|
|
769
784
|
status: "failed",
|
|
770
|
-
error: err
|
|
785
|
+
error: getErrorMessage(err),
|
|
771
786
|
};
|
|
772
787
|
}
|
|
773
788
|
}));
|