@bankr/cli 0.1.0 → 0.2.0

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.
@@ -0,0 +1,134 @@
1
+ import chalk from "chalk";
2
+ import { getPortfolio, } from "../lib/api.js";
3
+ import * as output from "../lib/output.js";
4
+ import { formatUsd, formatBalance } from "../lib/output.js";
5
+ import { CHAIN_LABELS, VALID_CHAINS } from "../lib/chains.js";
6
+ const BRAND = chalk.hex("#FF613D");
7
+ const DIM = chalk.dim;
8
+ const BOLD = chalk.bold;
9
+ const GREEN = chalk.greenBright;
10
+ const RED = chalk.redBright;
11
+ const WHITE = chalk.whiteBright;
12
+ function formatPnl(value) {
13
+ if (value === 0)
14
+ return DIM("$0.00");
15
+ const formatted = `$${Math.abs(value).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
16
+ return value > 0 ? GREEN(`+${formatted}`) : RED(`-${formatted}`);
17
+ }
18
+ function printChainBalances(chain, data, showPnl) {
19
+ const label = CHAIN_LABELS[chain] || chain;
20
+ const total = parseFloat(data.total);
21
+ const totalStr = total > 0 ? GREEN(formatUsd(total)) : DIM(formatUsd(total));
22
+ console.log();
23
+ console.log();
24
+ console.log(` ${BRAND.bold(label)} ${totalStr}`);
25
+ console.log(` ${DIM("─".repeat(showPnl ? 74 : 58))}`);
26
+ // Native balance
27
+ const nativeUsd = parseFloat(data.nativeUsd);
28
+ if (nativeUsd > 0 || parseFloat(data.nativeBalance) > 0) {
29
+ const nativeSymbol = chain === "solana" ? "SOL" : chain === "polygon" ? "POL" : "ETH";
30
+ console.log(` ${BOLD(WHITE(nativeSymbol.padEnd(12)))} ${formatBalance(data.nativeBalance).padStart(18)} ${DIM(formatUsd(nativeUsd))}`);
31
+ }
32
+ // Token balances sorted by USD value descending
33
+ const sorted = [...data.tokenBalances].sort((a, b) => b.token.balanceUSD - a.token.balanceUSD);
34
+ for (const tb of sorted) {
35
+ const symbol = tb.token.baseToken.symbol || "???";
36
+ const bal = formatBalance(tb.token.balance);
37
+ const usd = formatUsd(tb.token.balanceUSD);
38
+ let line = ` ${WHITE(symbol.padEnd(12))} ${bal.padStart(18)} ${DIM(usd)}`;
39
+ if (showPnl && tb.token.pnl) {
40
+ line += ` ${formatPnl(tb.token.pnl.totalPnl)}`;
41
+ }
42
+ console.log(line);
43
+ }
44
+ if (sorted.length === 0 && (nativeUsd === 0 || isNaN(nativeUsd))) {
45
+ console.log(DIM(" No token balances"));
46
+ }
47
+ }
48
+ export async function portfolioCommand(opts) {
49
+ // Validate chain option
50
+ const chains = opts.chain
51
+ ? opts.chain.split(",").map((c) => c.trim().toLowerCase())
52
+ : undefined;
53
+ if (chains) {
54
+ const invalid = chains.filter((c) => !VALID_CHAINS.has(c));
55
+ if (invalid.length > 0) {
56
+ output.error(`Invalid chain${invalid.length > 1 ? "s" : ""}: ${invalid.join(", ")}. Valid chains: ${[...VALID_CHAINS].join(", ")}`);
57
+ process.exit(1);
58
+ }
59
+ }
60
+ const includePnl = opts.pnl || opts.all;
61
+ const includeNfts = opts.nfts || opts.all;
62
+ const include = [];
63
+ if (includePnl)
64
+ include.push("pnl");
65
+ if (includeNfts)
66
+ include.push("nfts");
67
+ const spinText = include.length > 0
68
+ ? `Fetching portfolio (${include.join(", ")})...`
69
+ : "Fetching portfolio...";
70
+ const spin = output.spinner(spinText);
71
+ let data;
72
+ try {
73
+ data = await getPortfolio({
74
+ chains,
75
+ showLowValueTokens: opts.lowValue,
76
+ include: include.length > 0 ? include : undefined,
77
+ });
78
+ spin.succeed("Portfolio loaded");
79
+ }
80
+ catch (err) {
81
+ spin.fail(`Failed to fetch portfolio: ${err.message}`);
82
+ process.exit(1);
83
+ }
84
+ // JSON output mode
85
+ if (opts.json) {
86
+ console.log(JSON.stringify(data, null, 2));
87
+ return;
88
+ }
89
+ // Calculate grand total
90
+ let grandTotal = 0;
91
+ const chainEntries = Object.entries(data.balances);
92
+ for (const [, chainData] of chainEntries) {
93
+ grandTotal += parseFloat(chainData.total) || 0;
94
+ }
95
+ console.log();
96
+ output.label("Wallet", "");
97
+ console.log(` ${"EVM".padEnd(8)} ${data.evmAddress}`);
98
+ if (data.solAddress) {
99
+ console.log(` ${"SOLANA".padEnd(8)} ${data.solAddress}`);
100
+ }
101
+ console.log();
102
+ output.label("Total", GREEN(formatUsd(grandTotal)));
103
+ if (opts.lowValue) {
104
+ console.log();
105
+ output.info("Including low-value tokens");
106
+ }
107
+ // Print each chain
108
+ for (const [chain, chainData] of chainEntries) {
109
+ printChainBalances(chain, chainData, !!includePnl);
110
+ }
111
+ // Print NFTs
112
+ if (includeNfts && data.nfts && data.nfts.length > 0) {
113
+ console.log();
114
+ console.log();
115
+ console.log(` ${BRAND.bold("NFTs")} ${DIM(`${data.nfts.length} item(s)`)}`);
116
+ console.log(` ${DIM("─".repeat(58))}`);
117
+ for (const nft of data.nfts) {
118
+ const name = nft.name || DIM("Unnamed");
119
+ const collection = nft.collection?.name
120
+ ? DIM(` (${nft.collection.name})`)
121
+ : "";
122
+ const address = nft.collection?.address || DIM("unknown");
123
+ console.log(` ${WHITE(name)}${collection}`);
124
+ console.log(` ${DIM(address)} #${nft.tokenId} ${DIM(nft.chainName || nft.chain)}`);
125
+ console.log();
126
+ }
127
+ }
128
+ else if (includeNfts) {
129
+ console.log();
130
+ output.info("No NFTs found");
131
+ }
132
+ console.log();
133
+ }
134
+ //# sourceMappingURL=portfolio.js.map
@@ -0,0 +1,7 @@
1
+ export declare function tokensSearchCommand(query: string, opts: {
2
+ chain?: string;
3
+ }): Promise<void>;
4
+ export declare function tokensInfoCommand(address: string, opts: {
5
+ chain?: string;
6
+ }): Promise<void>;
7
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1,60 @@
1
+ import { searchTokens } from "../lib/api.js";
2
+ import * as output from "../lib/output.js";
3
+ export async function tokensSearchCommand(query, opts) {
4
+ const spin = output.spinner(`Searching tokens for "${query}"...`);
5
+ try {
6
+ const chainId = opts.chain ? Number(opts.chain) : undefined;
7
+ const res = await searchTokens(query, chainId);
8
+ spin.succeed(`Found ${res.tokens.length} result(s)`);
9
+ if (res.tokens.length === 0) {
10
+ output.info("No tokens found. Try a different search query.");
11
+ return;
12
+ }
13
+ console.log();
14
+ for (const token of res.tokens) {
15
+ const price = token.priceUsd
16
+ ? `$${token.priceUsd.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 })}`
17
+ : output.fmt.dim("n/a");
18
+ console.log(` ${output.fmt.brandBold(`${token.symbol}`)} ${output.fmt.dim(token.name)}`);
19
+ console.log(` Address: ${token.address}`);
20
+ console.log(` Price: ${price}`);
21
+ console.log();
22
+ }
23
+ }
24
+ catch (err) {
25
+ spin.fail(`Search failed: ${err.message}`);
26
+ process.exit(1);
27
+ }
28
+ }
29
+ export async function tokensInfoCommand(address, opts) {
30
+ const spin = output.spinner(`Fetching token info for ${address}...`);
31
+ try {
32
+ const chainId = opts.chain ? Number(opts.chain) : undefined;
33
+ const res = await searchTokens(address, chainId);
34
+ spin.succeed("Token info loaded");
35
+ // Match by the requested address (search returns all tokens from matching pools)
36
+ const addressLower = address.toLowerCase();
37
+ const token = res.tokens.find((t) => t.address.toLowerCase() === addressLower) ??
38
+ res.tokens[0];
39
+ if (!token) {
40
+ output.error("Token not found. Check the address and try again.");
41
+ process.exit(1);
42
+ }
43
+ console.log();
44
+ output.label("Name", token.name);
45
+ output.label("Symbol", token.symbol);
46
+ output.label("Address", token.address);
47
+ output.label("Decimals", String(token.decimals));
48
+ if (token.priceUsd) {
49
+ output.label("Price", `$${token.priceUsd.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 })}`);
50
+ }
51
+ if (token.logoURI) {
52
+ output.label("Logo", token.logoURI);
53
+ }
54
+ }
55
+ catch (err) {
56
+ spin.fail(`Failed to fetch token info: ${err.message}`);
57
+ process.exit(1);
58
+ }
59
+ }
60
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1,8 @@
1
+ export declare function transferCommand(opts: {
2
+ to: string;
3
+ amount: string;
4
+ token?: string;
5
+ native?: boolean;
6
+ chain?: string;
7
+ }): Promise<void>;
8
+ //# sourceMappingURL=transfer.d.ts.map
@@ -0,0 +1,80 @@
1
+ import { transfer, searchTokens } from "../lib/api.js";
2
+ import * as output from "../lib/output.js";
3
+ import { NATIVE_SYMBOLS, CHAIN_IDS } from "../lib/chains.js";
4
+ import { isAddress } from "viem";
5
+ /**
6
+ * Resolve a token symbol or address to a contract address.
7
+ * If it's already a valid address, return as-is. Otherwise search by symbol.
8
+ */
9
+ async function resolveToken(tokenInput, chain) {
10
+ if (isAddress(tokenInput)) {
11
+ return { address: tokenInput, symbol: tokenInput };
12
+ }
13
+ const chainId = CHAIN_IDS[chain];
14
+ const res = await searchTokens(tokenInput, chainId);
15
+ // Exact symbol match (case-insensitive) to avoid resolving to wrong token
16
+ const inputUpper = tokenInput.toUpperCase();
17
+ const exactMatch = res.tokens.find((t) => t.symbol.toUpperCase() === inputUpper);
18
+ if (!exactMatch) {
19
+ const available = res.tokens
20
+ .slice(0, 5)
21
+ .map((t) => `${t.symbol} (${t.address})`)
22
+ .join(", ");
23
+ throw new Error(`No exact match for "${tokenInput}" on ${chain}. Did you mean: ${available || "no results"}`);
24
+ }
25
+ return { address: exactMatch.address, symbol: exactMatch.symbol };
26
+ }
27
+ export async function transferCommand(opts) {
28
+ const isNative = opts.native ?? false;
29
+ const chain = opts.chain ?? "base";
30
+ if (!isNative && !opts.token) {
31
+ output.error("Token required for ERC20 transfers. Use --token <symbol or address> or --native.");
32
+ process.exit(1);
33
+ }
34
+ let tokenAddress;
35
+ let tokenLabel;
36
+ if (isNative) {
37
+ tokenAddress = "0x0000000000000000000000000000000000000000";
38
+ tokenLabel = NATIVE_SYMBOLS[chain] ?? "ETH";
39
+ }
40
+ else {
41
+ const spin = output.spinner(`Resolving token "${opts.token}"...`);
42
+ try {
43
+ const resolved = await resolveToken(opts.token, chain);
44
+ tokenAddress = resolved.address;
45
+ tokenLabel = resolved.symbol;
46
+ if (tokenAddress !== opts.token) {
47
+ spin.succeed(`Resolved ${opts.token} → ${tokenLabel} (${tokenAddress})`);
48
+ }
49
+ else {
50
+ spin.stop();
51
+ }
52
+ }
53
+ catch (err) {
54
+ spin.fail(err.message);
55
+ process.exit(1);
56
+ }
57
+ }
58
+ const chainLabel = chain !== "base" ? ` on ${chain}` : "";
59
+ const spin = output.spinner(`Transferring ${opts.amount} ${tokenLabel} to ${opts.to}${chainLabel}...`);
60
+ try {
61
+ const result = await transfer({
62
+ tokenAddress,
63
+ recipientAddress: opts.to,
64
+ amount: opts.amount,
65
+ isNativeToken: isNative,
66
+ chain,
67
+ });
68
+ if (!result.success) {
69
+ spin.fail(`Transfer failed: ${result.error}`);
70
+ process.exit(1);
71
+ }
72
+ spin.succeed(`Transfer successful`);
73
+ output.label("Tx Hash", result.txHash ?? "unknown");
74
+ }
75
+ catch (err) {
76
+ spin.fail(`Transfer failed: ${err.message}`);
77
+ process.exit(1);
78
+ }
79
+ }
80
+ //# sourceMappingURL=transfer.js.map
@@ -0,0 +1,4 @@
1
+ export declare function updateCommand(opts: {
2
+ check?: boolean;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1,116 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { execSync } from "node:child_process";
3
+ import * as output from "../lib/output.js";
4
+ const PACKAGE_NAME = "@bankr/cli";
5
+ /**
6
+ * Compare two semver version strings.
7
+ * Returns -1 if a < b, 0 if equal, 1 if a > b.
8
+ */
9
+ function compareSemver(a, b) {
10
+ const partsA = a.split(".").map(Number);
11
+ const partsB = b.split(".").map(Number);
12
+ for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
13
+ const numA = partsA[i] ?? 0;
14
+ const numB = partsB[i] ?? 0;
15
+ if (numA < numB)
16
+ return -1;
17
+ if (numA > numB)
18
+ return 1;
19
+ }
20
+ return 0;
21
+ }
22
+ async function fetchLatestVersion() {
23
+ const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
24
+ signal: AbortSignal.timeout(10000),
25
+ });
26
+ if (!res.ok) {
27
+ throw new Error(`Failed to check for updates (HTTP ${res.status})`);
28
+ }
29
+ const data = (await res.json());
30
+ return data.version;
31
+ }
32
+ function getCurrentVersion() {
33
+ const pkg = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf-8"));
34
+ return pkg.version;
35
+ }
36
+ /**
37
+ * Detect which package manager installed the CLI globally.
38
+ * Falls back to npm if we can't determine.
39
+ */
40
+ function detectPackageManager() {
41
+ try {
42
+ const execPath = process.argv[1] ?? "";
43
+ // Check if running under bun
44
+ if (execPath.includes("/.bun/") || "bun" in process.versions) {
45
+ return "bun";
46
+ }
47
+ // Check if running under pnpm
48
+ if (execPath.includes("/pnpm/") || execPath.includes("/.pnpm/")) {
49
+ return "pnpm";
50
+ }
51
+ // Check if running under yarn
52
+ if (execPath.includes("/yarn/") || execPath.includes("/.yarn/")) {
53
+ return "yarn";
54
+ }
55
+ }
56
+ catch {
57
+ // ignore detection errors
58
+ }
59
+ return "npm";
60
+ }
61
+ function getInstallCommand(pm) {
62
+ switch (pm) {
63
+ case "bun":
64
+ return `bun install -g ${PACKAGE_NAME}@latest`;
65
+ case "pnpm":
66
+ return `pnpm add -g ${PACKAGE_NAME}@latest`;
67
+ case "yarn":
68
+ return `yarn global add ${PACKAGE_NAME}@latest`;
69
+ default:
70
+ return `npm install -g ${PACKAGE_NAME}@latest`;
71
+ }
72
+ }
73
+ export async function updateCommand(opts) {
74
+ const current = getCurrentVersion();
75
+ const spin = output.spinner("Checking for updates...");
76
+ let latest;
77
+ try {
78
+ latest = await fetchLatestVersion();
79
+ }
80
+ catch (err) {
81
+ spin.fail();
82
+ output.error(err.message);
83
+ process.exit(1);
84
+ }
85
+ const cmp = compareSemver(current, latest);
86
+ if (cmp === 0) {
87
+ spin.succeed(`Already on the latest version (${current})`);
88
+ return;
89
+ }
90
+ if (cmp > 0) {
91
+ spin.succeed(`You're ahead of latest (${current} > ${latest}). No update needed.`);
92
+ return;
93
+ }
94
+ spin.succeed(`Update available: ${current} → ${latest}`);
95
+ if (opts.check) {
96
+ return;
97
+ }
98
+ const pm = detectPackageManager();
99
+ const cmd = getInstallCommand(pm);
100
+ output.blank();
101
+ output.info(`Updating via ${pm}...`);
102
+ output.dim(` $ ${cmd}`);
103
+ output.blank();
104
+ try {
105
+ execSync(cmd, { stdio: "inherit" });
106
+ output.blank();
107
+ output.success(`Updated to ${PACKAGE_NAME}@${latest}`);
108
+ }
109
+ catch {
110
+ output.blank();
111
+ output.error("Update failed. Try running manually:");
112
+ output.dim(` $ ${cmd}`);
113
+ process.exit(1);
114
+ }
115
+ }
116
+ //# sourceMappingURL=update.js.map
package/dist/lib/api.d.ts CHANGED
@@ -61,7 +61,29 @@ export interface TokenBalanceInfo {
61
61
  imgUrl: string;
62
62
  decimals: number;
63
63
  };
