@chainstream-io/cli 0.0.17 → 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.");
@@ -468,6 +535,7 @@ var x402_exports = {};
468
535
  __export(x402_exports, {
469
536
  getPricing: () => getPricing,
470
537
  getX402Fetch: () => getX402Fetch,
538
+ selectPaymentMethod: () => selectPaymentMethod,
471
539
  selectPlanInteractive: () => selectPlanInteractive
472
540
  });
473
541
  import { x402Client } from "@x402/core/client";
@@ -475,7 +543,7 @@ import { wrapFetchWithPayment } from "@x402/fetch";
475
543
  import { ExactEvmScheme } from "@x402/evm/exact/client";
476
544
  import { ExactSvmScheme } from "@x402/svm/exact/client";
477
545
  import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
478
- import { hashTypedData } from "viem";
546
+ import { hashTypedData as hashTypedData2 } from "viem";
479
547
  import { createKeyPairSignerFromBytes } from "@solana/kit";
480
548
  import * as readline from "readline/promises";
481
549
  function createTurnkeyPaymentAccount(creds) {
@@ -486,7 +554,7 @@ function createTurnkeyPaymentAccount(creds) {
486
554
  source: "custom",
487
555
  publicKey: "0x",
488
556
  async signTypedData(args) {
489
- const digest = hashTypedData(args);
557
+ const digest = hashTypedData2(args);
490
558
  const result = await turnkeyRequest(
491
559
  "/public/v1/submit/sign_raw_payload",
492
560
  {
@@ -608,6 +676,15 @@ async function getPricing(baseUrl) {
608
676
  if (!resp.ok) throw new Error(`Failed to get pricing (${resp.status})`);
609
677
  return resp.json();
610
678
  }
679
+ async function selectPaymentMethod() {
680
+ process.stderr.write("\n[chainstream] Choose payment method:\n");
681
+ process.stderr.write(" 1. x402 (USDC on Base/Solana)\n");
682
+ process.stderr.write(" 2. MPP Tempo (USDC.e on Tempo)\n\n");
683
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
684
+ const answer = await rl.question("Select method (1-2): ");
685
+ rl.close();
686
+ return answer.trim() === "2" ? "mpp" : "x402";
687
+ }
611
688
  async function selectPlanInteractive(baseUrl) {
612
689
  const pricing = await getPricing(baseUrl);
613
690
  const plans = pricing.plans.sort((a, b) => a.priceUsd - b.priceUsd);
@@ -677,29 +754,35 @@ function createClient() {
677
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"
678
755
  );
679
756
  }
680
- function requireWallet() {
681
- const config = loadConfig();
682
- const walletMode = getWalletMode(config);
683
- if (!walletMode) {
684
- throw new Error(
685
- "Wallet required for this operation. Run:\n chainstream login # Create ChainStream Wallet\n chainstream wallet set-raw # Dev: use raw private key"
686
- );
687
- }
688
- return createClient();
689
- }
690
757
  async function callWithAutoPayment(fn, retried = false) {
691
758
  try {
692
759
  return await fn();
693
760
  } catch (err) {
694
761
  const paymentInfo = extractPaymentInfo(err);
695
- if (paymentInfo && !retried && _wallet) {
762
+ if (paymentInfo && !retried) {
696
763
  const config = loadConfig();
697
764
  const chosenPlan = await selectPlanInteractive(config.baseUrl);
765
+ const method = _wallet ? await selectPaymentMethod() : "mpp";
766
+ if (method === "mpp") {
767
+ const mppUrl = `${config.baseUrl}/mpp/purchase?plan=${encodeURIComponent(chosenPlan)}`;
768
+ process.stderr.write(
769
+ `
770
+ [chainstream] To purchase via MPP (USDC.e on Tempo), run:
771
+ tempo request ${mppUrl}
772
+
773
+ After purchase, set the API key:
774
+ chainstream config set --key apiKey --value <key from response>
775
+
776
+ `
777
+ );
778
+ throw err;
779
+ }
780
+ if (!_wallet) throw err;
698
781
  const purchaseUrl = replacePlanInUrl(
699
782
  resolvePurchaseUrl(config.baseUrl, paymentInfo),
700
783
  chosenPlan
701
784
  );
702
- process.stderr.write(`[chainstream] Purchasing ${chosenPlan} plan...
785
+ process.stderr.write(`[chainstream] Purchasing ${chosenPlan} plan via x402...
703
786
  `);
704
787
  const x402Fetch = await getX402Fetch(config);
705
788
  const resp = await x402Fetch(purchaseUrl);
@@ -791,11 +874,6 @@ function validateAddress(address, chain) {
791
874
  throw new Error(`Invalid address for chain "${chain}": "${address}"`);
792
875
  }
793
876
  }
794
- function validateSlippage(value) {
795
- if (value < 1e-3 || value > 0.5) {
796
- throw new Error(`Invalid slippage: ${value}. Must be between 0.001 (0.1%) and 0.5 (50%).`);
797
- }
798
- }
799
877
  function resolveCurrency(nameOrAddress, chain) {
800
878
  const upper = nameOrAddress.toUpperCase();
801
879
  const resolved = CURRENCY_MAP[chain]?.[upper];
@@ -804,13 +882,8 @@ function resolveCurrency(nameOrAddress, chain) {
804
882
  }
805
883
 
806
884
  // src/lib/output.ts
807
- var EXPLORERS = {
808
- sol: "https://solscan.io/tx/",
809
- bsc: "https://bscscan.com/tx/",
810
- eth: "https://etherscan.io/tx/"
811
- };
812
- function printResult(data, raw) {
813
- if (raw) {
885
+ function printResult(data, json) {
886
+ if (json) {
814
887
  process.stdout.write(JSON.stringify(data) + "\n");
815
888
  } else {
816
889
  process.stdout.write(JSON.stringify(data, null, 2) + "\n");
@@ -828,27 +901,23 @@ function exitOnError(err) {
828
901
  }
829
902
  process.exit(1);
830
903
  }
831
- function explorerUrl(chain, txHash) {
832
- const base = EXPLORERS[chain] ?? EXPLORERS["eth"];
833
- return `${base}${txHash}`;
834
- }
835
904
 
836
905
  // src/commands/token.ts
837
906
  function registerTokenCommands(program2) {
838
907
  const token = program2.command("token").description("Token information and analytics");
839
- 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) => {
840
909
  try {
841
910
  validateChain(opts.chain);
842
911
  const client = createClient();
843
912
  const result = await callWithAutoPayment(
844
913
  () => client.token.search({ q: opts.keyword, chains: [opts.chain], limit: Number(opts.limit) })
845
914
  );
846
- printResult(result, opts.raw);
915
+ printResult(result, cmd.optsWithGlobals().json);
847
916
  } catch (err) {
848
917
  exitOnError(err);
849
918
  }
850
919
  });
851
- 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) => {
852
921
  try {
853
922
  validateChain(opts.chain);
854
923
  validateAddress(opts.address, opts.chain);
@@ -856,12 +925,12 @@ function registerTokenCommands(program2) {
856
925
  const result = await callWithAutoPayment(
857
926
  () => client.token.getToken(opts.chain, opts.address)
858
927
  );
859
- printResult(result, opts.raw);
928
+ printResult(result, cmd.optsWithGlobals().json);
860
929
  } catch (err) {
861
930
  exitOnError(err);
862
931
  }
863
932
  });
864
- 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) => {
865
934
  try {
866
935
  validateChain(opts.chain);
867
936
  validateAddress(opts.address, opts.chain);
@@ -869,12 +938,12 @@ function registerTokenCommands(program2) {
869
938
  const result = await callWithAutoPayment(
870
939
  () => client.token.getSecurity(opts.chain, opts.address)
871
940
  );
872
- printResult(result, opts.raw);
941
+ printResult(result, cmd.optsWithGlobals().json);
873
942
  } catch (err) {
874
943
  exitOnError(err);
875
944
  }
876
945
  });
877
- 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) => {
878
947
  try {
879
948
  validateChain(opts.chain);
880
949
  validateAddress(opts.address, opts.chain);
@@ -882,12 +951,12 @@ function registerTokenCommands(program2) {
882
951
  const result = await callWithAutoPayment(
883
952
  () => client.token.getTopHolders(opts.chain, opts.address, { limit: Number(opts.limit) })
884
953
  );
885
- printResult(result, opts.raw);
954
+ printResult(result, cmd.optsWithGlobals().json);
886
955
  } catch (err) {
887
956
  exitOnError(err);
888
957
  }
889
958
  });
890
- 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) => {
891
960
  try {
892
961
  validateChain(opts.chain);
893
962
  validateAddress(opts.address, opts.chain);
@@ -901,12 +970,12 @@ function registerTokenCommands(program2) {
901
970
  const result = await callWithAutoPayment(
902
971
  () => client.token.getCandles(opts.chain, opts.address, params)
903
972
  );
904
- printResult(result, opts.raw);
973
+ printResult(result, cmd.optsWithGlobals().json);
905
974
  } catch (err) {
906
975
  exitOnError(err);
907
976
  }
908
977
  });
909
- 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) => {
910
979
  try {
911
980
  validateChain(opts.chain);
912
981
  validateAddress(opts.address, opts.chain);
@@ -914,7 +983,7 @@ function registerTokenCommands(program2) {
914
983
  const result = await callWithAutoPayment(
915
984
  () => client.token.getPools(opts.chain, opts.address)
916
985
  );
917
- printResult(result, opts.raw);
986
+ printResult(result, cmd.optsWithGlobals().json);
918
987
  } catch (err) {
919
988
  exitOnError(err);
920
989
  }
@@ -924,7 +993,7 @@ function registerTokenCommands(program2) {
924
993
  // src/commands/market.ts
925
994
  function registerMarketCommands(program2) {
926
995
  const market = program2.command("market").description("Market data and trending tokens");
927
- 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) => {
928
997
  try {
929
998
  validateChain(opts.chain);
930
999
  const client = createClient();
@@ -933,12 +1002,12 @@ function registerMarketCommands(program2) {
933
1002
  const result = await callWithAutoPayment(
934
1003
  () => client.ranking.getHotTokens(opts.chain, opts.duration, params)
935
1004
  );
936
- printResult(result, opts.raw);
1005
+ printResult(result, cmd.optsWithGlobals().json);
937
1006
  } catch (err) {
938
1007
  exitOnError(err);
939
1008
  }
940
1009
  });
941
- 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) => {
942
1011
  try {
943
1012
  validateChain(opts.chain);
944
1013
  const client = createClient();
@@ -947,12 +1016,12 @@ function registerMarketCommands(program2) {
947
1016
  const result = await callWithAutoPayment(
948
1017
  () => client.ranking.getNewTokens(opts.chain, params)
949
1018
  );
950
- printResult(result, opts.raw);
1019
+ printResult(result, cmd.optsWithGlobals().json);
951
1020
  } catch (err) {
952
1021
  exitOnError(err);
953
1022
  }
954
1023
  });
955
- 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) => {
956
1025
  try {
957
1026
  validateChain(opts.chain);
958
1027
  const client = createClient();
@@ -962,7 +1031,7 @@ function registerMarketCommands(program2) {
962
1031
  const result = await callWithAutoPayment(
963
1032
  () => client.trade.getTrades(opts.chain, params)
964
1033
  );
965
- printResult(result, opts.raw);
1034
+ printResult(result, cmd.optsWithGlobals().json);
966
1035
  } catch (err) {
967
1036
  exitOnError(err);
968
1037
  }
@@ -973,8 +1042,25 @@ function registerMarketCommands(program2) {
973
1042
  init_config();
974
1043
  import * as readline2 from "readline/promises";
975
1044
  function registerWalletCommands(program2) {
976
- const wallet = program2.command("wallet").description("Wallet analytics and management");
977
- 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) => {
978
1064
  try {
979
1065
  validateChain(opts.chain);
980
1066
  validateAddress(opts.address, opts.chain);
@@ -986,12 +1072,12 @@ function registerWalletCommands(program2) {
986
1072
  client.wallet.getTokensBalance(opts.chain, opts.address)
987
1073
  ])
988
1074
  );
989
- printResult({ pnl, netWorth, balance }, opts.raw);
1075
+ printResult({ pnl, netWorth, balance }, cmd.optsWithGlobals().json);
990
1076
  } catch (err) {
991
1077
  exitOnError(err);
992
1078
  }
993
1079
  });
994
- 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) => {
995
1081
  try {
996
1082
  validateChain(opts.chain);
997
1083
  validateAddress(opts.address, opts.chain);
@@ -999,12 +1085,12 @@ function registerWalletCommands(program2) {
999
1085
  const result = await callWithAutoPayment(
1000
1086
  () => client.wallet.getPnl(opts.chain, opts.address)
1001
1087
  );
1002
- printResult(result, opts.raw);
1088
+ printResult(result, cmd.optsWithGlobals().json);
1003
1089
  } catch (err) {
1004
1090
  exitOnError(err);
1005
1091
  }
1006
1092
  });
1007
- 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) => {
1008
1094
  try {
1009
1095
  validateChain(opts.chain);
1010
1096
  validateAddress(opts.address, opts.chain);
@@ -1014,12 +1100,12 @@ function registerWalletCommands(program2) {
1014
1100
  const result = await callWithAutoPayment(
1015
1101
  () => client.wallet.getTokensBalance(opts.chain, opts.address, params)
1016
1102
  );
1017
- printResult(result, opts.raw);
1103
+ printResult(result, cmd.optsWithGlobals().json);
1018
1104
  } catch (err) {
1019
1105
  exitOnError(err);
1020
1106
  }
1021
1107
  });
1022
- 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) => {
1023
1109
  try {
1024
1110
  validateChain(opts.chain);
1025
1111
  validateAddress(opts.address, opts.chain);
@@ -1029,42 +1115,46 @@ function registerWalletCommands(program2) {
1029
1115
  const result = await callWithAutoPayment(
1030
1116
  () => client.wallet.getWalletTransfers(opts.chain, opts.address, params)
1031
1117
  );
1032
- printResult(result, opts.raw);
1118
+ printResult(result, cmd.optsWithGlobals().json);
1033
1119
  } catch (err) {
1034
1120
  exitOnError(err);
1035
1121
  }
1036
1122
  });
1037
- wallet.command("address").description("Show current wallet addresses").action(async () => {
1123
+ wallet.command("address").description("Show current wallet addresses").action(async (_opts, cmd) => {
1038
1124
  try {
1125
+ const json = cmd.optsWithGlobals().json;
1039
1126
  const config = loadConfig();
1040
1127
  const walletMode = (await Promise.resolve().then(() => (init_config(), config_exports))).getWalletMode(config);
1128
+ const addresses = {};
1041
1129
  if (walletMode === "turnkey" && config.turnkey) {
1042
1130
  if (config.turnkey.evmAddress || config.turnkey.solanaAddress) {
1043
- if (config.turnkey.evmAddress) process.stdout.write(`Base: ${config.turnkey.evmAddress}
1044
- `);
1045
- if (config.turnkey.solanaAddress) process.stdout.write(`Solana: ${config.turnkey.solanaAddress}
1046
- `);
1131
+ if (config.turnkey.evmAddress) addresses.base = config.turnkey.evmAddress;
1132
+ if (config.turnkey.solanaAddress) addresses.solana = config.turnkey.solanaAddress;
1047
1133
  } else {
1048
1134
  const { createWalletWithAddresses: createWalletWithAddresses2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
1049
1135
  const wallets = await createWalletWithAddresses2(config, "turnkey");
1050
- if (wallets.base) process.stdout.write(`Base: ${wallets.base.address}
1051
- `);
1052
- if (wallets.sol) process.stdout.write(`Solana: ${wallets.sol.address}
1053
- `);
1136
+ if (wallets.base) addresses.base = wallets.base.address;
1137
+ if (wallets.sol) addresses.solana = wallets.sol.address;
1054
1138
  }
1055
1139
  } else if (config.rawWallet) {
1056
1140
  const { createWallet: createWallet2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
1057
1141
  const w = createWallet2(config, "raw");
1058
- process.stdout.write(`${w.paymentChain.toUpperCase()}: ${w.address}
1059
- `);
1060
- } else {
1061
- 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;
1062
1151
  }
1152
+ printResult(addresses, json);
1063
1153
  } catch (err) {
1064
1154
  exitOnError(err);
1065
1155
  }
1066
1156
  });
1067
- 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) => {
1068
1158
  try {
1069
1159
  validateChain(opts.chain);
1070
1160
  const config = loadConfig();
@@ -1086,34 +1176,38 @@ function registerWalletCommands(program2) {
1086
1176
  const result = await callWithAutoPayment(
1087
1177
  () => client.wallet.getTokensBalance(opts.chain, address)
1088
1178
  );
1089
- printResult(result, opts.raw);
1179
+ printResult(result, cmd.optsWithGlobals().json);
1090
1180
  } catch (err) {
1091
1181
  exitOnError(err);
1092
1182
  }
1093
1183
  });
1094
- 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) => {
1095
1185
  try {
1186
+ const json = cmd.optsWithGlobals().json;
1096
1187
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
1097
- process.stdout.write("\u26A0 WARNING: Raw private key will be stored in plaintext.\n");
1098
- process.stdout.write(" Use Turnkey (chainstream login) for production.\n\n");
1099
- 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: ");
1100
1193
  rl.close();
1101
1194
  if (!key.trim()) throw new Error("Empty key provided.");
1102
1195
  updateConfig({
1103
1196
  rawWallet: { key: key.trim(), chain: opts.chain }
1104
1197
  });
1105
- process.stdout.write(`Raw wallet set (${opts.chain}). Run 'chainstream wallet address' to verify.
1106
- `);
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);
1107
1201
  } catch (err) {
1108
1202
  exitOnError(err);
1109
1203
  }
1110
1204
  });
1111
- 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) => {
1112
1206
  try {
1113
1207
  const config = loadConfig();
1114
1208
  const { getPricing: getPricing2 } = await Promise.resolve().then(() => (init_x402(), x402_exports));
1115
1209
  const plans = await getPricing2(config.baseUrl);
1116
- printResult(plans, opts.raw);
1210
+ printResult(plans, cmd.optsWithGlobals().json);
1117
1211
  } catch (err) {
1118
1212
  exitOnError(err);
1119
1213
  }
@@ -1121,121 +1215,136 @@ function registerWalletCommands(program2) {
1121
1215
  }
1122
1216
 
1123
1217
  // src/commands/dex.ts
1124
- import * as readline3 from "readline/promises";
1125
1218
  function registerDexCommands(program2) {
1126
- const dex = program2.command("dex").description("DEX swap, quote, and token creation");
1127
- 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) => {
1128
1221
  try {
1129
1222
  validateChain(opts.chain);
1223
+ validateAddress(opts.from, opts.chain);
1130
1224
  const inputToken = resolveCurrency(opts.inputToken, opts.chain);
1131
1225
  const outputToken = resolveCurrency(opts.outputToken, opts.chain);
1132
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;
1133
1242
  const result = await callWithAutoPayment(
1134
- () => client.dex.quote(opts.chain, {
1135
- inputMint: inputToken,
1136
- outputMint: outputToken,
1137
- amount: opts.amount
1138
- })
1243
+ () => client.dex.route(opts.chain, body)
1139
1244
  );
1140
- printResult(result, opts.raw);
1245
+ printResult(result, cmd.optsWithGlobals().json);
1141
1246
  } catch (err) {
1142
1247
  exitOnError(err);
1143
1248
  }
1144
1249
  });
1145
- 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) => {
1146
1251
  try {
1147
1252
  validateChain(opts.chain);
1148
1253
  validateAddress(opts.from, opts.chain);
1149
- if (opts.slippage) validateSlippage(Number(opts.slippage));
1150
1254
  const inputToken = resolveCurrency(opts.inputToken, opts.chain);
1151
1255
  const outputToken = resolveCurrency(opts.outputToken, opts.chain);
1152
- const client = requireWallet();
1153
- process.stderr.write("Fetching quote...\n");
1154
- const quote = await callWithAutoPayment(
1155
- () => client.dex.quote(opts.chain, {
1156
- inputMint: inputToken,
1157
- outputMint: outputToken,
1158
- amount: opts.amount
1159
- })
1160
- );
1161
- process.stderr.write("\n--- Swap Summary ---\n");
1162
- process.stderr.write(`Chain: ${opts.chain}
1163
- `);
1164
- process.stderr.write(`Input: ${inputToken}
1165
- `);
1166
- process.stderr.write(`Output: ${outputToken}
1167
- `);
1168
- process.stderr.write(`Amount: ${opts.amount}
1169
- `);
1170
- process.stderr.write(`Slippage: ${opts.slippage}
1171
- `);
1172
- process.stderr.write(`Quote: ${JSON.stringify(quote)}
1173
- `);
1174
- process.stderr.write("--------------------\n\n");
1175
- if (!opts.yes) {
1176
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
1177
- const answer = await rl.question("Confirm swap? (y/N): ");
1178
- rl.close();
1179
- if (answer.toLowerCase() !== "y") {
1180
- process.stderr.write("Swap cancelled.\n");
1181
- return;
1182
- }
1183
- }
1184
- process.stderr.write("Building transaction...\n");
1185
- const swapResult = await callWithAutoPayment(
1256
+ const client = createClient();
1257
+ const result = await callWithAutoPayment(
1186
1258
  () => client.dex.swap(opts.chain, {
1259
+ dex: opts.dex ?? "jupiter",
1187
1260
  userAddress: opts.from,
1188
1261
  inputMint: inputToken,
1189
1262
  outputMint: outputToken,
1190
1263
  amount: opts.amount,
1264
+ swapMode: "ExactIn",
1191
1265
  slippage: Number(opts.slippage)
1192
1266
  })
1193
1267
  );
1194
- const { createWallet: createWallet2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
1195
- const { loadConfig: loadConfig2, getWalletMode: getWalletMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1196
- const config = loadConfig2();
1197
- const walletMode = getWalletMode2(config);
1198
- if (!walletMode) throw new Error("Wallet required for swap.");
1199
- const wallet = createWallet2(config, walletMode);
1200
- process.stderr.write("Signing transaction...\n");
1201
- const signedTx = await wallet.signTransaction(swapResult.serializedTx);
1202
- process.stderr.write("Broadcasting transaction...\n");
1203
- const sendResult = await client.transaction.send(opts.chain, {
1204
- signedTx
1205
- });
1206
- if (sendResult.jobId) {
1207
- process.stderr.write(`Job ${sendResult.jobId} submitted. Waiting for confirmation...
1208
- `);
1209
- const jobResult = await client.waitForJob(sendResult.jobId);
1210
- const result = {
1211
- ...jobResult,
1212
- explorer: explorerUrl(opts.chain, (sendResult.signature ?? jobResult.hash) || "")
1213
- };
1214
- printResult(result, opts.raw);
1215
- } else {
1216
- const result = {
1217
- ...sendResult,
1218
- explorer: explorerUrl(opts.chain, sendResult.signature ?? "")
1219
- };
1220
- printResult(result, opts.raw);
1221
- }
1268
+ printResult(result, cmd.optsWithGlobals().json);
1222
1269
  } catch (err) {
1223
1270
  exitOnError(err);
1224
1271
  }
1225
1272
  });
1226
- 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) => {
1227
1274
  try {
1228
1275
  validateChain(opts.chain);
1229
- const client = requireWallet();
1276
+ validateAddress(opts.from, opts.chain);
1277
+ const client = createClient();
1230
1278
  const result = await callWithAutoPayment(
1231
1279
  () => client.dex.createToken(opts.chain, {
1280
+ dex: opts.dex,
1281
+ userAddress: opts.from,
1232
1282
  name: opts.name,
1233
1283
  symbol: opts.symbol,
1234
1284
  uri: opts.uri,
1235
- platform: opts.platform
1285
+ image: opts.image
1236
1286
  })
1237
1287
  );
1238
- 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);
1239
1348
  } catch (err) {
1240
1349
  exitOnError(err);
1241
1350
  }
@@ -1245,8 +1354,9 @@ function registerDexCommands(program2) {
1245
1354
  // src/commands/job.ts
1246
1355
  function registerJobCommands(program2) {
1247
1356
  const job = program2.command("job").description("Job status polling");
1248
- 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) => {
1249
1358
  try {
1359
+ const json = cmd.optsWithGlobals().json;
1250
1360
  const client = createClient();
1251
1361
  if (opts.wait) {
1252
1362
  process.stderr.write(`Waiting for job ${opts.id}...
@@ -1254,12 +1364,12 @@ function registerJobCommands(program2) {
1254
1364
  const result = await callWithAutoPayment(
1255
1365
  () => client.waitForJob(opts.id, Number(opts.timeout))
1256
1366
  );
1257
- printResult(result, opts.raw);
1367
+ printResult(result, json);
1258
1368
  } else {
1259
1369
  const result = await callWithAutoPayment(
1260
1370
  () => client.job.get(opts.id)
1261
1371
  );
1262
- printResult(result, opts.raw);
1372
+ printResult(result, json);
1263
1373
  }
1264
1374
  } catch (err) {
1265
1375
  exitOnError(err);
@@ -1328,7 +1438,7 @@ function saveKey(keyPair, profile = DEFAULT_PROFILE) {
1328
1438
  // src/commands/auth.ts
1329
1439
  init_turnkey();
1330
1440
  init_constants();
1331
- import * as readline4 from "readline/promises";
1441
+ import * as readline3 from "readline/promises";
1332
1442
  async function resolveAndStoreAddresses(turnkeyCreds) {
1333
1443
  process.stderr.write("Resolving wallet addresses...\n");
1334
1444
  const [evmAddress, solanaAddress] = await Promise.all([
@@ -1358,14 +1468,22 @@ function registerAuthCommands(program2) {
1358
1468
  exitOnError(err);
1359
1469
  }
1360
1470
  });
1361
- 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;
1362
1473
  const config = loadConfig();
1363
- if (config.turnkey) {
1474
+ const wasLoggedIn = !!config.turnkey;
1475
+ if (wasLoggedIn) {
1364
1476
  updateConfig({ turnkey: void 0 });
1365
- process.stdout.write("Logged out. P-256 keys preserved in ~/.config/chainstream/keys/\n");
1366
- process.stdout.write("Run 'chainstream login' to re-authenticate.\n");
1477
+ }
1478
+ if (json) {
1479
+ printResult({ success: true, wasLoggedIn }, true);
1367
1480
  } else {
1368
- 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
+ }
1369
1487
  }
1370
1488
  });
1371
1489
  program2.command("bind-email").description("Bind an email to your wallet (for account recovery)").argument("[email]", "Email address to bind").action(async (emailArg) => {
@@ -1526,7 +1644,7 @@ async function doKeyLogin() {
1526
1644
  }
1527
1645
  async function doEmailLogin(email) {
1528
1646
  if (!email) {
1529
- const rl2 = readline4.createInterface({ input: process.stdin, output: process.stderr });
1647
+ const rl2 = readline3.createInterface({ input: process.stdin, output: process.stderr });
1530
1648
  email = await rl2.question("Enter your email: ");
1531
1649
  rl2.close();
1532
1650
  if (!email?.trim()) throw new Error("Email required.");
@@ -1542,7 +1660,7 @@ async function doEmailLogin(email) {
1542
1660
  `);
1543
1661
  return;
1544
1662
  }
1545
- const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
1663
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
1546
1664
  const code = await rl.question("Enter OTP code: ");
1547
1665
  rl.close();
1548
1666
  if (!code?.trim()) throw new Error("OTP code required.");
@@ -1591,7 +1709,7 @@ async function doBindEmail(email) {
1591
1709
  throw new Error("No wallet found. Run 'chainstream login' first.");
1592
1710
  }
1593
1711
  if (!email) {
1594
- const rl2 = readline4.createInterface({ input: process.stdin, output: process.stderr });
1712
+ const rl2 = readline3.createInterface({ input: process.stdin, output: process.stderr });
1595
1713
  email = await rl2.question("Enter email to bind: ");
1596
1714
  rl2.close();
1597
1715
  if (!email?.trim()) throw new Error("Email required.");
@@ -1607,7 +1725,7 @@ async function doBindEmail(email) {
1607
1725
  `);
1608
1726
  return;
1609
1727
  }
1610
- const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
1728
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
1611
1729
  const code = await rl.question("Enter verification code: ");
1612
1730
  rl.close();
1613
1731
  if (!code?.trim()) throw new Error("Verification code required.");
@@ -1628,70 +1746,60 @@ async function doBindEmail(email) {
1628
1746
  init_config();
1629
1747
  function registerConfigCommands(program2) {
1630
1748
  const config = program2.command("config").description("Configuration management");
1631
- 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) => {
1632
1750
  try {
1751
+ const json = cmd.optsWithGlobals().json;
1633
1752
  const allowedKeys = ["apiKey", "baseUrl", "walletChain"];
1634
1753
  if (!allowedKeys.includes(opts.key)) {
1635
1754
  throw new Error(`Invalid key "${opts.key}". Allowed: ${allowedKeys.join(", ")}`);
1636
1755
  }
1637
1756
  updateConfig({ [opts.key]: opts.value });
1638
- process.stdout.write(`Set ${opts.key} = ${opts.key === "apiKey" ? "***" : opts.value}
1639
- `);
1757
+ printResult({ key: opts.key, value: opts.key === "apiKey" ? "(set)" : opts.value }, json);
1640
1758
  } catch (err) {
1641
1759
  exitOnError(err);
1642
1760
  }
1643
1761
  });
1644
- 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) => {
1645
1763
  try {
1764
+ const json = cmd.optsWithGlobals().json;
1646
1765
  const cfg = loadConfig();
1647
1766
  if (opts.key) {
1648
1767
  const value = cfg[opts.key];
1649
- if (value === void 0) {
1650
- process.stdout.write(`${opts.key}: (not set)
1651
- `);
1652
- } else if (opts.key === "apiKey" && typeof value === "string") {
1653
- process.stdout.write(`${opts.key}: ${value.slice(0, 8)}...${value.slice(-4)}
1654
- `);
1655
- } else {
1656
- process.stdout.write(`${opts.key}: ${JSON.stringify(value)}
1657
- `);
1658
- }
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);
1659
1770
  } else {
1660
1771
  const display = {
1661
- apiKey: cfg.apiKey ? `${cfg.apiKey.slice(0, 8)}...` : void 0,
1772
+ apiKey: cfg.apiKey ? `${cfg.apiKey.slice(0, 8)}...` : null,
1662
1773
  baseUrl: cfg.baseUrl,
1663
- walletMode: getWalletMode(cfg),
1664
- turnkey: cfg.turnkey ? { organizationId: cfg.turnkey.organizationId } : void 0,
1665
- rawWallet: cfg.rawWallet ? { chain: cfg.rawWallet.chain } : void 0
1774
+ walletMode: getWalletMode(cfg) ?? null,
1775
+ walletChain: cfg.walletChain ?? null
1666
1776
  };
1667
- process.stdout.write(JSON.stringify(display, null, 2) + "\n");
1777
+ printResult(display, json);
1668
1778
  }
1669
1779
  } catch (err) {
1670
1780
  exitOnError(err);
1671
1781
  }
1672
1782
  });
1673
- config.command("auth").description("Show current authentication status").action(() => {
1783
+ config.command("auth").description("Show current authentication status").action((_opts, cmd) => {
1674
1784
  try {
1785
+ const json = cmd.optsWithGlobals().json;
1675
1786
  const cfg = loadConfig();
1676
1787
  const mode = getWalletMode(cfg);
1788
+ const status = { mode: mode ?? "none" };
1677
1789
  if (mode === "turnkey") {
1678
- process.stdout.write(`Auth: Turnkey (org: ${cfg.turnkey.organizationId})
1679
- `);
1680
- 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;
1681
1793
  const expired = Date.now() > cfg.turnkey.sessionExpiry * 1e3;
1682
- process.stdout.write(`Session: ${expired ? "expired" : "active"} (expires: ${expiry.toISOString()})
1683
- `);
1794
+ status.session = expired ? "expired" : "active";
1795
+ status.sessionExpiry = new Date(cfg.turnkey.sessionExpiry * 1e3).toISOString();
1684
1796
  } else if (mode === "raw") {
1685
- process.stdout.write(`Auth: Raw Key (${cfg.rawWallet.chain})
1686
- `);
1797
+ status.chain = cfg.rawWallet.chain;
1687
1798
  } else if (cfg.apiKey) {
1688
- process.stdout.write(`Auth: API Key (${cfg.apiKey.slice(0, 8)}...)
1689
- `);
1690
- } else {
1691
- process.stdout.write("Auth: Not configured\n");
1692
- process.stdout.write(" Run: chainstream login # Turnkey wallet\n");
1693
- 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)}...`;
1694
1801
  }
1802
+ printResult(status, json);
1695
1803
  } catch (err) {
1696
1804
  exitOnError(err);
1697
1805
  }
@@ -1700,11 +1808,12 @@ function registerConfigCommands(program2) {
1700
1808
 
1701
1809
  // src/index.ts
1702
1810
  var program = new Command();
1703
- 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)");
1704
1812
  registerTokenCommands(program);
1705
1813
  registerMarketCommands(program);
1706
1814
  registerWalletCommands(program);
1707
1815
  registerDexCommands(program);
1816
+ registerTransactionCommands(program);
1708
1817
  registerJobCommands(program);
1709
1818
  registerAuthCommands(program);
1710
1819
  registerConfigCommands(program);