@chainstream-io/cli 0.0.18 → 0.0.19

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/dist/index.js CHANGED
@@ -84,19 +84,8 @@ var init_config = __esm({
84
84
  }
85
85
  });
86
86
 
87
- // src/wallet/types.ts
88
- function toSdkChain(chain) {
89
- return isSolanaChain(chain) ? "solana" : "evm";
90
- }
91
- var init_types = __esm({
92
- "src/wallet/types.ts"() {
93
- "use strict";
94
- init_config();
95
- }
96
- });
97
-
98
87
  // src/wallet/raw-wallet.ts
99
- import { privateKeyToAccount } from "viem/accounts";
88
+ import { privateKeyToAccount, signTypedData as viemSignTypedData } from "viem/accounts";
100
89
  import { Keypair, VersionedTransaction } from "@solana/web3.js";
101
90
  import { sign as ed25519Sign } from "crypto";
102
91
  function bs58Decode(str) {
@@ -122,37 +111,64 @@ function bs58Decode(str) {
122
111
  }
123
112
  return new Uint8Array(result.reverse());
124
113
  }
125
- var RawKeyWallet;
114
+ function toHexKey(key) {
115
+ return key.startsWith("0x") ? key : `0x${key}`;
116
+ }
117
+ function createRawKeyWallet(key, chain) {
118
+ return isEvmChain(chain) ? new RawEvmWallet(key, chain) : new RawSvmWallet(key, chain);
119
+ }
120
+ var RawEvmWallet, RawSvmWallet;
126
121
  var init_raw_wallet = __esm({
127
122
  "src/wallet/raw-wallet.ts"() {
128
123
  "use strict";
129
- init_types();
130
124
  init_config();
131
- RawKeyWallet = class {
132
- chain;
125
+ RawEvmWallet = class {
126
+ chain = "evm";
133
127
  paymentChain;
134
128
  address;
135
129
  privateKey;
136
130
  constructor(key, chain) {
137
131
  this.paymentChain = chain;
138
- this.chain = toSdkChain(chain);
139
132
  this.privateKey = key;
140
- if (isEvmChain(this.paymentChain)) {
141
- const hex = key.startsWith("0x") ? key : `0x${key}`;
142
- const account = privateKeyToAccount(hex);
143
- this.address = account.address;
144
- } else {
145
- const decoded = bs58Decode(key);
146
- const keypair = Keypair.fromSecretKey(decoded);
147
- this.address = keypair.publicKey.toBase58();
148
- }
133
+ const account = privateKeyToAccount(toHexKey(key));
134
+ this.address = account.address;
135
+ }
136
+ async signMessage(message) {
137
+ const account = privateKeyToAccount(toHexKey(this.privateKey));
138
+ return account.signMessage({ message });
139
+ }
140
+ async signTransaction(serializedTx) {
141
+ const txBytes = Buffer.from(serializedTx, "base64");
142
+ const account = privateKeyToAccount(toHexKey(this.privateKey));
143
+ const signedHex = await account.signTransaction({ type: "legacy" });
144
+ return Buffer.from(signedHex.slice(2), "hex").toString("base64");
145
+ }
146
+ async signTypedData(typedData) {
147
+ return viemSignTypedData({
148
+ privateKey: toHexKey(this.privateKey),
149
+ ...typedData,
150
+ domain: typedData.domain,
151
+ types: typedData.types
152
+ });
153
+ }
154
+ async signRawHash(hash) {
155
+ const account = privateKeyToAccount(toHexKey(this.privateKey));
156
+ return account.sign({ hash });
157
+ }
158
+ };
159
+ RawSvmWallet = class {
160
+ chain = "solana";
161
+ paymentChain;
162
+ address;
163
+ privateKey;
164
+ constructor(key, chain) {
165
+ this.paymentChain = chain;
166
+ this.privateKey = key;
167
+ const decoded = bs58Decode(key);
168
+ const keypair = Keypair.fromSecretKey(decoded);
169
+ this.address = keypair.publicKey.toBase58();
149
170
  }
150
171
  async signMessage(message) {
151
- if (isEvmChain(this.paymentChain)) {
152
- const hex = this.privateKey.startsWith("0x") ? this.privateKey : `0x${this.privateKey}`;
153
- const account = privateKeyToAccount(hex);
154
- return account.signMessage({ message });
155
- }
156
172
  const decoded = bs58Decode(this.privateKey);
157
173
  const secretKeyRaw = decoded.slice(0, 32);
158
174
  const pkcs8Prefix = Buffer.from("302e020100300506032b657004220420", "hex");
@@ -163,17 +179,11 @@ var init_raw_wallet = __esm({
163
179
  }
164
180
  async signTransaction(serializedTx) {
165
181
  const txBytes = Buffer.from(serializedTx, "base64");
166
- if (!isEvmChain(this.paymentChain)) {
167
- const tx = VersionedTransaction.deserialize(new Uint8Array(txBytes));
168
- const decoded = bs58Decode(this.privateKey);
169
- const keypair = Keypair.fromSecretKey(decoded);
170
- tx.sign([keypair]);
171
- return Buffer.from(tx.serialize()).toString("base64");
172
- }
173
- const hex = this.privateKey.startsWith("0x") ? this.privateKey : `0x${this.privateKey}`;
174
- const account = privateKeyToAccount(hex);
175
- const signedHex = await account.signTransaction({ type: "legacy" });
176
- return Buffer.from(signedHex.slice(2), "hex").toString("base64");
182
+ const tx = VersionedTransaction.deserialize(new Uint8Array(txBytes));
183
+ const decoded = bs58Decode(this.privateKey);
184
+ const keypair = Keypair.fromSecretKey(decoded);
185
+ tx.sign([keypair]);
186
+ return Buffer.from(tx.serialize()).toString("base64");
177
187
  }
178
188
  };
179
189
  }
@@ -328,74 +338,131 @@ var init_turnkey = __esm({
328
338
  });
329
339
 
330
340
  // src/wallet/turnkey-wallet.ts
331
- var TurnkeyWallet;
341
+ import { hashTypedData } from "viem";
342
+ async function turnkeySignRawPayload(creds, address, payloadHex, hashFunction) {
343
+ const result = await turnkeyRequest(
344
+ "/public/v1/submit/sign_raw_payload",
345
+ {
346
+ type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
347
+ timestampMs: Date.now().toString(),
348
+ organizationId: creds.organizationId,
349
+ parameters: {
350
+ signWith: address,
351
+ payload: payloadHex,
352
+ encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
353
+ hashFunction
354
+ }
355
+ },
356
+ creds
357
+ );
358
+ const sig = result.activity?.result?.signRawPayloadResult;
359
+ if (!sig) throw new Error("Turnkey sign: no signature in response");
360
+ return sig;
361
+ }
362
+ function formatEvmSignature(sig) {
363
+ const v = sig.v === "00" ? "1b" : "1c";
364
+ return `0x${sig.r}${sig.s}${v}`;
365
+ }
366
+ async function createTurnkeyWallets(creds) {
367
+ const [evmAddr, solAddr] = await Promise.all([
368
+ getEvmAddress(creds),
369
+ getSolanaAddress(creds)
370
+ ]);
371
+ return {
372
+ base: new TurnkeyEvmWallet(creds, "base", evmAddr),
373
+ sol: new TurnkeySvmWallet(creds, "sol", solAddr)
374
+ };
375
+ }
376
+ var TurnkeyEvmWallet, TurnkeySvmWallet;
332
377
  var init_turnkey_wallet = __esm({
333
378
  "src/wallet/turnkey-wallet.ts"() {
334
379
  "use strict";
335
- init_types();
336
- init_config();
337
380
  init_turnkey();
338
- TurnkeyWallet = class _TurnkeyWallet {
339
- chain;
381
+ TurnkeyEvmWallet = class {
382
+ chain = "evm";
340
383
  paymentChain;
341
384
  address;
342
385
  creds;
343
386
  constructor(creds, chain, address) {
344
387
  this.creds = creds;
345
388
  this.paymentChain = chain;
346
- this.chain = toSdkChain(chain);
347
389
  this.address = address;
348
390
  }
349
- static async create(creds) {
350
- const [evmAddr, solAddr] = await Promise.all([
351
- getEvmAddress(creds),
352
- getSolanaAddress(creds)
353
- ]);
354
- return {
355
- base: new _TurnkeyWallet(creds, "base", evmAddr),
356
- sol: new _TurnkeyWallet(creds, "sol", solAddr)
357
- };
358
- }
359
391
  async signMessage(message) {
360
- let payloadHex;
361
- let hashFunction;
362
- if (isEvmChain(this.paymentChain)) {
363
- const prefix = `Ethereum Signed Message:
392
+ const prefix = `Ethereum Signed Message:
364
393
  ${message.length}`;
365
- const prefixed = Buffer.concat([Buffer.from(prefix, "utf-8"), Buffer.from(message, "utf-8")]);
366
- payloadHex = prefixed.toString("hex");
367
- hashFunction = "HASH_FUNCTION_KECCAK256";
368
- } else {
369
- payloadHex = Buffer.from(message, "utf-8").toString("hex");
370
- hashFunction = "HASH_FUNCTION_NOT_APPLICABLE";
371
- }
394
+ const prefixed = Buffer.concat([Buffer.from(prefix, "utf-8"), Buffer.from(message, "utf-8")]);
395
+ const sig = await turnkeySignRawPayload(
396
+ this.creds,
397
+ this.address,
398
+ prefixed.toString("hex"),
399
+ "HASH_FUNCTION_KECCAK256"
400
+ );
401
+ return formatEvmSignature(sig);
402
+ }
403
+ async signTransaction(serializedTx) {
404
+ const txBytes = Buffer.from(serializedTx, "base64");
372
405
  const result = await turnkeyRequest(
373
- "/public/v1/submit/sign_raw_payload",
406
+ "/public/v1/submit/sign_transaction",
374
407
  {
375
- type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
408
+ type: "ACTIVITY_TYPE_SIGN_TRANSACTION_V2",
376
409
  timestampMs: Date.now().toString(),
377
410
  organizationId: this.creds.organizationId,
378
411
  parameters: {
379
412
  signWith: this.address,
380
- payload: payloadHex,
381
- encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
382
- hashFunction
413
+ unsignedTransaction: txBytes.toString("hex"),
414
+ type: "TRANSACTION_TYPE_ETHEREUM"
383
415
  }
384
416
  },
385
417
  this.creds
386
418
  );
387
- const sigResult = result.activity?.result?.signRawPayloadResult;
388
- if (!sigResult) throw new Error("Turnkey signMessage: no signature in response");
389
- if (isEvmChain(this.paymentChain)) {
390
- const v = sigResult.v === "00" ? "1b" : "1c";
391
- return `0x${sigResult.r}${sigResult.s}${v}`;
392
- }
393
- return `${sigResult.r}${sigResult.s}`;
419
+ const signedHex = result.activity?.result?.signTransactionResult?.signedTransaction;
420
+ if (!signedHex) throw new Error("Turnkey signTransaction: no signed tx in response");
421
+ return Buffer.from(signedHex, "hex").toString("base64");
422
+ }
423
+ async signTypedData(typedData) {
424
+ const digest = hashTypedData(typedData);
425
+ const payloadHex = digest.slice(2);
426
+ const sig = await turnkeySignRawPayload(
427
+ this.creds,
428
+ this.address,
429
+ payloadHex,
430
+ "HASH_FUNCTION_NO_OP"
431
+ );
432
+ return formatEvmSignature(sig);
433
+ }
434
+ async signRawHash(hash) {
435
+ const payloadHex = hash.startsWith("0x") ? hash.slice(2) : hash;
436
+ const sig = await turnkeySignRawPayload(
437
+ this.creds,
438
+ this.address,
439
+ payloadHex,
440
+ "HASH_FUNCTION_NO_OP"
441
+ );
442
+ return formatEvmSignature(sig);
443
+ }
444
+ };
445
+ TurnkeySvmWallet = class {
446
+ chain = "solana";
447
+ paymentChain;
448
+ address;
449
+ creds;
450
+ constructor(creds, chain, address) {
451
+ this.creds = creds;
452
+ this.paymentChain = chain;
453
+ this.address = address;
454
+ }
455
+ async signMessage(message) {
456
+ const sig = await turnkeySignRawPayload(
457
+ this.creds,
458
+ this.address,
459
+ Buffer.from(message, "utf-8").toString("hex"),
460
+ "HASH_FUNCTION_NOT_APPLICABLE"
461
+ );
462
+ return `${sig.r}${sig.s}`;
394
463
  }
395
464
  async signTransaction(serializedTx) {
396
465
  const txBytes = Buffer.from(serializedTx, "base64");
397
- const unsignedHex = txBytes.toString("hex");
398
- const txType = isEvmChain(this.paymentChain) ? "TRANSACTION_TYPE_ETHEREUM" : "TRANSACTION_TYPE_SOLANA";
399
466
  const result = await turnkeyRequest(
400
467
  "/public/v1/submit/sign_transaction",
401
468
  {
@@ -404,8 +471,8 @@ ${message.length}`;
404
471
  organizationId: this.creds.organizationId,
405
472
  parameters: {
406
473
  signWith: this.address,
407
- unsignedTransaction: unsignedHex,
408
- type: txType
474
+ unsignedTransaction: txBytes.toString("hex"),
475
+ type: "TRANSACTION_TYPE_SOLANA"
409
476
  }
410
477
  },
411
478
  this.creds
@@ -426,7 +493,7 @@ __export(wallet_exports, {
426
493
  });
427
494
  function createWallet(config, mode) {
428
495
  if (mode === "raw" && config.rawWallet) {
429
- return new RawKeyWallet(config.rawWallet.key, config.rawWallet.chain);
496
+ return createRawKeyWallet(config.rawWallet.key, config.rawWallet.chain);
430
497
  }
431
498
  if (mode === "turnkey" && config.turnkey) {
432
499
  const chain = config.walletChain ?? "base";
@@ -437,7 +504,7 @@ function createWallet(config, mode) {
437
504
  Please re-login to resolve addresses: chainstream login`
438
505
  );
439
506
  }
440
- return new TurnkeyWallet(config.turnkey, chain, address);
507
+ return isEvmChain(chain) ? new TurnkeyEvmWallet(config.turnkey, chain, address) : new TurnkeySvmWallet(config.turnkey, chain, address);
441
508
  }
442
509
  throw new Error(
443
510
  "No wallet configured. Run:\n chainstream login # Create Turnkey wallet\n chainstream wallet set-raw # Use raw private key (dev)"
@@ -445,11 +512,11 @@ Please re-login to resolve addresses: chainstream login`
445
512
  }
446
513
  async function createWalletWithAddresses(config, mode) {
447
514
  if (mode === "raw" && config.rawWallet) {
448
- const w = new RawKeyWallet(config.rawWallet.key, config.rawWallet.chain);
515
+ const w = createRawKeyWallet(config.rawWallet.key, config.rawWallet.chain);
449
516
  return isEvmChain(config.rawWallet.chain) ? { base: w } : { sol: w };
450
517
  }
451
518
  if (mode === "turnkey" && config.turnkey) {
452
- const wallets = await TurnkeyWallet.create(config.turnkey);
519
+ const wallets = await createTurnkeyWallets(config.turnkey);
453
520
  return { base: wallets.base, sol: wallets.sol };
454
521
  }
455
522
  throw new Error("No wallet configured.");
@@ -476,7 +543,7 @@ import { wrapFetchWithPayment } from "@x402/fetch";
476
543
  import { ExactEvmScheme } from "@x402/evm/exact/client";
477
544
  import { ExactSvmScheme } from "@x402/svm/exact/client";
478
545
  import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
479
- import { hashTypedData } from "viem";
546
+ import { hashTypedData as hashTypedData2 } from "viem";
480
547
  import { createKeyPairSignerFromBytes } from "@solana/kit";
481
548
  import * as readline from "readline/promises";
482
549
  function createTurnkeyPaymentAccount(creds) {
@@ -487,7 +554,7 @@ function createTurnkeyPaymentAccount(creds) {
487
554
  source: "custom",
488
555
  publicKey: "0x",
489
556
  async signTypedData(args) {
490
- const digest = hashTypedData(args);
557
+ const digest = hashTypedData2(args);
491
558
  const result = await turnkeyRequest(
492
559
  "/public/v1/submit/sign_raw_payload",
493
560
  {
@@ -687,16 +754,6 @@ function createClient() {
687
754
  "Not authenticated. Run one of:\n chainstream login # Create ChainStream Wallet (no email)\n chainstream login --email # Email OTP login\n chainstream wallet set-raw # Import private key (dev)\n chainstream config set --key apiKey --value <key> # API key only"
688
755
  );
689
756
  }
690
- function requireWallet() {
691
- const config = loadConfig();
692
- const walletMode = getWalletMode(config);
693
- if (!walletMode) {
694
- throw new Error(
695
- "Wallet required for this operation. Run:\n chainstream login # Create ChainStream Wallet\n chainstream wallet set-raw # Dev: use raw private key"
696
- );
697
- }
698
- return createClient();
699
- }
700
757
  async function callWithAutoPayment(fn, retried = false) {
701
758
  try {
702
759
  return await fn();
@@ -817,11 +874,6 @@ function validateAddress(address, chain) {
817
874
  throw new Error(`Invalid address for chain "${chain}": "${address}"`);
818
875
  }
819
876
  }
820
- function validateSlippage(value) {
821
- if (value < 1e-3 || value > 0.5) {
822
- throw new Error(`Invalid slippage: ${value}. Must be between 0.001 (0.1%) and 0.5 (50%).`);
823
- }
824
- }
825
877
  function resolveCurrency(nameOrAddress, chain) {
826
878
  const upper = nameOrAddress.toUpperCase();
827
879
  const resolved = CURRENCY_MAP[chain]?.[upper];
@@ -830,13 +882,8 @@ function resolveCurrency(nameOrAddress, chain) {
830
882
  }
831
883
 
832
884
  // src/lib/output.ts
833
- var EXPLORERS = {
834
- sol: "https://solscan.io/tx/",
835
- bsc: "https://bscscan.com/tx/",
836
- eth: "https://etherscan.io/tx/"
837
- };
838
- function printResult(data, raw) {
839
- if (raw) {
885
+ function printResult(data, json) {
886
+ if (json) {
840
887
  process.stdout.write(JSON.stringify(data) + "\n");
841
888
  } else {
842
889
  process.stdout.write(JSON.stringify(data, null, 2) + "\n");
@@ -854,27 +901,23 @@ function exitOnError(err) {
854
901
  }
855
902
  process.exit(1);
856
903
  }
857
- function explorerUrl(chain, txHash) {
858
- const base = EXPLORERS[chain] ?? EXPLORERS["eth"];
859
- return `${base}${txHash}`;
860
- }
861
904
 
862
905
  // src/commands/token.ts
863
906
  function registerTokenCommands(program2) {
864
907
  const token = program2.command("token").description("Token information and analytics");
865
- token.command("search").description("Search tokens by keyword").requiredOption("--keyword <keyword>", "Search keyword (name, symbol, or address)").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").option("--limit <n>", "Max results", "20").option("--raw", "Single-line JSON output").action(async (opts) => {
908
+ token.command("search").description("Search tokens by keyword").requiredOption("--keyword <keyword>", "Search keyword (name, symbol, or address)").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").option("--limit <n>", "Max results", "20").action(async (opts, cmd) => {
866
909
  try {
867
910
  validateChain(opts.chain);
868
911
  const client = createClient();
869
912
  const result = await callWithAutoPayment(
870
913
  () => client.token.search({ q: opts.keyword, chains: [opts.chain], limit: Number(opts.limit) })
871
914
  );
872
- printResult(result, opts.raw);
915
+ printResult(result, cmd.optsWithGlobals().json);
873
916
  } catch (err) {
874
917
  exitOnError(err);
875
918
  }
876
919
  });
877
- token.command("info").description("Get full token detail").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").option("--raw", "Single-line JSON output").action(async (opts) => {
920
+ token.command("info").description("Get full token detail").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").action(async (opts, cmd) => {
878
921
  try {
879
922
  validateChain(opts.chain);
880
923
  validateAddress(opts.address, opts.chain);
@@ -882,12 +925,12 @@ function registerTokenCommands(program2) {
882
925
  const result = await callWithAutoPayment(
883
926
  () => client.token.getToken(opts.chain, opts.address)
884
927
  );
885
- printResult(result, opts.raw);
928
+ printResult(result, cmd.optsWithGlobals().json);
886
929
  } catch (err) {
887
930
  exitOnError(err);
888
931
  }
889
932
  });
890
- token.command("security").description("Check token security (honeypot, mint auth, freeze auth)").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").option("--raw", "Single-line JSON output").action(async (opts) => {
933
+ token.command("security").description("Check token security (honeypot, mint auth, freeze auth)").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").action(async (opts, cmd) => {
891
934
  try {
892
935
  validateChain(opts.chain);
893
936
  validateAddress(opts.address, opts.chain);
@@ -895,12 +938,12 @@ function registerTokenCommands(program2) {
895
938
  const result = await callWithAutoPayment(
896
939
  () => client.token.getSecurity(opts.chain, opts.address)
897
940
  );
898
- printResult(result, opts.raw);
941
+ printResult(result, cmd.optsWithGlobals().json);
899
942
  } catch (err) {
900
943
  exitOnError(err);
901
944
  }
902
945
  });
903
- token.command("holders").description("Get top token holders").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").option("--limit <n>", "Max results", "20").option("--raw", "Single-line JSON output").action(async (opts) => {
946
+ token.command("holders").description("Get top token holders").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").option("--limit <n>", "Max results", "20").action(async (opts, cmd) => {
904
947
  try {
905
948
  validateChain(opts.chain);
906
949
  validateAddress(opts.address, opts.chain);
@@ -908,12 +951,12 @@ function registerTokenCommands(program2) {
908
951
  const result = await callWithAutoPayment(
909
952
  () => client.token.getTopHolders(opts.chain, opts.address, { limit: Number(opts.limit) })
910
953
  );
911
- printResult(result, opts.raw);
954
+ printResult(result, cmd.optsWithGlobals().json);
912
955
  } catch (err) {
913
956
  exitOnError(err);
914
957
  }
915
958
  });
916
- token.command("candles").description("Get OHLCV candlestick data").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").requiredOption("--resolution <res>", "Resolution: 1m/5m/15m/1h/4h/1d").option("--from <timestamp>", "Start time (Unix seconds)").option("--to <timestamp>", "End time (Unix seconds)").option("--limit <n>", "Max candles", "100").option("--raw", "Single-line JSON output").action(async (opts) => {
959
+ token.command("candles").description("Get OHLCV candlestick data").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").requiredOption("--resolution <res>", "Resolution: 1m/5m/15m/1h/4h/1d").option("--from <timestamp>", "Start time (Unix seconds)").option("--to <timestamp>", "End time (Unix seconds)").option("--limit <n>", "Max candles", "100").action(async (opts, cmd) => {
917
960
  try {
918
961
  validateChain(opts.chain);
919
962
  validateAddress(opts.address, opts.chain);
@@ -927,12 +970,12 @@ function registerTokenCommands(program2) {
927
970
  const result = await callWithAutoPayment(
928
971
  () => client.token.getCandles(opts.chain, opts.address, params)
929
972
  );
930
- printResult(result, opts.raw);
973
+ printResult(result, cmd.optsWithGlobals().json);
931
974
  } catch (err) {
932
975
  exitOnError(err);
933
976
  }
934
977
  });
935
- token.command("pools").description("Get liquidity pools for a token").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").option("--raw", "Single-line JSON output").action(async (opts) => {
978
+ token.command("pools").description("Get liquidity pools for a token").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Token contract address").action(async (opts, cmd) => {
936
979
  try {
937
980
  validateChain(opts.chain);
938
981
  validateAddress(opts.address, opts.chain);
@@ -940,7 +983,7 @@ function registerTokenCommands(program2) {
940
983
  const result = await callWithAutoPayment(
941
984
  () => client.token.getPools(opts.chain, opts.address)
942
985
  );
943
- printResult(result, opts.raw);
986
+ printResult(result, cmd.optsWithGlobals().json);
944
987
  } catch (err) {
945
988
  exitOnError(err);
946
989
  }
@@ -950,7 +993,7 @@ function registerTokenCommands(program2) {
950
993
  // src/commands/market.ts
951
994
  function registerMarketCommands(program2) {
952
995
  const market = program2.command("market").description("Market data and trending tokens");
953
- market.command("trending").description("Get hot/trending tokens").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--duration <dur>", "Duration: 1h/6h/24h").option("--limit <n>", "Max results").option("--raw", "Single-line JSON output").action(async (opts) => {
996
+ market.command("trending").description("Get hot/trending tokens").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--duration <dur>", "Duration: 1h/6h/24h").option("--limit <n>", "Max results").action(async (opts, cmd) => {
954
997
  try {
955
998
  validateChain(opts.chain);
956
999
  const client = createClient();
@@ -959,12 +1002,12 @@ function registerMarketCommands(program2) {
959
1002
  const result = await callWithAutoPayment(
960
1003
  () => client.ranking.getHotTokens(opts.chain, opts.duration, params)
961
1004
  );
962
- printResult(result, opts.raw);
1005
+ printResult(result, cmd.optsWithGlobals().json);
963
1006
  } catch (err) {
964
1007
  exitOnError(err);
965
1008
  }
966
1009
  });
967
- market.command("new").description("Get newly created tokens").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").option("--limit <n>", "Max results").option("--raw", "Single-line JSON output").action(async (opts) => {
1010
+ market.command("new").description("Get newly created tokens").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").option("--limit <n>", "Max results").action(async (opts, cmd) => {
968
1011
  try {
969
1012
  validateChain(opts.chain);
970
1013
  const client = createClient();
@@ -973,12 +1016,12 @@ function registerMarketCommands(program2) {
973
1016
  const result = await callWithAutoPayment(
974
1017
  () => client.ranking.getNewTokens(opts.chain, params)
975
1018
  );
976
- printResult(result, opts.raw);
1019
+ printResult(result, cmd.optsWithGlobals().json);
977
1020
  } catch (err) {
978
1021
  exitOnError(err);
979
1022
  }
980
1023
  });
981
- market.command("trades").description("Get recent trades").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").option("--token <address>", "Filter by token address").option("--limit <n>", "Max results").option("--raw", "Single-line JSON output").action(async (opts) => {
1024
+ market.command("trades").description("Get recent trades").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").option("--token <address>", "Filter by token address").option("--limit <n>", "Max results").action(async (opts, cmd) => {
982
1025
  try {
983
1026
  validateChain(opts.chain);
984
1027
  const client = createClient();
@@ -988,7 +1031,7 @@ function registerMarketCommands(program2) {
988
1031
  const result = await callWithAutoPayment(
989
1032
  () => client.trade.getTrades(opts.chain, params)
990
1033
  );
991
- printResult(result, opts.raw);
1034
+ printResult(result, cmd.optsWithGlobals().json);
992
1035
  } catch (err) {
993
1036
  exitOnError(err);
994
1037
  }
@@ -999,8 +1042,25 @@ function registerMarketCommands(program2) {
999
1042
  init_config();
1000
1043
  import * as readline2 from "readline/promises";
1001
1044
  function registerWalletCommands(program2) {
1002
- const wallet = program2.command("wallet").description("Wallet analytics and management");
1003
- wallet.command("profile").description("Wallet profile: PnL + net worth + top holdings").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Wallet address").option("--raw", "Single-line JSON output").action(async (opts) => {
1045
+ const wallet = program2.command("wallet").description("Wallet analytics, management, and signing");
1046
+ wallet.command("sign").description("Sign a serialized transaction").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--tx <base64>", "Base64-encoded unsigned transaction (serializedTx)").action(async (opts, cmd) => {
1047
+ try {
1048
+ validateChain(opts.chain);
1049
+ const { createWallet: createWallet2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
1050
+ const { getWalletMode: getWalletMode2, isSolanaChain: isSolanaChain2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1051
+ const config = loadConfig();
1052
+ const walletMode = getWalletMode2(config);
1053
+ if (!walletMode) throw new Error("Wallet required for signing. Run: chainstream login");
1054
+ const signChain = isSolanaChain2(opts.chain) ? "sol" : "base";
1055
+ const signConfig = { ...config, walletChain: signChain };
1056
+ const w = createWallet2(signConfig, walletMode);
1057
+ const signedTx = await w.signTransaction(opts.tx);
1058
+ printResult({ signedTx }, cmd.optsWithGlobals().json);
1059
+ } catch (err) {
1060
+ exitOnError(err);
1061
+ }
1062
+ });
1063
+ wallet.command("profile").description("Wallet profile: PnL + net worth + top holdings").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Wallet address").action(async (opts, cmd) => {
1004
1064
  try {
1005
1065
  validateChain(opts.chain);
1006
1066
  validateAddress(opts.address, opts.chain);
@@ -1012,12 +1072,12 @@ function registerWalletCommands(program2) {
1012
1072
  client.wallet.getTokensBalance(opts.chain, opts.address)
1013
1073
  ])
1014
1074
  );
1015
- printResult({ pnl, netWorth, balance }, opts.raw);
1075
+ printResult({ pnl, netWorth, balance }, cmd.optsWithGlobals().json);
1016
1076
  } catch (err) {
1017
1077
  exitOnError(err);
1018
1078
  }
1019
1079
  });
1020
- wallet.command("pnl").description("Get wallet PnL details").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Wallet address").option("--raw", "Single-line JSON output").action(async (opts) => {
1080
+ wallet.command("pnl").description("Get wallet PnL details").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Wallet address").action(async (opts, cmd) => {
1021
1081
  try {
1022
1082
  validateChain(opts.chain);
1023
1083
  validateAddress(opts.address, opts.chain);
@@ -1025,12 +1085,12 @@ function registerWalletCommands(program2) {
1025
1085
  const result = await callWithAutoPayment(
1026
1086
  () => client.wallet.getPnl(opts.chain, opts.address)
1027
1087
  );
1028
- printResult(result, opts.raw);
1088
+ printResult(result, cmd.optsWithGlobals().json);
1029
1089
  } catch (err) {
1030
1090
  exitOnError(err);
1031
1091
  }
1032
1092
  });
1033
- wallet.command("holdings").description("Get wallet token balances").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Wallet address").option("--limit <n>", "Max results").option("--raw", "Single-line JSON output").action(async (opts) => {
1093
+ wallet.command("holdings").description("Get wallet token balances").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Wallet address").option("--limit <n>", "Max results").action(async (opts, cmd) => {
1034
1094
  try {
1035
1095
  validateChain(opts.chain);
1036
1096
  validateAddress(opts.address, opts.chain);
@@ -1040,12 +1100,12 @@ function registerWalletCommands(program2) {
1040
1100
  const result = await callWithAutoPayment(
1041
1101
  () => client.wallet.getTokensBalance(opts.chain, opts.address, params)
1042
1102
  );
1043
- printResult(result, opts.raw);
1103
+ printResult(result, cmd.optsWithGlobals().json);
1044
1104
  } catch (err) {
1045
1105
  exitOnError(err);
1046
1106
  }
1047
1107
  });
1048
- wallet.command("activity").description("Get wallet transfer history").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Wallet address").option("--limit <n>", "Max results").option("--raw", "Single-line JSON output").action(async (opts) => {
1108
+ wallet.command("activity").description("Get wallet transfer history").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--address <address>", "Wallet address").option("--limit <n>", "Max results").action(async (opts, cmd) => {
1049
1109
  try {
1050
1110
  validateChain(opts.chain);
1051
1111
  validateAddress(opts.address, opts.chain);
@@ -1055,42 +1115,46 @@ function registerWalletCommands(program2) {
1055
1115
  const result = await callWithAutoPayment(
1056
1116
  () => client.wallet.getWalletTransfers(opts.chain, opts.address, params)
1057
1117
  );
1058
- printResult(result, opts.raw);
1118
+ printResult(result, cmd.optsWithGlobals().json);
1059
1119
  } catch (err) {
1060
1120
  exitOnError(err);
1061
1121
  }
1062
1122
  });
1063
- wallet.command("address").description("Show current wallet addresses").action(async () => {
1123
+ wallet.command("address").description("Show current wallet addresses").action(async (_opts, cmd) => {
1064
1124
  try {
1125
+ const json = cmd.optsWithGlobals().json;
1065
1126
  const config = loadConfig();
1066
1127
  const walletMode = (await Promise.resolve().then(() => (init_config(), config_exports))).getWalletMode(config);
1128
+ const addresses = {};
1067
1129
  if (walletMode === "turnkey" && config.turnkey) {
1068
1130
  if (config.turnkey.evmAddress || config.turnkey.solanaAddress) {
1069
- if (config.turnkey.evmAddress) process.stdout.write(`Base: ${config.turnkey.evmAddress}
1070
- `);
1071
- if (config.turnkey.solanaAddress) process.stdout.write(`Solana: ${config.turnkey.solanaAddress}
1072
- `);
1131
+ if (config.turnkey.evmAddress) addresses.base = config.turnkey.evmAddress;
1132
+ if (config.turnkey.solanaAddress) addresses.solana = config.turnkey.solanaAddress;
1073
1133
  } else {
1074
1134
  const { createWalletWithAddresses: createWalletWithAddresses2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
1075
1135
  const wallets = await createWalletWithAddresses2(config, "turnkey");
1076
- if (wallets.base) process.stdout.write(`Base: ${wallets.base.address}
1077
- `);
1078
- if (wallets.sol) process.stdout.write(`Solana: ${wallets.sol.address}
1079
- `);
1136
+ if (wallets.base) addresses.base = wallets.base.address;
1137
+ if (wallets.sol) addresses.solana = wallets.sol.address;
1080
1138
  }
1081
1139
  } else if (config.rawWallet) {
1082
1140
  const { createWallet: createWallet2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
1083
1141
  const w = createWallet2(config, "raw");
1084
- process.stdout.write(`${w.paymentChain.toUpperCase()}: ${w.address}
1085
- `);
1086
- } else {
1087
- process.stdout.write("No wallet configured. Run: chainstream login\n");
1142
+ addresses[w.paymentChain] = w.address;
1143
+ }
1144
+ if (Object.keys(addresses).length === 0) {
1145
+ if (json) {
1146
+ printResult({ error: "No wallet configured" }, true);
1147
+ } else {
1148
+ process.stdout.write("No wallet configured. Run: chainstream login\n");
1149
+ }
1150
+ return;
1088
1151
  }
1152
+ printResult(addresses, json);
1089
1153
  } catch (err) {
1090
1154
  exitOnError(err);
1091
1155
  }
1092
1156
  });
1093
- wallet.command("balance").description("Show current wallet balance").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").option("--raw", "Single-line JSON output").action(async (opts) => {
1157
+ wallet.command("balance").description("Show current wallet balance").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").action(async (opts, cmd) => {
1094
1158
  try {
1095
1159
  validateChain(opts.chain);
1096
1160
  const config = loadConfig();
@@ -1112,34 +1176,38 @@ function registerWalletCommands(program2) {
1112
1176
  const result = await callWithAutoPayment(
1113
1177
  () => client.wallet.getTokensBalance(opts.chain, address)
1114
1178
  );
1115
- printResult(result, opts.raw);
1179
+ printResult(result, cmd.optsWithGlobals().json);
1116
1180
  } catch (err) {
1117
1181
  exitOnError(err);
1118
1182
  }
1119
1183
  });
1120
- wallet.command("set-raw").description("Set raw private key (dev/testing only)").requiredOption("--chain <chain>", "Chain: base/sol").action(async (opts) => {
1184
+ wallet.command("set-raw").description("Set raw private key (dev/testing only)").requiredOption("--chain <chain>", "Chain: base/sol").action(async (opts, cmd) => {
1121
1185
  try {
1186
+ const json = cmd.optsWithGlobals().json;
1122
1187
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
1123
- process.stdout.write("\u26A0 WARNING: Raw private key will be stored in plaintext.\n");
1124
- process.stdout.write(" Use Turnkey (chainstream login) for production.\n\n");
1125
- const key = await rl.question("Enter private key: ");
1188
+ if (!json) {
1189
+ process.stdout.write("WARNING: Raw private key will be stored in plaintext.\n");
1190
+ process.stdout.write(" Use Turnkey (chainstream login) for production.\n\n");
1191
+ }
1192
+ const key = await rl.question(json ? "" : "Enter private key: ");
1126
1193
  rl.close();
1127
1194
  if (!key.trim()) throw new Error("Empty key provided.");
1128
1195
  updateConfig({
1129
1196
  rawWallet: { key: key.trim(), chain: opts.chain }
1130
1197
  });
1131
- process.stdout.write(`Raw wallet set (${opts.chain}). Run 'chainstream wallet address' to verify.
1132
- `);
1198
+ const { createWallet: createWallet2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
1199
+ const w = createWallet2(loadConfig(), "raw");
1200
+ printResult({ chain: opts.chain, address: w.address }, json);
1133
1201
  } catch (err) {
1134
1202
  exitOnError(err);
1135
1203
  }
1136
1204
  });
1137
- wallet.command("pricing").description("Show available x402 quota plans").option("--raw", "Single-line JSON output").action(async (opts) => {
1205
+ wallet.command("pricing").description("Show available x402 quota plans").action(async (_opts, cmd) => {
1138
1206
  try {
1139
1207
  const config = loadConfig();
1140
1208
  const { getPricing: getPricing2 } = await Promise.resolve().then(() => (init_x402(), x402_exports));
1141
1209
  const plans = await getPricing2(config.baseUrl);
1142
- printResult(plans, opts.raw);
1210
+ printResult(plans, cmd.optsWithGlobals().json);
1143
1211
  } catch (err) {
1144
1212
  exitOnError(err);
1145
1213
  }
@@ -1147,121 +1215,136 @@ function registerWalletCommands(program2) {
1147
1215
  }
1148
1216
 
1149
1217
  // src/commands/dex.ts
1150
- import * as readline3 from "readline/promises";
1151
1218
  function registerDexCommands(program2) {
1152
- const dex = program2.command("dex").description("DEX swap, quote, and token creation");
1153
- dex.command("quote").description("Get swap quote (read-only)").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--input-token <addr>", "Input token (address or SOL/ETH/BNB/USDC)").requiredOption("--output-token <addr>", "Output token (address or SOL/ETH/BNB/USDC)").requiredOption("--amount <amount>", "Input amount (smallest unit)").option("--raw", "Single-line JSON output").action(async (opts) => {
1219
+ const dex = program2.command("dex").description("DEX route, swap, and token creation (returns unsigned transactions)");
1220
+ dex.command("route").description("Get aggregated route + build unsigned transaction").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--from <address>", "Sender wallet address").requiredOption("--input-token <addr>", "Input token (address or SOL/ETH/BNB/USDC)").requiredOption("--output-token <addr>", "Output token (address or SOL/ETH/BNB/USDC)").requiredOption("--amount <amount>", "Input amount (smallest unit)").option("--slippage <n>", "Slippage tolerance (0-100, e.g. 5 = 5%)", "5").option("--dex <dex>", "Specific DEX protocol (e.g. jupiter, kyberswap)").option("--recipient <addr>", "Recipient address (if different from sender)").option("--anti-mev", "Enable anti-MEV protection").option("--gas-price <n>", "Gas price (EVM, numeric string)").option("--gas-limit <n>", "Gas limit (EVM, numeric string)").option("--max-fee-per-gas <n>", "Max fee per gas (EVM EIP-1559)").option("--max-priority-fee-per-gas <n>", "Max priority fee per gas (EVM EIP-1559)").action(async (opts, cmd) => {
1154
1221
  try {
1155
1222
  validateChain(opts.chain);
1223
+ validateAddress(opts.from, opts.chain);
1156
1224
  const inputToken = resolveCurrency(opts.inputToken, opts.chain);
1157
1225
  const outputToken = resolveCurrency(opts.outputToken, opts.chain);
1158
1226
  const client = createClient();
1227
+ const body = {
1228
+ dex: opts.dex ?? "jupiter",
1229
+ userAddress: opts.from,
1230
+ inputMint: inputToken,
1231
+ outputMint: outputToken,
1232
+ amount: opts.amount,
1233
+ swapMode: "ExactIn",
1234
+ slippage: Number(opts.slippage)
1235
+ };
1236
+ if (opts.recipient) body.recipientAddress = opts.recipient;
1237
+ if (opts.antiMev) body.isAntiMev = true;
1238
+ if (opts.gasPrice) body.gasPrice = opts.gasPrice;
1239
+ if (opts.gasLimit) body.gasLimit = opts.gasLimit;
1240
+ if (opts.maxFeePerGas) body.maxFeePerGas = opts.maxFeePerGas;
1241
+ if (opts.maxPriorityFeePerGas) body.maxPriorityFeePerGas = opts.maxPriorityFeePerGas;
1159
1242
  const result = await callWithAutoPayment(
1160
- () => client.dex.quote(opts.chain, {
1161
- inputMint: inputToken,
1162
- outputMint: outputToken,
1163
- amount: opts.amount
1164
- })
1243
+ () => client.dex.route(opts.chain, body)
1165
1244
  );
1166
- printResult(result, opts.raw);
1245
+ printResult(result, cmd.optsWithGlobals().json);
1167
1246
  } catch (err) {
1168
1247
  exitOnError(err);
1169
1248
  }
1170
1249
  });
1171
- dex.command("swap").description("[FINANCIAL] Execute token swap (irreversible)").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--from <address>", "Sender wallet address").requiredOption("--input-token <addr>", "Input token (address or SOL/ETH/BNB/USDC)").requiredOption("--output-token <addr>", "Output token (address or SOL/ETH/BNB/USDC)").requiredOption("--amount <amount>", "Input amount (smallest unit)").option("--slippage <n>", "Slippage tolerance (e.g. 0.01 = 1%)", "0.01").option("--raw", "Single-line JSON output").option("--yes", "Skip confirmation prompt").action(async (opts) => {
1250
+ dex.command("swap").description("Build unsigned swap transaction").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--from <address>", "Sender wallet address").requiredOption("--input-token <addr>", "Input token (address or SOL/ETH/BNB/USDC)").requiredOption("--output-token <addr>", "Output token (address or SOL/ETH/BNB/USDC)").requiredOption("--amount <amount>", "Input amount (smallest unit)").option("--slippage <n>", "Slippage tolerance (0-100, e.g. 5 = 5%)", "5").option("--dex <dex>", "Specific DEX protocol").action(async (opts, cmd) => {
1172
1251
  try {
1173
1252
  validateChain(opts.chain);
1174
1253
  validateAddress(opts.from, opts.chain);
1175
- if (opts.slippage) validateSlippage(Number(opts.slippage));
1176
1254
  const inputToken = resolveCurrency(opts.inputToken, opts.chain);
1177
1255
  const outputToken = resolveCurrency(opts.outputToken, opts.chain);
1178
- const client = requireWallet();
1179
- process.stderr.write("Fetching quote...\n");
1180
- const quote = await callWithAutoPayment(
1181
- () => client.dex.quote(opts.chain, {
1182
- inputMint: inputToken,
1183
- outputMint: outputToken,
1184
- amount: opts.amount
1185
- })
1186
- );
1187
- process.stderr.write("\n--- Swap Summary ---\n");
1188
- process.stderr.write(`Chain: ${opts.chain}
1189
- `);
1190
- process.stderr.write(`Input: ${inputToken}
1191
- `);
1192
- process.stderr.write(`Output: ${outputToken}
1193
- `);
1194
- process.stderr.write(`Amount: ${opts.amount}
1195
- `);
1196
- process.stderr.write(`Slippage: ${opts.slippage}
1197
- `);
1198
- process.stderr.write(`Quote: ${JSON.stringify(quote)}
1199
- `);
1200
- process.stderr.write("--------------------\n\n");
1201
- if (!opts.yes) {
1202
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
1203
- const answer = await rl.question("Confirm swap? (y/N): ");
1204
- rl.close();
1205
- if (answer.toLowerCase() !== "y") {
1206
- process.stderr.write("Swap cancelled.\n");
1207
- return;
1208
- }
1209
- }
1210
- process.stderr.write("Building transaction...\n");
1211
- const swapResult = await callWithAutoPayment(
1256
+ const client = createClient();
1257
+ const result = await callWithAutoPayment(
1212
1258
  () => client.dex.swap(opts.chain, {
1259
+ dex: opts.dex ?? "jupiter",
1213
1260
  userAddress: opts.from,
1214
1261
  inputMint: inputToken,
1215
1262
  outputMint: outputToken,
1216
1263
  amount: opts.amount,
1264
+ swapMode: "ExactIn",
1217
1265
  slippage: Number(opts.slippage)
1218
1266
  })
1219
1267
  );
1220
- const { createWallet: createWallet2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
1221
- const { loadConfig: loadConfig2, getWalletMode: getWalletMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1222
- const config = loadConfig2();
1223
- const walletMode = getWalletMode2(config);
1224
- if (!walletMode) throw new Error("Wallet required for swap.");
1225
- const wallet = createWallet2(config, walletMode);
1226
- process.stderr.write("Signing transaction...\n");
1227
- const signedTx = await wallet.signTransaction(swapResult.serializedTx);
1228
- process.stderr.write("Broadcasting transaction...\n");
1229
- const sendResult = await client.transaction.send(opts.chain, {
1230
- signedTx
1231
- });
1232
- if (sendResult.jobId) {
1233
- process.stderr.write(`Job ${sendResult.jobId} submitted. Waiting for confirmation...
1234
- `);
1235
- const jobResult = await client.waitForJob(sendResult.jobId);
1236
- const result = {
1237
- ...jobResult,
1238
- explorer: explorerUrl(opts.chain, (sendResult.signature ?? jobResult.hash) || "")
1239
- };
1240
- printResult(result, opts.raw);
1241
- } else {
1242
- const result = {
1243
- ...sendResult,
1244
- explorer: explorerUrl(opts.chain, sendResult.signature ?? "")
1245
- };
1246
- printResult(result, opts.raw);
1247
- }
1268
+ printResult(result, cmd.optsWithGlobals().json);
1248
1269
  } catch (err) {
1249
1270
  exitOnError(err);
1250
1271
  }
1251
1272
  });
1252
- dex.command("create").description("[FINANCIAL] Create token on launchpad").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--name <name>", "Token name").requiredOption("--symbol <symbol>", "Token symbol").requiredOption("--uri <uri>", "Metadata URI (IPFS/HTTP)").option("--platform <platform>", "Launchpad: pumpfun/raydium").option("--raw", "Single-line JSON output").action(async (opts) => {
1273
+ dex.command("create").description("Build unsigned token creation transaction").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--from <address>", "Creator wallet address").requiredOption("--name <name>", "Token name").requiredOption("--symbol <symbol>", "Token symbol").option("--dex <dex>", "Launchpad: pumpfun/raydium", "pumpfun").option("--uri <uri>", "Metadata URI (IPFS/HTTP)").option("--image <url>", "Token image URL").action(async (opts, cmd) => {
1253
1274
  try {
1254
1275
  validateChain(opts.chain);
1255
- const client = requireWallet();
1276
+ validateAddress(opts.from, opts.chain);
1277
+ const client = createClient();
1256
1278
  const result = await callWithAutoPayment(
1257
1279
  () => client.dex.createToken(opts.chain, {
1280
+ dex: opts.dex,
1281
+ userAddress: opts.from,
1258
1282
  name: opts.name,
1259
1283
  symbol: opts.symbol,
1260
1284
  uri: opts.uri,
1261
- platform: opts.platform
1285
+ image: opts.image
1262
1286
  })
1263
1287
  );
1264
- printResult(result, opts.raw);
1288
+ printResult(result, cmd.optsWithGlobals().json);
1289
+ } catch (err) {
1290
+ exitOnError(err);
1291
+ }
1292
+ });
1293
+ }
1294
+
1295
+ // src/commands/transaction.ts
1296
+ var EVM_ONLY_CHAINS = /* @__PURE__ */ new Set(["eth", "bsc"]);
1297
+ function validateEvmChain(chain) {
1298
+ validateChain(chain);
1299
+ if (!EVM_ONLY_CHAINS.has(chain)) {
1300
+ throw new Error(`Chain "${chain}" is not supported for this operation. Only EVM chains: ${[...EVM_ONLY_CHAINS].join(", ")}`);
1301
+ }
1302
+ }
1303
+ function registerTransactionCommands(program2) {
1304
+ const tx = program2.command("tx").description("Transaction broadcast, gas estimation");
1305
+ tx.command("send").description("Broadcast a signed transaction").requiredOption("--chain <chain>", "Chain: sol/bsc/eth").requiredOption("--signed-tx <base64>", "Base64-encoded signed transaction").option("--submit-type <type>", "Submit type: default/priority", "default").option("--anti-mev", "Enable anti-MEV protection").action(async (opts, cmd) => {
1306
+ try {
1307
+ validateChain(opts.chain);
1308
+ const client = createClient();
1309
+ const body = {
1310
+ signedTx: opts.signedTx
1311
+ };
1312
+ if (opts.submitType && opts.submitType !== "default") body.submitType = opts.submitType;
1313
+ if (opts.antiMev) body.options = { isAntiMev: true };
1314
+ const result = await callWithAutoPayment(
1315
+ () => client.transaction.send(opts.chain, body)
1316
+ );
1317
+ printResult(result, cmd.optsWithGlobals().json);
1318
+ } catch (err) {
1319
+ exitOnError(err);
1320
+ }
1321
+ });
1322
+ tx.command("gas-price").description("Get current gas price (EVM only)").requiredOption("--chain <chain>", "Chain: eth/bsc").action(async (opts, cmd) => {
1323
+ try {
1324
+ validateEvmChain(opts.chain);
1325
+ const client = createClient();
1326
+ const result = await callWithAutoPayment(
1327
+ () => client.transaction.getGasPrice(opts.chain)
1328
+ );
1329
+ printResult(result, cmd.optsWithGlobals().json);
1330
+ } catch (err) {
1331
+ exitOnError(err);
1332
+ }
1333
+ });
1334
+ tx.command("estimate-gas").description("Estimate gas limit for a transaction (EVM only)").requiredOption("--chain <chain>", "Chain: eth/bsc").requiredOption("--from <address>", "From address").requiredOption("--to <address>", "To address").requiredOption("--data <hex>", "Transaction data (hex)").option("--value <hex>", "Value in wei (hex string)", "0x0").action(async (opts, cmd) => {
1335
+ try {
1336
+ validateEvmChain(opts.chain);
1337
+ const client = createClient();
1338
+ const body = {
1339
+ from: opts.from,
1340
+ to: opts.to,
1341
+ data: opts.data
1342
+ };
1343
+ if (opts.value && opts.value !== "0x0") body.value = opts.value;
1344
+ const result = await callWithAutoPayment(
1345
+ () => client.transaction.getGasLimit(opts.chain, body)
1346
+ );
1347
+ printResult(result, cmd.optsWithGlobals().json);
1265
1348
  } catch (err) {
1266
1349
  exitOnError(err);
1267
1350
  }
@@ -1271,8 +1354,9 @@ function registerDexCommands(program2) {
1271
1354
  // src/commands/job.ts
1272
1355
  function registerJobCommands(program2) {
1273
1356
  const job = program2.command("job").description("Job status polling");
1274
- job.command("status").description("Check job status").requiredOption("--id <jobId>", "Job ID").option("--wait", "Wait for job completion via SSE").option("--timeout <ms>", "Wait timeout in milliseconds", "60000").option("--raw", "Single-line JSON output").action(async (opts) => {
1357
+ job.command("status").description("Check job status").requiredOption("--id <jobId>", "Job ID").option("--wait", "Wait for job completion via SSE").option("--timeout <ms>", "Wait timeout in milliseconds", "60000").action(async (opts, cmd) => {
1275
1358
  try {
1359
+ const json = cmd.optsWithGlobals().json;
1276
1360
  const client = createClient();
1277
1361
  if (opts.wait) {
1278
1362
  process.stderr.write(`Waiting for job ${opts.id}...
@@ -1280,12 +1364,12 @@ function registerJobCommands(program2) {
1280
1364
  const result = await callWithAutoPayment(
1281
1365
  () => client.waitForJob(opts.id, Number(opts.timeout))
1282
1366
  );
1283
- printResult(result, opts.raw);
1367
+ printResult(result, json);
1284
1368
  } else {
1285
1369
  const result = await callWithAutoPayment(
1286
1370
  () => client.job.get(opts.id)
1287
1371
  );
1288
- printResult(result, opts.raw);
1372
+ printResult(result, json);
1289
1373
  }
1290
1374
  } catch (err) {
1291
1375
  exitOnError(err);
@@ -1354,7 +1438,7 @@ function saveKey(keyPair, profile = DEFAULT_PROFILE) {
1354
1438
  // src/commands/auth.ts
1355
1439
  init_turnkey();
1356
1440
  init_constants();
1357
- import * as readline4 from "readline/promises";
1441
+ import * as readline3 from "readline/promises";
1358
1442
  async function resolveAndStoreAddresses(turnkeyCreds) {
1359
1443
  process.stderr.write("Resolving wallet addresses...\n");
1360
1444
  const [evmAddress, solanaAddress] = await Promise.all([
@@ -1384,14 +1468,22 @@ function registerAuthCommands(program2) {
1384
1468
  exitOnError(err);
1385
1469
  }
1386
1470
  });
1387
- program2.command("logout").description("Clear session (P-256 keys preserved)").action(() => {
1471
+ program2.command("logout").description("Clear session (P-256 keys preserved)").action((_opts, cmd) => {
1472
+ const json = cmd.optsWithGlobals().json;
1388
1473
  const config = loadConfig();
1389
- if (config.turnkey) {
1474
+ const wasLoggedIn = !!config.turnkey;
1475
+ if (wasLoggedIn) {
1390
1476
  updateConfig({ turnkey: void 0 });
1391
- process.stdout.write("Logged out. P-256 keys preserved in ~/.config/chainstream/keys/\n");
1392
- process.stdout.write("Run 'chainstream login' to re-authenticate.\n");
1477
+ }
1478
+ if (json) {
1479
+ printResult({ success: true, wasLoggedIn }, true);
1393
1480
  } else {
1394
- process.stdout.write("Not logged in via Turnkey.\n");
1481
+ if (wasLoggedIn) {
1482
+ process.stdout.write("Logged out. P-256 keys preserved in ~/.config/chainstream/keys/\n");
1483
+ process.stdout.write("Run 'chainstream login' to re-authenticate.\n");
1484
+ } else {
1485
+ process.stdout.write("Not logged in via Turnkey.\n");
1486
+ }
1395
1487
  }
1396
1488
  });
1397
1489
  program2.command("bind-email").description("Bind an email to your wallet (for account recovery)").argument("[email]", "Email address to bind").action(async (emailArg) => {
@@ -1552,7 +1644,7 @@ async function doKeyLogin() {
1552
1644
  }
1553
1645
  async function doEmailLogin(email) {
1554
1646
  if (!email) {
1555
- const rl2 = readline4.createInterface({ input: process.stdin, output: process.stderr });
1647
+ const rl2 = readline3.createInterface({ input: process.stdin, output: process.stderr });
1556
1648
  email = await rl2.question("Enter your email: ");
1557
1649
  rl2.close();
1558
1650
  if (!email?.trim()) throw new Error("Email required.");
@@ -1568,7 +1660,7 @@ async function doEmailLogin(email) {
1568
1660
  `);
1569
1661
  return;
1570
1662
  }
1571
- const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
1663
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
1572
1664
  const code = await rl.question("Enter OTP code: ");
1573
1665
  rl.close();
1574
1666
  if (!code?.trim()) throw new Error("OTP code required.");
@@ -1617,7 +1709,7 @@ async function doBindEmail(email) {
1617
1709
  throw new Error("No wallet found. Run 'chainstream login' first.");
1618
1710
  }
1619
1711
  if (!email) {
1620
- const rl2 = readline4.createInterface({ input: process.stdin, output: process.stderr });
1712
+ const rl2 = readline3.createInterface({ input: process.stdin, output: process.stderr });
1621
1713
  email = await rl2.question("Enter email to bind: ");
1622
1714
  rl2.close();
1623
1715
  if (!email?.trim()) throw new Error("Email required.");
@@ -1633,7 +1725,7 @@ async function doBindEmail(email) {
1633
1725
  `);
1634
1726
  return;
1635
1727
  }
1636
- const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
1728
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
1637
1729
  const code = await rl.question("Enter verification code: ");
1638
1730
  rl.close();
1639
1731
  if (!code?.trim()) throw new Error("Verification code required.");
@@ -1654,70 +1746,60 @@ async function doBindEmail(email) {
1654
1746
  init_config();
1655
1747
  function registerConfigCommands(program2) {
1656
1748
  const config = program2.command("config").description("Configuration management");
1657
- config.command("set").description("Set a configuration value").requiredOption("--key <key>", "Config key (apiKey, baseUrl)").requiredOption("--value <value>", "Config value").action((opts) => {
1749
+ config.command("set").description("Set a configuration value").requiredOption("--key <key>", "Config key (apiKey, baseUrl)").requiredOption("--value <value>", "Config value").action((opts, cmd) => {
1658
1750
  try {
1751
+ const json = cmd.optsWithGlobals().json;
1659
1752
  const allowedKeys = ["apiKey", "baseUrl", "walletChain"];
1660
1753
  if (!allowedKeys.includes(opts.key)) {
1661
1754
  throw new Error(`Invalid key "${opts.key}". Allowed: ${allowedKeys.join(", ")}`);
1662
1755
  }
1663
1756
  updateConfig({ [opts.key]: opts.value });
1664
- process.stdout.write(`Set ${opts.key} = ${opts.key === "apiKey" ? "***" : opts.value}
1665
- `);
1757
+ printResult({ key: opts.key, value: opts.key === "apiKey" ? "(set)" : opts.value }, json);
1666
1758
  } catch (err) {
1667
1759
  exitOnError(err);
1668
1760
  }
1669
1761
  });
1670
- config.command("get").description("Show configuration").option("--key <key>", "Specific key to show").action((opts) => {
1762
+ config.command("get").description("Show configuration").option("--key <key>", "Specific key to show").action((opts, cmd) => {
1671
1763
  try {
1764
+ const json = cmd.optsWithGlobals().json;
1672
1765
  const cfg = loadConfig();
1673
1766
  if (opts.key) {
1674
1767
  const value = cfg[opts.key];
1675
- if (value === void 0) {
1676
- process.stdout.write(`${opts.key}: (not set)
1677
- `);
1678
- } else if (opts.key === "apiKey" && typeof value === "string") {
1679
- process.stdout.write(`${opts.key}: ${value.slice(0, 8)}...${value.slice(-4)}
1680
- `);
1681
- } else {
1682
- process.stdout.write(`${opts.key}: ${JSON.stringify(value)}
1683
- `);
1684
- }
1768
+ const displayValue = value === void 0 ? null : opts.key === "apiKey" && typeof value === "string" ? `${value.slice(0, 8)}...${value.slice(-4)}` : value;
1769
+ printResult({ key: opts.key, value: displayValue }, json);
1685
1770
  } else {
1686
1771
  const display = {
1687
- apiKey: cfg.apiKey ? `${cfg.apiKey.slice(0, 8)}...` : void 0,
1772
+ apiKey: cfg.apiKey ? `${cfg.apiKey.slice(0, 8)}...` : null,
1688
1773
  baseUrl: cfg.baseUrl,
1689
- walletMode: getWalletMode(cfg),
1690
- turnkey: cfg.turnkey ? { organizationId: cfg.turnkey.organizationId } : void 0,
1691
- rawWallet: cfg.rawWallet ? { chain: cfg.rawWallet.chain } : void 0
1774
+ walletMode: getWalletMode(cfg) ?? null,
1775
+ walletChain: cfg.walletChain ?? null
1692
1776
  };
1693
- process.stdout.write(JSON.stringify(display, null, 2) + "\n");
1777
+ printResult(display, json);
1694
1778
  }
1695
1779
  } catch (err) {
1696
1780
  exitOnError(err);
1697
1781
  }
1698
1782
  });
1699
- config.command("auth").description("Show current authentication status").action(() => {
1783
+ config.command("auth").description("Show current authentication status").action((_opts, cmd) => {
1700
1784
  try {
1785
+ const json = cmd.optsWithGlobals().json;
1701
1786
  const cfg = loadConfig();
1702
1787
  const mode = getWalletMode(cfg);
1788
+ const status = { mode: mode ?? "none" };
1703
1789
  if (mode === "turnkey") {
1704
- process.stdout.write(`Auth: Turnkey (org: ${cfg.turnkey.organizationId})
1705
- `);
1706
- const expiry = new Date(cfg.turnkey.sessionExpiry * 1e3);
1790
+ status.organizationId = cfg.turnkey.organizationId;
1791
+ status.evmAddress = cfg.turnkey.evmAddress ?? null;
1792
+ status.solanaAddress = cfg.turnkey.solanaAddress ?? null;
1707
1793
  const expired = Date.now() > cfg.turnkey.sessionExpiry * 1e3;
1708
- process.stdout.write(`Session: ${expired ? "expired" : "active"} (expires: ${expiry.toISOString()})
1709
- `);
1794
+ status.session = expired ? "expired" : "active";
1795
+ status.sessionExpiry = new Date(cfg.turnkey.sessionExpiry * 1e3).toISOString();
1710
1796
  } else if (mode === "raw") {
1711
- process.stdout.write(`Auth: Raw Key (${cfg.rawWallet.chain})
1712
- `);
1797
+ status.chain = cfg.rawWallet.chain;
1713
1798
  } else if (cfg.apiKey) {
1714
- process.stdout.write(`Auth: API Key (${cfg.apiKey.slice(0, 8)}...)
1715
- `);
1716
- } else {
1717
- process.stdout.write("Auth: Not configured\n");
1718
- process.stdout.write(" Run: chainstream login # Turnkey wallet\n");
1719
- process.stdout.write(" Run: chainstream config set --key apiKey --value <key> # API key\n");
1799
+ status.mode = "apiKey";
1800
+ status.apiKey = `${cfg.apiKey.slice(0, 8)}...`;
1720
1801
  }
1802
+ printResult(status, json);
1721
1803
  } catch (err) {
1722
1804
  exitOnError(err);
1723
1805
  }
@@ -1726,11 +1808,12 @@ function registerConfigCommands(program2) {
1726
1808
 
1727
1809
  // src/index.ts
1728
1810
  var program = new Command();
1729
- program.name("chainstream").version("0.1.0").description("ChainStream CLI \u2014 on-chain data and DeFi execution for AI agents");
1811
+ program.name("chainstream").version("0.0.19").description("ChainStream CLI \u2014 on-chain data and DeFi execution for AI agents").option("--json", "Output as single-line JSON (machine-readable)");
1730
1812
  registerTokenCommands(program);
1731
1813
  registerMarketCommands(program);
1732
1814
  registerWalletCommands(program);
1733
1815
  registerDexCommands(program);
1816
+ registerTransactionCommands(program);
1734
1817
  registerJobCommands(program);
1735
1818
  registerAuthCommands(program);
1736
1819
  registerConfigCommands(program);