64
+ pnl?: {
65
+ realizedPnl: number;
66
+ unrealizedPnl: number;
67
+ totalPnl: number;
68
+ averageEntryPrice: number;
69
+ };
70
+ };
71
+ }
72
+ export interface NftInfo {
73
+ name?: string;
74
+ tokenId: string;
75
+ collection?: {
76
+ name: string;
77
+ address: string;
78
+ nftStandard?: string;
64
79
  };
80
+ chain: string;
81
+ chainName?: string;
82
+ image?: {
83
+ thumbnail?: string;
84
+ original?: string;
85
+ };
86
+ openSeaUrl?: string;
65
87
  }
66
88
  export interface ChainBalanceInfo {
67
89
  nativeBalance: string;
@@ -69,13 +91,23 @@ export interface ChainBalanceInfo {
69
91
  tokenBalances: TokenBalanceInfo[];
70
92
  total: string;
71
93
  }
72
- export interface BalancesResponse {
94
+ export interface PortfolioResponse {
73
95
  success: boolean;
74
96
  evmAddress: string;
75
97
  solAddress?: string;
76
98
  balances: Record<string, ChainBalanceInfo>;
99
+ nfts?: NftInfo[];
100
+ }
101
+ export interface PortfolioOptions {
102
+ chains?: string[];
103
+ showLowValueTokens?: boolean;
104
+ include?: string[];
77
105
  }
78
- export declare function getBalances(chains?: string[]): Promise<BalancesResponse>;
106
+ export declare function getPortfolio(opts?: PortfolioOptions): Promise<PortfolioResponse>;
107
+ /** @deprecated Use PortfolioResponse instead */
108
+ export type BalancesResponse = PortfolioResponse;
109
+ /** @deprecated Use getPortfolio instead */
110
+ export declare const getBalances: (chains?: string[], showLowValueTokens?: boolean) => Promise<PortfolioResponse>;
79
111
  export declare function pollJob(jobId: string, opts?: {
80
112
  interval?: number;
81
113
  maxAttempts?: number;
@@ -324,5 +356,30 @@ export declare function addProjectUpdate(data: {
324
356
  title: string;
325
357
  content: string;
326
358
  }): Promise<AgentProfileResponse>;
359
+ export interface TokenSearchResult {
360
+ address: string;
361
+ symbol: string;
362
+ name: string;
363
+ decimals: number;
364
+ logoURI?: string;
365
+ priceUsd?: number;
366
+ }
367
+ export interface TokenSearchResponse {
368
+ tokens: TokenSearchResult[];
369
+ }
370
+ export declare function searchTokens(query: string, chainId?: number): Promise<TokenSearchResponse>;
371
+ export interface TransferRequest {
372
+ tokenAddress: string;
373
+ recipientAddress: string;
374
+ amount: string;
375
+ isNativeToken: boolean;
376
+ chain?: string;
377
+ }
378
+ export interface TransferResponse {
379
+ success: boolean;
380
+ txHash?: string;
381
+ error?: string;
382
+ }
383
+ export declare function transfer(request: TransferRequest): Promise<TransferResponse>;
327
384
  export declare function buildPublicClaimTxs(beneficiaryAddress: string, tokenAddresses: string[]): Promise<BuildClaimResponse>;
328
385
  //# sourceMappingURL=api.d.ts.map
package/dist/lib/api.js CHANGED
@@ -62,18 +62,25 @@ export async function validateApiKey() {
62
62
  }
63
63
  }
64
64
  export async function getUserInfo() {
65
- const res = await fetch(`${getApiUrl()}/agent/me`, {
65
+ const res = await fetch(`${getApiUrl()}/wallet/me`, {
66
66
  headers: authHeaders(),
67
67
  });
68
68
  return handleResponse(res);
69
69
  }
70
- export async function getBalances(chains) {
71
- const params = chains?.length ? `?chains=${chains.join(",")}` : "";
72
- const res = await fetch(`${getApiUrl()}/agent/balances${params}`, {
73
- headers: authHeaders(),
74
- });
70
+ export async function getPortfolio(opts = {}) {
71
+ const query = new URLSearchParams();
72
+ if (opts.chains?.length)
73
+ query.set("chains", opts.chains.join(","));
74
+ if (opts.showLowValueTokens)
75
+ query.set("showLowValueTokens", "true");
76
+ if (opts.include?.length)
77
+ query.set("include", opts.include.join(","));
78
+ const qs = query.toString();
79
+ const res = await fetch(`${getApiUrl()}/wallet/portfolio${qs ? `?${qs}` : ""}`, { headers: authHeaders() });
75
80
  return handleResponse(res);
76
81
  }
82
+ /** @deprecated Use getPortfolio instead */
83
+ export const getBalances = (chains, showLowValueTokens) => getPortfolio({ chains, showLowValueTokens });
77
84
  export async function pollJob(jobId, opts = {}) {
78
85
  const { interval = 2000, maxAttempts = 150, onStatus } = opts;
79
86
  let attempts = 0;
@@ -89,7 +96,7 @@ export async function pollJob(jobId, opts = {}) {
89
96
  throw new Error(`Polling timed out after ${maxAttempts} attempts`);
90
97
  }
91
98
  export async function sign(request) {
92
- const res = await fetch(`${getApiUrl()}/agent/sign`, {
99
+ const res = await fetch(`${getApiUrl()}/wallet/sign`, {
93
100
  method: "POST",
94
101
  headers: authHeaders(),
95
102
  body: JSON.stringify(request),
@@ -97,7 +104,7 @@ export async function sign(request) {
97
104
  return handleResponse(res);
98
105
  }
99
106
  export async function submit(request) {
100
- const res = await fetch(`${getApiUrl()}/agent/submit`, {
107
+ const res = await fetch(`${getApiUrl()}/wallet/submit`, {
101
108
  method: "POST",
102
109
  headers: authHeaders(),
103
110
  body: JSON.stringify(request),
@@ -254,6 +261,25 @@ export async function addProjectUpdate(data) {
254
261
  });
255
262
  return handleResponse(res);
256
263
  }
264
+ export async function searchTokens(query, chainId) {
265
+ const params = new URLSearchParams({ query });
266
+ if (chainId)
267
+ params.set("chainId", String(chainId));
268
+ const res = await fetch(`${getApiUrl()}/tokens/search?${params}`, {
269
+ headers: {
270
+ "User-Agent": CLI_USER_AGENT,
271
+ },
272
+ });
273
+ return handleResponse(res);
274
+ }
275
+ export async function transfer(request) {
276
+ const res = await fetch(`${getApiUrl()}/wallet/transfer`, {
277
+ method: "POST",
278
+ headers: authHeaders(),
279
+ body: JSON.stringify(request),
280
+ });
281
+ return handleResponse(res);
282
+ }
257
283
  export async function buildPublicClaimTxs(beneficiaryAddress, tokenAddresses) {
258
284
  const res = await fetch(`${getApiUrl()}/public/doppler/build-claim`, {
259
285
  method: "POST",
@@ -0,0 +1,5 @@
1
+ export declare const CHAIN_LABELS: Record<string, string>;
2
+ export declare const VALID_CHAINS: Set<string>;
3
+ export declare const CHAIN_IDS: Record<string, number>;
4
+ export declare const NATIVE_SYMBOLS: Record<string, string>;
5
+ //# sourceMappingURL=chains.d.ts.map
@@ -0,0 +1,35 @@
1
+ export const CHAIN_LABELS = {
2
+ base: "Base",
3
+ polygon: "Polygon",
4
+ mainnet: "Ethereum",
5
+ unichain: "Unichain",
6
+ worldchain: "World Chain",
7
+ arbitrum: "Arbitrum",
8
+ solana: "Solana",
9
+ };
10
+ export const VALID_CHAINS = new Set([
11
+ "base",
12
+ "polygon",
13
+ "mainnet",
14
+ "unichain",
15
+ "worldchain",
16
+ "arbitrum",
17
+ "solana",
18
+ ]);
19
+ export const CHAIN_IDS = {
20
+ base: 8453,
21
+ mainnet: 1,
22
+ polygon: 137,
23
+ unichain: 130,
24
+ worldchain: 480,
25
+ arbitrum: 42161,
26
+ };
27
+ export const NATIVE_SYMBOLS = {
28
+ base: "ETH",
29
+ mainnet: "ETH",
30
+ unichain: "ETH",
31
+ worldchain: "ETH",
32
+ arbitrum: "ETH",
33
+ polygon: "POL",
34
+ };
35
+ //# sourceMappingURL=chains.js.map
@@ -29,5 +29,7 @@ export declare function keyValue(key: string, value: string): void;
29
29
  export declare function spinner(text: string): Ora;
30
30
  export declare function maskApiKey(key: string): string;
31
31
  export declare function formatDuration(ms: number): string;
32
+ export declare function formatUsd(value: string | number): string;
33
+ export declare function formatBalance(value: string | number, decimals?: number): string;
32
34
  export declare function formatStatus(status: string): string;
33
35
  //# sourceMappingURL=output.d.ts.map
@@ -66,6 +66,23 @@ export function formatDuration(ms) {
66
66
  return `${ms}ms`;
67
67
  return `${(ms / 1000).toFixed(1)}s`;
68
68
  }
69
+ export function formatUsd(value) {
70
+ const num = typeof value === "string" ? parseFloat(value) : value;
71
+ if (isNaN(num) || num === 0)
72
+ return chalk.dim("$0.00");
73
+ return `$${num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
74
+ }
75
+ export function formatBalance(value, decimals = 6) {
76
+ const num = typeof value === "string" ? parseFloat(value) : value;
77
+ if (isNaN(num) || num === 0)
78
+ return "0";
79
+ if (num < 0.000001)
80
+ return "<0.000001";
81
+ return num.toLocaleString("en-US", {
82
+ minimumFractionDigits: 0,
83
+ maximumFractionDigits: decimals,
84
+ });
85
+ }
69
86
  export function formatStatus(status) {
70
87
  switch (status) {
71
88
  case "completed":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bankr/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Official CLI for the Bankr AI agent platform",
5
5
  "type": "module",
6
6
  "bin": {