chainbase-cli 0.1.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.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # Chainbase CLI
2
+
3
+ A command-line interface for the [Chainbase Web3 API](https://docs.chainbase.com/api-reference/overview). Designed for both human use and AI agent automation — outputs JSON by default, with an optional `--pretty` flag for human-readable format.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g chainbase-cli
9
+ ```
10
+
11
+ Or run directly with npx:
12
+
13
+ ```bash
14
+ npx chainbase-cli --help
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ # Set your API key (get one at https://console.chainbase.com)
21
+ chainbase config set api-key YOUR_API_KEY
22
+
23
+ # Get latest Ethereum block number
24
+ chainbase block latest
25
+
26
+ # Get USDT price
27
+ chainbase token price 0xdac17f958d2ee523a2206206994597c13d831ec7
28
+
29
+ # Resolve ENS domain
30
+ chainbase domain ens-resolve vitalik.eth
31
+
32
+ # Query on BSC (chain ID 56)
33
+ chainbase token price 0x55d398326f99059fF775485246999027B3197955 --chain 56
34
+ ```
35
+
36
+ ## Global Options
37
+
38
+ | Option | Description | Default |
39
+ |--------|-------------|---------|
40
+ | `--chain <id>` | Chain ID | `1` (Ethereum) |
41
+ | `--pretty` | Human-readable output | `false` |
42
+ | `--page <n>` | Page number | `1` |
43
+ | `--limit <n>` | Results per page | `20` |
44
+
45
+ ## Commands
46
+
47
+ ### `config` — Configuration
48
+
49
+ ```bash
50
+ chainbase config set api-key <key> # Set API key
51
+ chainbase config set default-chain 137 # Set default chain to Polygon
52
+ chainbase config get api-key # Get a config value
53
+ chainbase config list # List all config
54
+ ```
55
+
56
+ ### `block` — Block Queries
57
+
58
+ ```bash
59
+ chainbase block latest # Latest block number
60
+ chainbase block detail 20000000 # Block details by number
61
+ ```
62
+
63
+ ### `tx` — Transaction Queries
64
+
65
+ ```bash
66
+ chainbase tx detail <hash> # Transaction by hash
67
+ chainbase tx list <address> # Account transactions
68
+ chainbase tx list <address> --from-block 20000000 # With block range filter
69
+ ```
70
+
71
+ ### `token` — Token Queries
72
+
73
+ ```bash
74
+ chainbase token metadata <contract> # Token metadata
75
+ chainbase token price <contract> # Current price
76
+ chainbase token price-history <contract> --from <ts> --to <ts> # Price history
77
+ chainbase token holders <contract> # All holders
78
+ chainbase token top-holders <contract> # Top holders
79
+ chainbase token transfers --contract <addr> # Transfer history
80
+ ```
81
+
82
+ ### `nft` — NFT Queries
83
+
84
+ ```bash
85
+ chainbase nft metadata <contract> <token_id> # NFT metadata
86
+ chainbase nft collection <contract> # Collection info
87
+ chainbase nft collection-items <contract> # Items in collection
88
+ chainbase nft search "Bored Ape" # Search by name
89
+ chainbase nft owner <contract> <token_id> # Current owner
90
+ chainbase nft owners <contract> # All owners
91
+ chainbase nft owner-history <contract> <token_id> # Owner history
92
+ chainbase nft transfers --contract <addr> # Transfer history
93
+ chainbase nft floor-price <contract> # Floor price
94
+ chainbase nft price-history <contract> --from <ts> --to <ts> # Price history
95
+ chainbase nft trending # Trending collections
96
+ chainbase nft rarity <contract> # Rarity scores
97
+ ```
98
+
99
+ ### `balance` — Balance & Portfolio
100
+
101
+ ```bash
102
+ chainbase balance native <address> # Native token balance (ETH, BNB, etc.)
103
+ chainbase balance tokens <address> # ERC20 token balances
104
+ chainbase balance nfts <address> # NFTs owned
105
+ chainbase balance portfolios <address> # DeFi positions
106
+ chainbase balance portfolios <address> --chains 1,137 # Filter by chains
107
+ ```
108
+
109
+ ### `domain` — ENS & Space ID
110
+
111
+ ```bash
112
+ chainbase domain ens <address> # ENS domains held by address
113
+ chainbase domain ens-resolve vitalik.eth # Resolve ENS → address
114
+ chainbase domain ens-reverse <address> # Reverse resolve address → ENS
115
+ chainbase domain spaceid-resolve <domain> # Resolve Space ID (BSC)
116
+ chainbase domain spaceid-reverse <address> # Reverse resolve Space ID
117
+ ```
118
+
119
+ ### `contract` — Smart Contract
120
+
121
+ ```bash
122
+ chainbase contract call \
123
+ --address <contract> \
124
+ --function "balanceOf" \
125
+ --abi '[...]' \
126
+ --params '["0x..."]'
127
+ ```
128
+
129
+ ### `sql` — SQL Queries
130
+
131
+ ```bash
132
+ chainbase sql execute "SELECT number FROM ethereum.blocks LIMIT 5" # Execute async query
133
+ chainbase sql status <execution_id> # Check status
134
+ chainbase sql results <execution_id> # Get results
135
+ ```
136
+
137
+ ## Supported Chains
138
+
139
+ | Chain | ID | Chain | ID |
140
+ |-------|----|-------|----|
141
+ | Ethereum | `1` | Arbitrum | `42161` |
142
+ | BSC | `56` | Optimism | `10` |
143
+ | Polygon | `137` | Base | `8453` |
144
+ | Avalanche | `43114` | zkSync | `324` |
145
+
146
+ ## Authentication
147
+
148
+ The API key can be configured in three ways (in priority order):
149
+
150
+ 1. **Environment variable**: `CHAINBASE_API_KEY=xxx chainbase block latest`
151
+ 2. **Config file**: `chainbase config set api-key xxx` (stored at `~/.chainbase/config.json`, mode 0600)
152
+
153
+ ## For AI Agents
154
+
155
+ This CLI is designed to be controlled by AI agents. Key features:
156
+
157
+ - **JSON output by default** — machine-parseable, no colors or formatting
158
+ - **Consistent error format** — errors output as `{"error":"message"}` to stderr
159
+ - **Discoverable** — `chainbase --help` and `chainbase <command> --help` list all available commands
160
+ - **Predictable** — every command follows the same pattern: `chainbase <group> <action> [args] [options]`
161
+
162
+ Example agent usage:
163
+
164
+ ```bash
165
+ # Parse output directly
166
+ PRICE=$(chainbase token price 0xdac17f958d2ee523a2206206994597c13d831ec7 | jq '.data.price')
167
+
168
+ # Check if command succeeded
169
+ if chainbase balance native 0x... 2>/dev/null; then
170
+ echo "Success"
171
+ fi
172
+ ```
173
+
174
+ ## Development
175
+
176
+ ```bash
177
+ git clone https://github.com/chainbase-labs/cli.git
178
+ cd cli
179
+ npm install
180
+ npm run build # Build
181
+ npm test # Run tests
182
+ npm run lint # Type-check
183
+ npm link # Install globally for local dev
184
+ ```
185
+
186
+ ## License
187
+
188
+ ISC
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,633 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command as Command11 } from "commander";
5
+
6
+ // src/commands/config.ts
7
+ import { Command } from "commander";
8
+
9
+ // src/config.ts
10
+ import fs from "fs";
11
+ import path from "path";
12
+ import os from "os";
13
+ function getConfigDir() {
14
+ return process.env.CHAINBASE_CONFIG_DIR || path.join(os.homedir(), ".chainbase");
15
+ }
16
+ function getConfigPath() {
17
+ return path.join(getConfigDir(), "config.json");
18
+ }
19
+ function readConfigFile() {
20
+ const configPath = getConfigPath();
21
+ if (!fs.existsSync(configPath)) return {};
22
+ try {
23
+ return JSON.parse(fs.readFileSync(configPath, "utf-8"));
24
+ } catch {
25
+ throw new Error(`Config file corrupted: ${configPath}
26
+ Delete it and re-configure: chainbase config set api-key <your-key>`);
27
+ }
28
+ }
29
+ function writeConfigFile(config) {
30
+ const dir = getConfigDir();
31
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true, mode: 448 });
32
+ fs.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), { mode: 384 });
33
+ }
34
+ function setConfig(key, value) {
35
+ const config = readConfigFile();
36
+ config[key] = value;
37
+ writeConfigFile(config);
38
+ }
39
+ function getConfig(key) {
40
+ const config = readConfigFile();
41
+ return config[key];
42
+ }
43
+ function listConfig() {
44
+ return readConfigFile();
45
+ }
46
+ function getApiKey() {
47
+ const envKey = process.env.CHAINBASE_API_KEY;
48
+ if (envKey) return envKey;
49
+ const fileKey = getConfig("api-key");
50
+ if (fileKey) return fileKey;
51
+ throw new Error("No API key configured. Run: chainbase config set api-key <your-key>");
52
+ }
53
+ function getDefaultChain() {
54
+ return getConfig("default-chain") || "1";
55
+ }
56
+
57
+ // src/output.ts
58
+ import chalk from "chalk";
59
+ function formatOutput(data, pretty) {
60
+ if (pretty) {
61
+ process.stdout.write(JSON.stringify(data, null, 2) + "\n");
62
+ } else {
63
+ process.stdout.write(JSON.stringify(data) + "\n");
64
+ }
65
+ }
66
+ function formatError(message, pretty) {
67
+ const error = { error: message };
68
+ if (pretty) {
69
+ process.stderr.write(chalk.red(`Error: ${message}`) + "\n");
70
+ } else {
71
+ process.stderr.write(JSON.stringify(error) + "\n");
72
+ }
73
+ }
74
+
75
+ // src/commands/config.ts
76
+ function createConfigCommand() {
77
+ const cmd = new Command("config").description("\u7BA1\u7406 CLI \u914D\u7F6E");
78
+ cmd.command("set <key> <value>").description("\u8BBE\u7F6E\u914D\u7F6E\u9879\uFF08api-key, default-chain\uFF09").action((key, value) => {
79
+ setConfig(key, value);
80
+ const pretty = cmd.parent?.opts().pretty ?? false;
81
+ formatOutput({ status: "ok", key, value: key === "api-key" ? "***" : value }, pretty);
82
+ });
83
+ cmd.command("get <key>").description("\u83B7\u53D6\u914D\u7F6E\u9879").action((key) => {
84
+ const pretty = cmd.parent?.opts().pretty ?? false;
85
+ let value = getConfig(key) ?? null;
86
+ if (key === "api-key" && value) value = "***" + value.slice(-4);
87
+ formatOutput({ key, value }, pretty);
88
+ });
89
+ cmd.command("list").description("\u5217\u51FA\u6240\u6709\u914D\u7F6E\u9879").action(() => {
90
+ const pretty = cmd.parent?.opts().pretty ?? false;
91
+ const config = listConfig();
92
+ const safe = { ...config };
93
+ if (safe["api-key"]) safe["api-key"] = "***" + safe["api-key"].slice(-4);
94
+ formatOutput(safe, pretty);
95
+ });
96
+ return cmd;
97
+ }
98
+
99
+ // src/commands/block.ts
100
+ import { Command as Command2 } from "commander";
101
+
102
+ // src/client.ts
103
+ import axios from "axios";
104
+ var WEB3_BASE = "https://api.chainbase.online";
105
+ var SQL_BASE = "https://api.chainbase.com/api/v1";
106
+ var ChainbaseClient = class {
107
+ constructor(apiKey) {
108
+ this.apiKey = apiKey;
109
+ }
110
+ getBaseUrl(path2) {
111
+ if (path2.startsWith("/query/") || path2.startsWith("/execution/")) {
112
+ return SQL_BASE;
113
+ }
114
+ return WEB3_BASE;
115
+ }
116
+ async get(path2, params) {
117
+ return this.request("GET", path2, { params });
118
+ }
119
+ async post(path2, data) {
120
+ return this.request("POST", path2, { data });
121
+ }
122
+ async request(method, path2, options) {
123
+ const baseUrl = this.getBaseUrl(path2);
124
+ const response = await axios.request({
125
+ method,
126
+ url: `${baseUrl}${path2}`,
127
+ headers: {
128
+ "x-api-key": this.apiKey,
129
+ "Content-Type": "application/json"
130
+ },
131
+ params: options.params,
132
+ data: options.data
133
+ });
134
+ const body = response.data;
135
+ if (body && typeof body === "object" && "code" in body && body.code !== 0) {
136
+ throw new Error(body.message || `API error code: ${body.code}`);
137
+ }
138
+ return body;
139
+ }
140
+ };
141
+ function createClient(opts) {
142
+ const apiKey = getApiKey();
143
+ const chainId = opts.chain || getDefaultChain();
144
+ return { client: new ChainbaseClient(apiKey), chainId };
145
+ }
146
+
147
+ // src/commands/block.ts
148
+ function createBlockCommand() {
149
+ const cmd = new Command2("block").description("\u533A\u5757\u67E5\u8BE2");
150
+ cmd.command("latest").description("\u83B7\u53D6\u6700\u65B0\u533A\u5757\u53F7").action(async () => {
151
+ const opts = cmd.parent.opts();
152
+ const { client, chainId } = createClient(opts);
153
+ const result = await client.get("/v1/block/number/latest", { chain_id: chainId });
154
+ formatOutput(result, opts.pretty);
155
+ });
156
+ cmd.command("detail <number>").description("\u6309\u533A\u5757\u53F7\u67E5\u8BE2\u533A\u5757\u8BE6\u60C5").action(async (number) => {
157
+ const opts = cmd.parent.opts();
158
+ const { client, chainId } = createClient(opts);
159
+ const result = await client.get("/v1/block/detail", { chain_id: chainId, number });
160
+ formatOutput(result, opts.pretty);
161
+ });
162
+ return cmd;
163
+ }
164
+
165
+ // src/commands/tx.ts
166
+ import { Command as Command3 } from "commander";
167
+ function createTxCommand() {
168
+ const cmd = new Command3("tx").description("\u4EA4\u6613\u67E5\u8BE2");
169
+ cmd.command("detail <hash>").description("\u6309\u54C8\u5E0C\u67E5\u8BE2\u4EA4\u6613\u8BE6\u60C5").action(async (hash) => {
170
+ const opts = cmd.parent.opts();
171
+ const { client, chainId } = createClient(opts);
172
+ const result = await client.get("/v1/tx/detail", { chain_id: chainId, hash });
173
+ formatOutput(result, opts.pretty);
174
+ });
175
+ cmd.command("list <address>").description("\u67E5\u8BE2\u8D26\u6237\u4EA4\u6613\u5217\u8868").option("--from-block <n>", "\u8D77\u59CB\u533A\u5757\u53F7").option("--to-block <n>", "\u7ED3\u675F\u533A\u5757\u53F7").option("--from-timestamp <n>", "\u8D77\u59CB\u65F6\u95F4\u6233").option("--end-timestamp <n>", "\u7ED3\u675F\u65F6\u95F4\u6233").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (address, cmdOpts) => {
176
+ const opts = cmd.parent.opts();
177
+ const { client, chainId } = createClient(opts);
178
+ const params = {
179
+ chain_id: chainId,
180
+ address
181
+ };
182
+ if (cmdOpts.page) params.page = cmdOpts.page;
183
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
184
+ if (cmdOpts.fromBlock) params.from_block = cmdOpts.fromBlock;
185
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
186
+ if (cmdOpts.fromTimestamp) params.from_timestamp = cmdOpts.fromTimestamp;
187
+ if (cmdOpts.endTimestamp) params.end_timestamp = cmdOpts.endTimestamp;
188
+ const result = await client.get("/v1/account/txs", params);
189
+ formatOutput(result, opts.pretty);
190
+ });
191
+ return cmd;
192
+ }
193
+
194
+ // src/commands/contract.ts
195
+ import { Command as Command4 } from "commander";
196
+ function createContractCommand() {
197
+ const cmd = new Command4("contract").description("\u667A\u80FD\u5408\u7EA6\u4EA4\u4E92");
198
+ cmd.command("call").description("\u8C03\u7528\u5408\u7EA6\u53EA\u8BFB\u51FD\u6570").requiredOption("--address <addr>", "\u5408\u7EA6\u5730\u5740").requiredOption("--function <name>", "\u51FD\u6570\u540D").requiredOption("--abi <json>", "\u5408\u7EA6 ABI\uFF08JSON \u5B57\u7B26\u4E32\uFF09").option("--params <json>", "\u51FD\u6570\u53C2\u6570\uFF08JSON \u6570\u7EC4\uFF09", "[]").option("--to-block <n>", "\u533A\u5757\u53F7").action(async (cmdOpts) => {
199
+ const opts = cmd.parent.opts();
200
+ const { client, chainId } = createClient(opts);
201
+ const body = {
202
+ chain_id: Number(chainId),
203
+ contract_address: cmdOpts.address,
204
+ function_name: cmdOpts.function,
205
+ abi: cmdOpts.abi,
206
+ params: (() => {
207
+ try {
208
+ return JSON.parse(cmdOpts.params);
209
+ } catch {
210
+ throw new Error(`--params \u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u63D0\u4F9B\u5408\u6CD5 JSON \u6570\u7EC4\uFF0C\u5982 '["0x..."]'`);
211
+ }
212
+ })(),
213
+ ...cmdOpts.toBlock && { to_block: cmdOpts.toBlock }
214
+ };
215
+ const result = await client.post("/v1/contract/call", body);
216
+ formatOutput(result, opts.pretty);
217
+ });
218
+ return cmd;
219
+ }
220
+
221
+ // src/commands/address.ts
222
+ import { Command as Command5 } from "commander";
223
+ function createAddressCommand() {
224
+ const cmd = new Command5("address").description("\u5730\u5740\u67E5\u8BE2");
225
+ cmd.command("labels <address>").description("\u83B7\u53D6\u5730\u5740\u6807\u7B7E").action(async (address) => {
226
+ const opts = cmd.parent.opts();
227
+ const { client, chainId } = createClient(opts);
228
+ const result = await client.get("/v1/address/labels", { chain_id: chainId, address });
229
+ formatOutput(result, opts.pretty);
230
+ });
231
+ return cmd;
232
+ }
233
+
234
+ // src/commands/token.ts
235
+ import { Command as Command6 } from "commander";
236
+ function createTokenCommand() {
237
+ const cmd = new Command6("token").description("\u4EE3\u5E01\u67E5\u8BE2");
238
+ cmd.command("metadata <contract>").description("\u83B7\u53D6\u4EE3\u5E01\u5143\u6570\u636E").action(async (contract) => {
239
+ const opts = cmd.parent.opts();
240
+ const { client, chainId } = createClient(opts);
241
+ const result = await client.get("/v1/token/metadata", {
242
+ chain_id: chainId,
243
+ contract_address: contract
244
+ });
245
+ formatOutput(result, opts.pretty);
246
+ });
247
+ cmd.command("transfers").description("\u83B7\u53D6\u4EE3\u5E01\u8F6C\u8D26\u8BB0\u5F55").option("--contract <addr>", "\u5408\u7EA6\u5730\u5740").option("--address <addr>", "\u94B1\u5305\u5730\u5740").option("--from-block <n>", "\u8D77\u59CB\u533A\u5757\u53F7").option("--to-block <n>", "\u7ED3\u675F\u533A\u5757\u53F7").option("--from-timestamp <n>", "\u8D77\u59CB\u65F6\u95F4\u6233").option("--end-timestamp <n>", "\u7ED3\u675F\u65F6\u95F4\u6233").action(async (cmdOpts) => {
248
+ const opts = cmd.parent.opts();
249
+ const { client, chainId } = createClient(opts);
250
+ const params = {
251
+ chain_id: chainId
252
+ };
253
+ if (cmdOpts.contract) params.contract_address = cmdOpts.contract;
254
+ if (cmdOpts.address) params.address = cmdOpts.address;
255
+ if (cmdOpts.fromBlock) params.from_block = cmdOpts.fromBlock;
256
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
257
+ if (cmdOpts.fromTimestamp) params.from_timestamp = cmdOpts.fromTimestamp;
258
+ if (cmdOpts.endTimestamp) params.end_timestamp = cmdOpts.endTimestamp;
259
+ if (opts.page) params.page = opts.page;
260
+ if (opts.limit) params.limit = opts.limit;
261
+ const result = await client.get("/v1/token/transfers", params);
262
+ formatOutput(result, opts.pretty);
263
+ });
264
+ cmd.command("holders <contract>").description("\u83B7\u53D6\u4EE3\u5E01\u6301\u6709\u8005\u5217\u8868").action(async (contract) => {
265
+ const opts = cmd.parent.opts();
266
+ const { client, chainId } = createClient(opts);
267
+ const params = {
268
+ chain_id: chainId,
269
+ contract_address: contract
270
+ };
271
+ if (opts.page) params.page = opts.page;
272
+ if (opts.limit) params.limit = opts.limit;
273
+ const result = await client.get("/v1/token/holders", params);
274
+ formatOutput(result, opts.pretty);
275
+ });
276
+ cmd.command("top-holders <contract>").description("\u83B7\u53D6\u4EE3\u5E01 Top \u6301\u6709\u8005").action(async (contract) => {
277
+ const opts = cmd.parent.opts();
278
+ const { client, chainId } = createClient(opts);
279
+ const params = {
280
+ chain_id: chainId,
281
+ contract_address: contract
282
+ };
283
+ if (opts.page) params.page = opts.page;
284
+ if (opts.limit) params.limit = opts.limit;
285
+ const result = await client.get("/v1/token/top-holders", params);
286
+ formatOutput(result, opts.pretty);
287
+ });
288
+ cmd.command("price <contract>").description("\u83B7\u53D6\u4EE3\u5E01\u4EF7\u683C").action(async (contract) => {
289
+ const opts = cmd.parent.opts();
290
+ const { client, chainId } = createClient(opts);
291
+ const result = await client.get("/v1/token/price", {
292
+ chain_id: chainId,
293
+ contract_address: contract
294
+ });
295
+ formatOutput(result, opts.pretty);
296
+ });
297
+ cmd.command("price-history <contract>").description("\u83B7\u53D6\u4EE3\u5E01\u5386\u53F2\u4EF7\u683C").requiredOption("--from <timestamp>", "\u8D77\u59CB\u65F6\u95F4\u6233").requiredOption("--to <timestamp>", "\u7ED3\u675F\u65F6\u95F4\u6233").action(async (contract, cmdOpts) => {
298
+ const opts = cmd.parent.opts();
299
+ const { client, chainId } = createClient(opts);
300
+ const result = await client.get("/v1/token/price/history", {
301
+ chain_id: chainId,
302
+ contract_address: contract,
303
+ from_timestamp: cmdOpts.from,
304
+ end_timestamp: cmdOpts.to
305
+ });
306
+ formatOutput(result, opts.pretty);
307
+ });
308
+ return cmd;
309
+ }
310
+
311
+ // src/commands/nft.ts
312
+ import { Command as Command7 } from "commander";
313
+ function createNftCommand() {
314
+ const cmd = new Command7("nft").description("NFT \u67E5\u8BE2");
315
+ cmd.command("metadata <contract> <token_id>").description("\u83B7\u53D6 NFT \u5143\u6570\u636E").action(async (contract, tokenId) => {
316
+ const opts = cmd.parent.opts();
317
+ const { client, chainId } = createClient(opts);
318
+ const result = await client.get("/v1/nft/metadata", {
319
+ chain_id: chainId,
320
+ contract_address: contract,
321
+ token_id: tokenId
322
+ });
323
+ formatOutput(result, opts.pretty);
324
+ });
325
+ cmd.command("collection <contract>").description("\u83B7\u53D6 NFT \u96C6\u5408\u4FE1\u606F").action(async (contract) => {
326
+ const opts = cmd.parent.opts();
327
+ const { client, chainId } = createClient(opts);
328
+ const result = await client.get("/v1/nft/collection", {
329
+ chain_id: chainId,
330
+ contract_address: contract
331
+ });
332
+ formatOutput(result, opts.pretty);
333
+ });
334
+ cmd.command("collection-items <contract>").description("\u83B7\u53D6 NFT \u96C6\u5408\u5185\u6240\u6709\u9879\u76EE").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (contract, cmdOpts) => {
335
+ const opts = cmd.parent.opts();
336
+ const { client, chainId } = createClient(opts);
337
+ const params = {
338
+ chain_id: chainId,
339
+ contract_address: contract
340
+ };
341
+ if (cmdOpts.page) params.page = cmdOpts.page;
342
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
343
+ const result = await client.get("/v1/nft/collection/items", params);
344
+ formatOutput(result, opts.pretty);
345
+ });
346
+ cmd.command("search <name>").description("\u6309\u540D\u79F0\u641C\u7D22 NFT \u96C6\u5408").option("--contract <addr>", "\u5408\u7EA6\u5730\u5740").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (name, cmdOpts) => {
347
+ const opts = cmd.parent.opts();
348
+ const { client, chainId } = createClient(opts);
349
+ const params = {
350
+ chain_id: chainId,
351
+ name
352
+ };
353
+ if (cmdOpts.contract) params.contract_address = cmdOpts.contract;
354
+ if (cmdOpts.page) params.page = cmdOpts.page;
355
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
356
+ const result = await client.get("/v1/nft/search", params);
357
+ formatOutput(result, opts.pretty);
358
+ });
359
+ cmd.command("transfers").description("\u83B7\u53D6 NFT \u8F6C\u8D26\u8BB0\u5F55").option("--contract <addr>", "\u5408\u7EA6\u5730\u5740").option("--token-id <id>", "Token ID").option("--address <addr>", "\u94B1\u5305\u5730\u5740").option("--from-block <n>", "\u8D77\u59CB\u533A\u5757\u53F7").option("--to-block <n>", "\u7ED3\u675F\u533A\u5757\u53F7").option("--from-timestamp <n>", "\u8D77\u59CB\u65F6\u95F4\u6233").option("--end-timestamp <n>", "\u7ED3\u675F\u65F6\u95F4\u6233").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (cmdOpts) => {
360
+ const opts = cmd.parent.opts();
361
+ const { client, chainId } = createClient(opts);
362
+ const params = {
363
+ chain_id: chainId
364
+ };
365
+ if (cmdOpts.contract) params.contract_address = cmdOpts.contract;
366
+ if (cmdOpts.tokenId) params.token_id = cmdOpts.tokenId;
367
+ if (cmdOpts.address) params.address = cmdOpts.address;
368
+ if (cmdOpts.fromBlock) params.from_block = cmdOpts.fromBlock;
369
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
370
+ if (cmdOpts.fromTimestamp) params.from_timestamp = cmdOpts.fromTimestamp;
371
+ if (cmdOpts.endTimestamp) params.end_timestamp = cmdOpts.endTimestamp;
372
+ if (cmdOpts.page) params.page = cmdOpts.page;
373
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
374
+ const result = await client.get("/v1/nft/transfers", params);
375
+ formatOutput(result, opts.pretty);
376
+ });
377
+ cmd.command("owner <contract> <token_id>").description("\u83B7\u53D6 NFT \u5F53\u524D\u6301\u6709\u8005").option("--to-block <n>", "\u533A\u5757\u53F7").action(async (contract, tokenId, cmdOpts) => {
378
+ const opts = cmd.parent.opts();
379
+ const { client, chainId } = createClient(opts);
380
+ const params = {
381
+ chain_id: chainId,
382
+ contract_address: contract,
383
+ token_id: tokenId
384
+ };
385
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
386
+ const result = await client.get("/v1/nft/owner", params);
387
+ formatOutput(result, opts.pretty);
388
+ });
389
+ cmd.command("owners <contract>").description("\u83B7\u53D6 NFT \u96C6\u5408\u6240\u6709\u6301\u6709\u8005").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (contract, cmdOpts) => {
390
+ const opts = cmd.parent.opts();
391
+ const { client, chainId } = createClient(opts);
392
+ const params = {
393
+ chain_id: chainId,
394
+ contract_address: contract
395
+ };
396
+ if (cmdOpts.page) params.page = cmdOpts.page;
397
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
398
+ const result = await client.get("/v1/nft/owners", params);
399
+ formatOutput(result, opts.pretty);
400
+ });
401
+ cmd.command("owner-history <contract> <token_id>").description("\u83B7\u53D6 NFT \u6301\u6709\u8005\u5386\u53F2").option("--from-block <n>", "\u8D77\u59CB\u533A\u5757\u53F7").option("--to-block <n>", "\u7ED3\u675F\u533A\u5757\u53F7").option("--from-timestamp <n>", "\u8D77\u59CB\u65F6\u95F4\u6233").option("--end-timestamp <n>", "\u7ED3\u675F\u65F6\u95F4\u6233").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (contract, tokenId, cmdOpts) => {
402
+ const opts = cmd.parent.opts();
403
+ const { client, chainId } = createClient(opts);
404
+ const params = {
405
+ chain_id: chainId,
406
+ contract_address: contract,
407
+ token_id: tokenId
408
+ };
409
+ if (cmdOpts.fromBlock) params.from_block = cmdOpts.fromBlock;
410
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
411
+ if (cmdOpts.fromTimestamp) params.from_timestamp = cmdOpts.fromTimestamp;
412
+ if (cmdOpts.endTimestamp) params.end_timestamp = cmdOpts.endTimestamp;
413
+ if (cmdOpts.page) params.page = cmdOpts.page;
414
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
415
+ const result = await client.get("/v1/nft/owner/history", params);
416
+ formatOutput(result, opts.pretty);
417
+ });
418
+ cmd.command("floor-price <contract>").description("\u83B7\u53D6 NFT \u5730\u677F\u4EF7").action(async (contract) => {
419
+ const opts = cmd.parent.opts();
420
+ const { client, chainId } = createClient(opts);
421
+ const result = await client.get("/v1/nft/floor_price", {
422
+ chain_id: chainId,
423
+ contract_address: contract
424
+ });
425
+ formatOutput(result, opts.pretty);
426
+ });
427
+ cmd.command("price-history <contract>").description("\u83B7\u53D6 NFT \u5386\u53F2\u5730\u677F\u4EF7").requiredOption("--from <timestamp>", "\u8D77\u59CB\u65F6\u95F4\u6233").requiredOption("--to <timestamp>", "\u7ED3\u675F\u65F6\u95F4\u6233").action(async (contract, cmdOpts) => {
428
+ const opts = cmd.parent.opts();
429
+ const { client, chainId } = createClient(opts);
430
+ const result = await client.get("/v1/nft/price/history", {
431
+ chain_id: chainId,
432
+ contract_address: contract,
433
+ from_timestamp: cmdOpts.from,
434
+ end_timestamp: cmdOpts.to
435
+ });
436
+ formatOutput(result, opts.pretty);
437
+ });
438
+ cmd.command("trending").description("\u83B7\u53D6\u70ED\u95E8 NFT \u96C6\u5408").option("--range <period>", "\u65F6\u95F4\u8303\u56F4", "7d").option("--exchange <name>", "\u4EA4\u6613\u6240\u540D\u79F0").option("--sort <order>", "\u6392\u5E8F\u65B9\u5F0F").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (cmdOpts) => {
439
+ const opts = cmd.parent.opts();
440
+ const { client, chainId } = createClient(opts);
441
+ const params = {
442
+ chain_id: chainId,
443
+ range: cmdOpts.range
444
+ };
445
+ if (cmdOpts.exchange) params.exchange_name = cmdOpts.exchange;
446
+ if (cmdOpts.sort) params.sort = cmdOpts.sort;
447
+ if (cmdOpts.page) params.page = cmdOpts.page;
448
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
449
+ const result = await client.get("/v1/nft/collection/trending", params);
450
+ formatOutput(result, opts.pretty);
451
+ });
452
+ cmd.command("rarity <contract>").description("\u83B7\u53D6 NFT \u7A00\u6709\u5EA6").option("--token-id <id>", "Token ID").option("--rank-min <n>", "\u6700\u4F4E\u6392\u540D").option("--rank-max <n>", "\u6700\u9AD8\u6392\u540D").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (contract, cmdOpts) => {
453
+ const opts = cmd.parent.opts();
454
+ const { client, chainId } = createClient(opts);
455
+ const params = {
456
+ chain_id: chainId,
457
+ contract_address: contract
458
+ };
459
+ if (cmdOpts.tokenId) params.token_id = cmdOpts.tokenId;
460
+ if (cmdOpts.rankMin) params.rank_min = cmdOpts.rankMin;
461
+ if (cmdOpts.rankMax) params.rank_max = cmdOpts.rankMax;
462
+ if (cmdOpts.page) params.page = cmdOpts.page;
463
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
464
+ const result = await client.get("/v1/nft/rarity", params);
465
+ formatOutput(result, opts.pretty);
466
+ });
467
+ return cmd;
468
+ }
469
+
470
+ // src/commands/balance.ts
471
+ import { Command as Command8 } from "commander";
472
+ function createBalanceCommand() {
473
+ const cmd = new Command8("balance").description("\u4F59\u989D\u4E0E\u8D26\u6237\u67E5\u8BE2");
474
+ cmd.command("native <address>").description("\u83B7\u53D6\u539F\u751F\u4EE3\u5E01\u4F59\u989D").option("--to-block <n>", "\u533A\u5757\u53F7").action(async (address, cmdOpts) => {
475
+ const opts = cmd.parent.opts();
476
+ const { client, chainId } = createClient(opts);
477
+ const params = {
478
+ chain_id: chainId,
479
+ address
480
+ };
481
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
482
+ const result = await client.get("/v1/account/balance", params);
483
+ formatOutput(result, opts.pretty);
484
+ });
485
+ cmd.command("tokens <address>").description("\u83B7\u53D6 ERC20 \u4EE3\u5E01\u4F59\u989D").option("--contract <addr>", "\u6309\u5408\u7EA6\u5730\u5740\u7B5B\u9009").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (address, cmdOpts) => {
486
+ const opts = cmd.parent.opts();
487
+ const { client, chainId } = createClient(opts);
488
+ const params = {
489
+ chain_id: chainId,
490
+ address
491
+ };
492
+ if (cmdOpts.contract) params.contract_address = cmdOpts.contract;
493
+ if (cmdOpts.page) params.page = cmdOpts.page;
494
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
495
+ const result = await client.get("/v1/account/tokens", params);
496
+ formatOutput(result, opts.pretty);
497
+ });
498
+ cmd.command("portfolios <address>").description("\u83B7\u53D6 DeFi \u7EC4\u5408\u6301\u4ED3").option("--chains <ids>", "\u94FE ID\uFF08\u9017\u53F7\u5206\u9694\uFF09").action(async (address, cmdOpts) => {
499
+ const opts = cmd.parent.opts();
500
+ const { client } = createClient(opts);
501
+ const params = {
502
+ address
503
+ };
504
+ if (cmdOpts.chains) params.chain_id = cmdOpts.chains.split(",");
505
+ const result = await client.get("/v1/account/portfolios", params);
506
+ formatOutput(result, opts.pretty);
507
+ });
508
+ cmd.command("nfts <address>").description("\u83B7\u53D6\u6301\u6709\u7684 NFT \u5217\u8868").option("--contract <addr>", "\u6309\u5408\u7EA6\u5730\u5740\u7B5B\u9009").option("--page <n>", "\u9875\u7801").option("--limit <n>", "\u6BCF\u9875\u6761\u6570").action(async (address, cmdOpts) => {
509
+ const opts = cmd.parent.opts();
510
+ const { client, chainId } = createClient(opts);
511
+ const params = {
512
+ chain_id: chainId,
513
+ address
514
+ };
515
+ if (cmdOpts.contract) params.contract_address = cmdOpts.contract;
516
+ if (cmdOpts.page) params.page = cmdOpts.page;
517
+ if (cmdOpts.limit) params.limit = cmdOpts.limit;
518
+ const result = await client.get("/v1/account/nfts", params);
519
+ formatOutput(result, opts.pretty);
520
+ });
521
+ return cmd;
522
+ }
523
+
524
+ // src/commands/domain.ts
525
+ import { Command as Command9 } from "commander";
526
+ function createDomainCommand() {
527
+ const cmd = new Command9("domain").description("\u57DF\u540D\u67E5\u8BE2");
528
+ cmd.command("ens <address>").description("\u83B7\u53D6\u5730\u5740\u6301\u6709\u7684 ENS \u57DF\u540D").option("--to-block <n>", "\u533A\u5757\u53F7").action(async (address, cmdOpts) => {
529
+ const opts = cmd.parent.opts();
530
+ const { client, chainId } = createClient(opts);
531
+ const params = {
532
+ chain_id: chainId,
533
+ address
534
+ };
535
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
536
+ const result = await client.get("/v1/account/ens", params);
537
+ formatOutput(result, opts.pretty);
538
+ });
539
+ cmd.command("ens-resolve <domain>").description("\u89E3\u6790 ENS \u57DF\u540D").option("--to-block <n>", "\u533A\u5757\u53F7").action(async (domain, cmdOpts) => {
540
+ const opts = cmd.parent.opts();
541
+ const { client, chainId } = createClient(opts);
542
+ const params = {
543
+ chain_id: chainId,
544
+ domain
545
+ };
546
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
547
+ const result = await client.get("/v1/ens/records", params);
548
+ formatOutput(result, opts.pretty);
549
+ });
550
+ cmd.command("ens-reverse <address>").description("\u53CD\u5411\u89E3\u6790\u5730\u5740\u5230 ENS \u57DF\u540D").option("--to-block <n>", "\u533A\u5757\u53F7").action(async (address, cmdOpts) => {
551
+ const opts = cmd.parent.opts();
552
+ const { client, chainId } = createClient(opts);
553
+ const params = {
554
+ chain_id: chainId,
555
+ address
556
+ };
557
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
558
+ const result = await client.get("/v1/ens/reverse", params);
559
+ formatOutput(result, opts.pretty);
560
+ });
561
+ cmd.command("spaceid-resolve <domain>").description("\u89E3\u6790 Space ID \u57DF\u540D").option("--to-block <n>", "\u533A\u5757\u53F7").action(async (domain, cmdOpts) => {
562
+ const opts = cmd.parent.opts();
563
+ const { client, chainId } = createClient(opts);
564
+ const params = {
565
+ chain_id: chainId,
566
+ domain
567
+ };
568
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
569
+ const result = await client.get("/v1/space-id/records", params);
570
+ formatOutput(result, opts.pretty);
571
+ });
572
+ cmd.command("spaceid-reverse <address>").description("\u53CD\u5411\u89E3\u6790\u5730\u5740\u5230 Space ID \u57DF\u540D").option("--to-block <n>", "\u533A\u5757\u53F7").action(async (address, cmdOpts) => {
573
+ const opts = cmd.parent.opts();
574
+ const { client, chainId } = createClient(opts);
575
+ const params = {
576
+ chain_id: chainId,
577
+ address
578
+ };
579
+ if (cmdOpts.toBlock) params.to_block = cmdOpts.toBlock;
580
+ const result = await client.get("/v1/space-id/reverse", params);
581
+ formatOutput(result, opts.pretty);
582
+ });
583
+ return cmd;
584
+ }
585
+
586
+ // src/commands/sql.ts
587
+ import { Command as Command10 } from "commander";
588
+ function createSqlCommand() {
589
+ const cmd = new Command10("sql").description("SQL \u67E5\u8BE2\u4E0E\u6267\u884C");
590
+ cmd.command("execute <sql>").description("\u5F02\u6B65\u6267\u884C SQL \u8BED\u53E5").action(async (sql) => {
591
+ const opts = cmd.parent.opts();
592
+ const { client } = createClient(opts);
593
+ const result = await client.post("/query/execute", { sql });
594
+ formatOutput(result, opts.pretty);
595
+ });
596
+ cmd.command("status <execution_id>").description("\u67E5\u8BE2\u6267\u884C\u72B6\u6001").action(async (executionId) => {
597
+ const opts = cmd.parent.opts();
598
+ const { client } = createClient(opts);
599
+ const result = await client.get(`/execution/${executionId}/status`, {});
600
+ formatOutput(result, opts.pretty);
601
+ });
602
+ cmd.command("results <execution_id>").description("\u83B7\u53D6\u6267\u884C\u7ED3\u679C").action(async (executionId) => {
603
+ const opts = cmd.parent.opts();
604
+ const { client } = createClient(opts);
605
+ const result = await client.get(`/execution/${executionId}/results`, {});
606
+ formatOutput(result, opts.pretty);
607
+ });
608
+ return cmd;
609
+ }
610
+
611
+ // src/index.ts
612
+ var program = new Command11();
613
+ program.name("chainbase").description("Chainbase Web3 API \u547D\u4EE4\u884C\u5DE5\u5177").version("0.1.0").option("--chain <id>", "\u94FE ID\uFF08\u9ED8\u8BA4\u4ECE\u914D\u7F6E\u8BFB\u53D6\uFF0C\u5426\u5219\u4E3A 1\uFF09").option("--pretty", "\u4EE5\u4EBA\u7C7B\u53EF\u8BFB\u683C\u5F0F\u8F93\u51FA", false).option("--page <n>", "\u9875\u7801", "1").option("--limit <n>", "\u6BCF\u9875\u6761\u6570", "20");
614
+ program.addCommand(createConfigCommand());
615
+ program.addCommand(createBlockCommand());
616
+ program.addCommand(createTxCommand());
617
+ program.addCommand(createContractCommand());
618
+ program.addCommand(createAddressCommand());
619
+ program.addCommand(createTokenCommand());
620
+ program.addCommand(createNftCommand());
621
+ program.addCommand(createBalanceCommand());
622
+ program.addCommand(createDomainCommand());
623
+ program.addCommand(createSqlCommand());
624
+ async function main() {
625
+ try {
626
+ await program.parseAsync();
627
+ } catch (err) {
628
+ const message = err instanceof Error ? err.message : String(err);
629
+ formatError(message, program.opts().pretty);
630
+ process.exit(1);
631
+ }
632
+ }
633
+ main();
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "chainbase-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for Chainbase Web3 API",
5
+ "type": "module",
6
+ "bin": {
7
+ "chainbase": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsup src/index.ts --format esm --dts --clean",
11
+ "dev": "tsup src/index.ts --format esm --watch",
12
+ "test": "vitest run",
13
+ "test:watch": "vitest",
14
+ "lint": "tsc --noEmit"
15
+ },
16
+ "files": ["dist"],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/chainbase-com/chainbase-cli.git"
20
+ },
21
+ "keywords": [],
22
+ "author": "",
23
+ "license": "ISC",
24
+ "bugs": {
25
+ "url": "https://github.com/chainbase-com/chainbase-cli/issues"
26
+ },
27
+ "homepage": "https://github.com/chainbase-com/chainbase-cli#readme",
28
+ "dependencies": {
29
+ "axios": "^1.13.5",
30
+ "chalk": "^5.6.2",
31
+ "cli-table3": "^0.6.5",
32
+ "commander": "^14.0.3"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^25.3.0",
36
+ "tsup": "^8.5.1",
37
+ "typescript": "^5.9.3",
38
+ "vitest": "^4.0.18"
39
+ }
40
+ }