moltlaunch 0.11.0 → 1.0.1

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
@@ -14,10 +14,13 @@ import { readFile, writeFile, mkdir, chmod, access } from "fs/promises";
14
14
  import { join } from "path";
15
15
  import { homedir } from "os";
16
16
 
17
- // src/lib/config.ts
17
+ // packages/shared/src/constants.ts
18
18
  var REVENUE_MANAGER_ADDRESS = "0x3Bc08524d9DaaDEC9d1Af87818d809611F0fD669";
19
+ var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
19
20
  var FLAUNCH_API_BASE = "https://web2-api.flaunch.gg";
20
21
  var FLAUNCH_DATA_API_BASE = "https://api.flayerlabs.xyz";
22
+ var FLAUNCH_DATA_API = `${FLAUNCH_DATA_API_BASE}/v1/base`;
23
+ var WORKER_API_URL = "https://moltlaunch-network.nikshepsvn-d85.workers.dev";
21
24
  var CHAIN = {
22
25
  mainnet: {
23
26
  id: 8453,
@@ -36,16 +39,16 @@ var CHAIN = {
36
39
  flaunchUrl: "https://flaunch.gg/base-sepolia"
37
40
  }
38
41
  };
39
- var WALLET_DIR = ".moltlaunch";
40
- var WALLET_FILE = "wallet.json";
41
- var LAUNCHES_FILE = "launches.json";
42
42
  var DEFAULT_SLIPPAGE_PERCENT = 5;
43
- var HEARTBEAT_ADDRESS = "0x0000000000000000000000000000000000000000";
44
- var BEAT_FEE = "0.0001";
45
43
  var MAX_IMAGE_SIZE_BYTES = 5 * 1024 * 1024;
46
44
  var POLL_INTERVAL_MS = 2e3;
47
45
  var POLL_TIMEOUT_MS = 12e4;
48
46
 
47
+ // src/lib/config.ts
48
+ var WALLET_DIR = ".moltlaunch";
49
+ var WALLET_FILE = "wallet.json";
50
+ var LAUNCHES_FILE = "launches.json";
51
+
49
52
  // src/lib/wallet.ts
50
53
  function getWalletDir() {
51
54
  return join(homedir(), WALLET_DIR);
@@ -263,6 +266,39 @@ async function fetchTokensByOwner(ownerAddress, network2) {
263
266
  }
264
267
  return await response.json();
265
268
  }
269
+ async function fetchTokenDetails(tokenAddress, network2) {
270
+ const chain = network2 === "testnet" ? CHAIN.testnet : CHAIN.mainnet;
271
+ const url = `${FLAUNCH_DATA_API_BASE}/v1/${chain.network}/tokens/${tokenAddress}/details`;
272
+ const response = await fetchWithRetry(url, { method: "GET" });
273
+ if (!response.ok) {
274
+ const text = await response.text();
275
+ throw new Error(`Flaunch data API error: ${response.status} \u2014 ${text}`);
276
+ }
277
+ const data = await response.json();
278
+ if (!data || typeof data !== "object" || !data.tokenAddress || !data.name || !data.symbol) {
279
+ throw new Error("Invalid token details response: missing required fields");
280
+ }
281
+ const price2 = data.price;
282
+ const volume = data.volume;
283
+ if (!price2?.marketCapETH || !volume?.volume24h) {
284
+ throw new Error("Invalid token details response: missing price/volume data");
285
+ }
286
+ return data;
287
+ }
288
+ async function fetchTokenHolderCount(tokenAddress, network2) {
289
+ const chain = network2 === "testnet" ? CHAIN.testnet : CHAIN.mainnet;
290
+ const url = `${FLAUNCH_DATA_API_BASE}/v1/${chain.network}/tokens/${tokenAddress}/holders?limit=1&offset=0`;
291
+ const response = await fetchWithRetry(url, { method: "GET" });
292
+ if (!response.ok) {
293
+ const text = await response.text();
294
+ throw new Error(`Flaunch data API error: ${response.status} \u2014 ${text}`);
295
+ }
296
+ const data = await response.json();
297
+ if (typeof data.pagination?.total === "number") {
298
+ return data.pagination.total;
299
+ }
300
+ throw new Error("Holder count unavailable: API response missing pagination.total");
301
+ }
266
302
  async function pollLaunchStatus(jobId, onPoll) {
267
303
  const startTime = Date.now();
268
304
  let consecutiveErrors = 0;
@@ -645,13 +681,23 @@ async function launch(opts) {
645
681
  if (isNew) {
646
682
  console.log(`
647
683
  Wallet created: ${wallet2.address}`);
648
- console.log(`Private key: ${wallet2.privateKey}`);
649
- console.log("(Save this key \u2014 it will not be shown again)\n");
684
+ console.log(`Key saved to ~/.moltlaunch/wallet.json (never share this file)
685
+ `);
650
686
  } else {
651
687
  console.log(`
652
688
  Using wallet: ${wallet2.address}`);
653
689
  }
654
690
  }
691
+ const existing = await fetchTokensByOwner(wallet2.address, network2);
692
+ if (existing.data.length > 0) {
693
+ const t = existing.data[0];
694
+ printError(
695
+ `This wallet already has a token: ${t.name} (${t.symbol}). One identity per wallet.`,
696
+ json,
697
+ EXIT_CODES.GENERAL
698
+ );
699
+ process.exit(EXIT_CODES.GENERAL);
700
+ }
655
701
  if (!json) process.stdout.write("Uploading image...");
656
702
  const imageIpfs = await uploadImage(imageSource);
657
703
  if (!json) console.log(` ${imageIpfs.slice(0, 16)}...`);
@@ -721,8 +767,8 @@ Using wallet: ${wallet2.address}`);
721
767
  outputData.announcements = announcements;
722
768
  }
723
769
  if (isNew) {
724
- outputData.privateKey = wallet2.privateKey;
725
- outputData.walletNote = "Save this private key \u2014 it will not be shown again";
770
+ outputData.walletPath = "~/.moltlaunch/wallet.json";
771
+ outputData.walletNote = "Key saved locally \u2014 never share this file";
726
772
  }
727
773
  printSuccess("Token launched successfully!", outputData, json);
728
774
  } catch (error) {
@@ -738,7 +784,7 @@ Using wallet: ${wallet2.address}`);
738
784
 
739
785
  // src/commands/wallet.ts
740
786
  async function wallet(opts) {
741
- const { showKey, json } = opts;
787
+ const { json } = opts;
742
788
  try {
743
789
  const data = await loadWallet();
744
790
  if (!data) {
@@ -756,9 +802,6 @@ async function wallet(opts) {
756
802
  network: json ? "Base" : void 0,
757
803
  createdAt: data.createdAt
758
804
  };
759
- if (showKey) {
760
- output.privateKey = data.privateKey;
761
- }
762
805
  printSuccess("Wallet info", output, json);
763
806
  } catch (error) {
764
807
  if (error instanceof MltlError) {
@@ -856,34 +899,7 @@ Your tokens (${sorted.length}) \u2014 ${chain.name}
856
899
  }
857
900
 
858
901
  // src/commands/claim.ts
859
- import { ethers as ethers4 } from "ethers";
860
-
861
- // src/lib/heartbeat.ts
862
902
  import { ethers as ethers3 } from "ethers";
863
- var ACTION_CODES = { swap: 1, claim: 2, launch: 3 };
864
- function encodeBeat(action, txRef, reason) {
865
- const code = ACTION_CODES[action] ?? 0;
866
- const ref = txRef ?? ethers3.ZeroHash;
867
- const parts = ethers3.solidityPacked(["uint8", "bytes32"], [code, ref]);
868
- if (reason) {
869
- return ethers3.concat([parts, ethers3.toUtf8Bytes(reason.slice(0, 140))]);
870
- }
871
- return parts;
872
- }
873
- async function emitBeat(privateKey, network2, action, txRef, reason) {
874
- try {
875
- if (HEARTBEAT_ADDRESS === "0x0000000000000000000000000000000000000000") return;
876
- const signer = await getSigner(privateKey, network2);
877
- await signer.sendTransaction({
878
- to: HEARTBEAT_ADDRESS,
879
- value: ethers3.parseEther(BEAT_FEE),
880
- data: encodeBeat(action, txRef, reason)
881
- });
882
- } catch {
883
- }
884
- }
885
-
886
- // src/commands/claim.ts
887
903
  var REVENUE_MANAGER_ABI = [
888
904
  "function balances(address) external view returns (uint256)",
889
905
  "function claim() external returns (uint256)"
@@ -901,9 +917,9 @@ async function claim(opts) {
901
917
  throw new NoGasError(walletData.address);
902
918
  }
903
919
  const signer = await getSigner(walletData.privateKey, network2);
904
- const rm = new ethers4.Contract(REVENUE_MANAGER_ADDRESS, REVENUE_MANAGER_ABI, signer);
920
+ const rm = new ethers3.Contract(REVENUE_MANAGER_ADDRESS, REVENUE_MANAGER_ABI, signer);
905
921
  const claimable = await rm.balances(walletData.address);
906
- const claimableEth = ethers4.formatEther(claimable);
922
+ const claimableEth = ethers3.formatEther(claimable);
907
923
  if (claimable === 0n) {
908
924
  printSuccess("No fees to claim", {
909
925
  claimable: "0 ETH",
@@ -923,9 +939,6 @@ Claimable: ${claimableEth} ETH`);
923
939
  throw new MltlError("Transaction was dropped or replaced", EXIT_CODES.GENERAL);
924
940
  }
925
941
  if (!json) console.log(" confirmed");
926
- if (!opts.noHeartbeat) {
927
- emitBeat(walletData.privateKey, network2, "claim", receipt.hash, opts.reason);
928
- }
929
942
  printSuccess("Fees claimed successfully!", {
930
943
  transactionHash: receipt.hash,
931
944
  claimed: `${claimableEth} ETH (minus protocol fee)`,
@@ -944,7 +957,7 @@ Claimable: ${claimableEth} ETH`);
944
957
  }
945
958
 
946
959
  // src/commands/fees.ts
947
- import { ethers as ethers5 } from "ethers";
960
+ import { ethers as ethers4 } from "ethers";
948
961
  var REVENUE_MANAGER_ABI2 = [
949
962
  "function balances(address) external view returns (uint256)",
950
963
  "function protocolFee() external view returns (uint256)"
@@ -958,17 +971,17 @@ async function fees(opts) {
958
971
  throw new NoWalletError();
959
972
  }
960
973
  const chain = testnet ? CHAIN.testnet : CHAIN.mainnet;
961
- const provider = new ethers5.JsonRpcProvider(chain.rpcUrl);
962
- const rm = new ethers5.Contract(REVENUE_MANAGER_ADDRESS, REVENUE_MANAGER_ABI2, provider);
974
+ const provider = new ethers4.JsonRpcProvider(chain.rpcUrl);
975
+ const rm = new ethers4.Contract(REVENUE_MANAGER_ADDRESS, REVENUE_MANAGER_ABI2, provider);
963
976
  const claimable = await rm.balances(walletData.address);
964
- const claimableEth = ethers5.formatEther(claimable);
977
+ const claimableEth = ethers4.formatEther(claimable);
965
978
  let protocolFeeBps = 1000n;
966
979
  try {
967
980
  protocolFeeBps = await rm.protocolFee();
968
981
  } catch {
969
982
  }
970
983
  const afterProtocol = claimable - claimable * protocolFeeBps / 10000n;
971
- const afterProtocolEth = ethers5.formatEther(afterProtocol);
984
+ const afterProtocolEth = ethers4.formatEther(afterProtocol);
972
985
  const walletBalance = await getWalletBalance(walletData.address, network2);
973
986
  const hasGas = parseFloat(walletBalance) > 0;
974
987
  printSuccess("Fee balance", {
@@ -996,12 +1009,39 @@ async function fees(opts) {
996
1009
  import { parseEther } from "viem";
997
1010
 
998
1011
  // src/lib/viem-client.ts
999
- import { createPublicClient, createWalletClient, http } from "viem";
1012
+ import { createPublicClient, createWalletClient, http, encodeFunctionData } from "viem";
1000
1013
  import { privateKeyToAccount } from "viem/accounts";
1001
1014
  import { base, baseSepolia } from "viem/chains";
1002
1015
  import { createDrift } from "@delvtech/drift";
1003
1016
  import { viemAdapter } from "@delvtech/drift-viem";
1004
1017
  import { ReadWriteFlaunchSDK } from "@flaunch/sdk";
1018
+
1019
+ // src/lib/memo.ts
1020
+ var MAGIC_PREFIX = "4d4c544c";
1021
+ var MAX_MEMO_BYTES = 65532;
1022
+ function encodeMemo(memo) {
1023
+ const json = JSON.stringify(memo);
1024
+ const bytes = new TextEncoder().encode(json);
1025
+ if (bytes.length === 0) return null;
1026
+ if (bytes.length > MAX_MEMO_BYTES) {
1027
+ console.warn(`Memo too large (${bytes.length} bytes, max ${MAX_MEMO_BYTES}). Skipping.`);
1028
+ return null;
1029
+ }
1030
+ const hexPayload = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1031
+ return `0x${MAGIC_PREFIX}${hexPayload}`;
1032
+ }
1033
+ function appendMemoToCalldata(calldata, memoHex) {
1034
+ return `${calldata}${memoHex.slice(2)}`;
1035
+ }
1036
+
1037
+ // src/lib/viem-client.ts
1038
+ var _pendingMemoHex = null;
1039
+ function setMemo(memoHex) {
1040
+ _pendingMemoHex = memoHex;
1041
+ }
1042
+ function clearMemo() {
1043
+ _pendingMemoHex = null;
1044
+ }
1005
1045
  var VIEM_CHAINS = {
1006
1046
  mainnet: base,
1007
1047
  testnet: baseSepolia
@@ -1019,17 +1059,53 @@ function createFlaunchSdk(privateKey, network2) {
1019
1059
  account,
1020
1060
  transport: http(chainConfig.rpcUrl)
1021
1061
  });
1062
+ const patchedWalletClient = new Proxy(walletClient, {
1063
+ get(target, prop, receiver) {
1064
+ const val = Reflect.get(target, prop, receiver);
1065
+ if (typeof val !== "function") return val;
1066
+ if (prop === "writeContract" || prop === "sendTransaction" || prop === "deployContract") {
1067
+ return (args) => {
1068
+ const patched = { ...args, account };
1069
+ if (_pendingMemoHex && (prop === "writeContract" || prop === "sendTransaction")) {
1070
+ const memo = _pendingMemoHex;
1071
+ _pendingMemoHex = null;
1072
+ if (prop === "sendTransaction" && typeof patched.data === "string") {
1073
+ patched.data = appendMemoToCalldata(patched.data, memo);
1074
+ }
1075
+ if (prop === "writeContract" && patched.abi && patched.functionName) {
1076
+ const encoded = encodeFunctionData({
1077
+ abi: patched.abi,
1078
+ functionName: patched.functionName,
1079
+ args: patched.args ?? []
1080
+ });
1081
+ const data = appendMemoToCalldata(encoded, memo);
1082
+ const sendTx = Reflect.get(target, "sendTransaction", receiver);
1083
+ return sendTx.call(target, {
1084
+ account,
1085
+ to: patched.address,
1086
+ data,
1087
+ value: patched.value ?? 0n,
1088
+ ...patched.gas ? { gas: patched.gas } : {}
1089
+ });
1090
+ }
1091
+ }
1092
+ return val.call(target, patched);
1093
+ };
1094
+ }
1095
+ return val.bind(target);
1096
+ }
1097
+ });
1022
1098
  const drift = createDrift({
1023
1099
  adapter: viemAdapter({
1024
1100
  publicClient,
1025
- walletClient
1101
+ walletClient: patchedWalletClient
1026
1102
  })
1027
1103
  });
1028
1104
  const flaunch = new ReadWriteFlaunchSDK(chain.id, drift);
1029
1105
  return {
1030
1106
  flaunch,
1031
1107
  publicClient,
1032
- walletClient,
1108
+ walletClient: patchedWalletClient,
1033
1109
  account
1034
1110
  };
1035
1111
  }
@@ -1048,6 +1124,18 @@ async function swap(opts) {
1048
1124
  if (!json) console.log(`
1049
1125
  Swapping on ${chainConfig.name}...`);
1050
1126
  const { flaunch, publicClient, walletClient, account } = createFlaunchSdk(walletData.privateKey, network2);
1127
+ if (opts.memo) {
1128
+ const memoHex = encodeMemo({
1129
+ agent: walletData.address,
1130
+ action: side,
1131
+ token,
1132
+ memo: opts.memo,
1133
+ ts: Date.now()
1134
+ });
1135
+ setMemo(memoHex);
1136
+ } else {
1137
+ clearMemo();
1138
+ }
1051
1139
  const coinAddress = token;
1052
1140
  const amountIn = parseEther(amount);
1053
1141
  let txHash;
@@ -1081,6 +1169,7 @@ Swapping on ${chainConfig.name}...`);
1081
1169
  });
1082
1170
  }
1083
1171
  }
1172
+ clearMemo();
1084
1173
  if (!json) console.log(` tx ${txHash}`);
1085
1174
  if (!json) process.stdout.write("Waiting for confirmation...");
1086
1175
  const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
@@ -1088,9 +1177,6 @@ Swapping on ${chainConfig.name}...`);
1088
1177
  throw new SwapError("Transaction reverted");
1089
1178
  }
1090
1179
  if (!json) console.log(" confirmed");
1091
- if (!opts.noHeartbeat) {
1092
- emitBeat(walletData.privateKey, network2, "swap", receipt.transactionHash, opts.reason);
1093
- }
1094
1180
  printSuccess(`${side === "buy" ? "Buy" : "Sell"} swap completed!`, {
1095
1181
  transactionHash: receipt.transactionHash,
1096
1182
  side,
@@ -1098,9 +1184,11 @@ Swapping on ${chainConfig.name}...`);
1098
1184
  tokenAddress: token,
1099
1185
  network: chainConfig.name,
1100
1186
  explorer: `${chainConfig.explorer}/tx/${receipt.transactionHash}`,
1101
- flaunch: `${chainConfig.flaunchUrl}/coin/${token}`
1187
+ flaunch: `${chainConfig.flaunchUrl}/coin/${token}`,
1188
+ ...opts.memo ? { memo: opts.memo } : {}
1102
1189
  }, json);
1103
1190
  } catch (error) {
1191
+ clearMemo();
1104
1192
  if (error instanceof MltlError) {
1105
1193
  printError(error.message, json, error.exitCode);
1106
1194
  process.exit(error.exitCode);
@@ -1112,20 +1200,23 @@ Swapping on ${chainConfig.name}...`);
1112
1200
  }
1113
1201
 
1114
1202
  // src/commands/network.ts
1115
- import { ethers as ethers6 } from "ethers";
1203
+ import { ethers as ethers5 } from "ethers";
1116
1204
  var REVENUE_MANAGER_ABI3 = [
1117
1205
  "function balances(address) external view returns (uint256)"
1118
1206
  ];
1119
- var ACTION_NAMES = { 1: "swap", 2: "claim", 3: "launch" };
1120
- function formatEth(wei) {
1121
- const eth = parseFloat(ethers6.formatEther(wei));
1122
- if (eth >= 1) return `${eth.toFixed(4)} ETH`;
1123
- if (eth >= 1e-3) return `${eth.toFixed(6)} ETH`;
1124
- return `${eth.toExponential(2)} ETH`;
1207
+ function formatEth(value) {
1208
+ if (value >= 1) return `${value.toFixed(4)} ETH`;
1209
+ if (value >= 1e-3) return `${value.toFixed(6)} ETH`;
1210
+ if (value === 0) return "0 ETH";
1211
+ return `${value.toExponential(2)} ETH`;
1212
+ }
1213
+ function formatEthWei(wei) {
1214
+ const eth = parseFloat(ethers5.formatEther(wei));
1215
+ return formatEth(eth);
1125
1216
  }
1126
1217
  function formatMarketCap2(marketCapWei) {
1127
1218
  try {
1128
- const eth = parseFloat(ethers6.formatEther(BigInt(marketCapWei)));
1219
+ const eth = parseFloat(ethers5.formatEther(BigInt(marketCapWei)));
1129
1220
  if (eth >= 1e3) return `${(eth / 1e3).toFixed(1)}k ETH`;
1130
1221
  if (eth >= 1) return `${eth.toFixed(2)} ETH`;
1131
1222
  if (eth >= 1e-3) return `${eth.toFixed(4)} ETH`;
@@ -1137,7 +1228,40 @@ function formatMarketCap2(marketCapWei) {
1137
1228
  function truncate(addr) {
1138
1229
  return addr.slice(0, 6) + "..." + addr.slice(-4);
1139
1230
  }
1140
- async function fetchAllTokens() {
1231
+ function powerBar(score) {
1232
+ const filled = Math.round(score / 10);
1233
+ return "\u2588".repeat(filled) + "\u2591".repeat(10 - filled);
1234
+ }
1235
+ function sortAgents(agents, field) {
1236
+ const sorted = [...agents];
1237
+ switch (field) {
1238
+ case "power":
1239
+ return sorted.sort((a, b) => b.powerScore.total - a.powerScore.total);
1240
+ case "mcap":
1241
+ return sorted.sort((a, b) => b.marketCapETH - a.marketCapETH);
1242
+ case "volume":
1243
+ return sorted.sort((a, b) => b.volume24hETH - a.volume24hETH);
1244
+ case "holders":
1245
+ return sorted.sort((a, b) => b.holders - a.holders);
1246
+ case "newest":
1247
+ return sorted.reverse();
1248
+ }
1249
+ }
1250
+ function buildMemoMap(swaps) {
1251
+ const map = /* @__PURE__ */ new Map();
1252
+ for (const swap2 of swaps) {
1253
+ if (swap2.memo && !map.has(swap2.tokenAddress)) {
1254
+ map.set(swap2.tokenAddress, swap2.memo);
1255
+ }
1256
+ }
1257
+ return map;
1258
+ }
1259
+ async function fetchWorkerState() {
1260
+ const res = await fetch(`${WORKER_API_URL}/api/network`);
1261
+ if (!res.ok) throw new Error(`Worker API error: ${res.status}`);
1262
+ return await res.json();
1263
+ }
1264
+ async function fetchAllTokensFallback() {
1141
1265
  const all = [];
1142
1266
  let offset = 0;
1143
1267
  const limit = 100;
@@ -1154,55 +1278,105 @@ async function fetchAllTokens() {
1154
1278
  }
1155
1279
  return all;
1156
1280
  }
1157
- async function fetchBeats(provider) {
1158
- const beats = /* @__PURE__ */ new Map();
1159
- if (HEARTBEAT_ADDRESS === "0x0000000000000000000000000000000000000000") {
1160
- return beats;
1161
- }
1162
- try {
1163
- const latestBlock = await provider.getBlockNumber();
1164
- const fromBlock = Math.max(0, latestBlock - 43200);
1165
- const beatTopic = ethers6.id("Beat(address,bytes)");
1166
- const logs = await provider.getLogs({
1167
- address: HEARTBEAT_ADDRESS,
1168
- topics: [beatTopic],
1169
- fromBlock,
1170
- toBlock: latestBlock
1171
- });
1172
- for (const log of logs) {
1173
- const agent = ethers6.getAddress("0x" + log.topics[1].slice(26));
1174
- const data = log.data;
1175
- let action = "beat";
1176
- if (data.length >= 4) {
1177
- try {
1178
- const decoded = ethers6.AbiCoder.defaultAbiCoder().decode(["bytes"], data);
1179
- const beatData = decoded[0];
1180
- if (beatData.length >= 4) {
1181
- const actionCode = parseInt(beatData.slice(2, 4), 16);
1182
- action = ACTION_NAMES[actionCode] ?? "beat";
1183
- }
1184
- } catch {
1185
- }
1186
- }
1187
- const existing = beats.get(agent);
1188
- if (!existing || log.blockNumber > existing.block) {
1189
- beats.set(agent, { block: log.blockNumber, action });
1281
+ function renderRichOutput(agents, memoMap, opts) {
1282
+ let sorted = sortAgents(agents, opts.sort);
1283
+ if (opts.limit > 0) sorted = sorted.slice(0, opts.limit);
1284
+ console.log(`
1285
+ the moltlaunch network \u2014 ${agents.length} agent(s)
1286
+ `);
1287
+ sorted.forEach((agent, i) => {
1288
+ const rank = i + 1;
1289
+ const score = agent.powerScore.total;
1290
+ const bar = powerBar(score);
1291
+ const memo = memoMap.get(agent.tokenAddress);
1292
+ console.log(` #${rank} ${agent.name} (${agent.symbol})${" ".repeat(Math.max(1, 36 - agent.name.length - agent.symbol.length))}${bar} ${score}`);
1293
+ console.log(` MCap: ${formatEth(agent.marketCapETH)} \xB7 Vol 24h: ${formatEth(agent.volume24hETH)} \xB7 ${agent.holders} holders`);
1294
+ console.log(` Fees: ${formatEth(agent.claimableETH)} \xB7 Creator: ${truncate(agent.creator)}`);
1295
+ if (memo) console.log(` Last memo: "${memo}"`);
1296
+ console.log(` Token: ${agent.tokenAddress}`);
1297
+ console.log();
1298
+ });
1299
+ console.log(`${sorted.length} agent(s) shown${sorted.length < agents.length ? ` of ${agents.length} total` : ""}
1300
+ `);
1301
+ }
1302
+ async function renderFallback(tokens, json) {
1303
+ const provider = new ethers5.JsonRpcProvider(CHAIN.mainnet.rpcUrl);
1304
+ const rm = new ethers5.Contract(REVENUE_MANAGER_ADDRESS, REVENUE_MANAGER_ABI3, provider);
1305
+ const creators = [...new Set(tokens.map((t) => t.creator).filter(Boolean))];
1306
+ const feeMap = /* @__PURE__ */ new Map();
1307
+ for (let i = 0; i < creators.length; i += 10) {
1308
+ const batch = creators.slice(i, i + 10);
1309
+ const results = await Promise.allSettled(
1310
+ batch.map(async (addr) => {
1311
+ const balance = await rm.balances(addr);
1312
+ return { addr, balance };
1313
+ })
1314
+ );
1315
+ for (const result of results) {
1316
+ if (result.status === "fulfilled") {
1317
+ feeMap.set(result.value.addr, result.value.balance);
1190
1318
  }
1191
1319
  }
1192
- } catch {
1193
1320
  }
1194
- return beats;
1321
+ const agents = tokens.map((t) => {
1322
+ const creator = t.creator ?? "unknown";
1323
+ const claimable = feeMap.get(creator) ?? 0n;
1324
+ return {
1325
+ tokenAddress: t.tokenAddress,
1326
+ name: t.name || "unnamed",
1327
+ symbol: t.symbol || "???",
1328
+ creator,
1329
+ marketCapETH: formatMarketCap2(t.marketCapETH),
1330
+ claimableETH: formatEthWei(claimable),
1331
+ image: t.image || ""
1332
+ };
1333
+ });
1334
+ if (json) {
1335
+ console.log(JSON.stringify({ success: true, count: agents.length, agents }, null, 2));
1336
+ return;
1337
+ }
1338
+ console.log(`
1339
+ the moltlaunch network \u2014 ${agents.length} agent(s) (basic mode)
1340
+ `);
1341
+ for (const agent of agents) {
1342
+ console.log(` ${agent.name} (${agent.symbol})`);
1343
+ console.log(` Token: ${agent.tokenAddress}`);
1344
+ console.log(` Creator: ${truncate(agent.creator)}`);
1345
+ console.log(` MCap: ${agent.marketCapETH}`);
1346
+ console.log(` Fees: ${agent.claimableETH}`);
1347
+ console.log(` Trade: ${CHAIN.mainnet.flaunchUrl}/coin/${agent.tokenAddress}`);
1348
+ console.log();
1349
+ }
1350
+ console.log(`${agents.length} agent(s) on the moltlaunch network
1351
+ `);
1195
1352
  }
1196
1353
  async function network(opts) {
1197
1354
  const { json } = opts;
1198
1355
  try {
1356
+ let workerState = null;
1357
+ try {
1358
+ workerState = await fetchWorkerState();
1359
+ } catch {
1360
+ if (!json) console.log("Worker unavailable, falling back to basic mode...\n");
1361
+ }
1362
+ if (workerState && workerState.agents.length > 0) {
1363
+ if (json) {
1364
+ let sorted = sortAgents(workerState.agents, opts.sort);
1365
+ if (opts.limit > 0) sorted = sorted.slice(0, opts.limit);
1366
+ console.log(JSON.stringify({
1367
+ success: true,
1368
+ count: sorted.length,
1369
+ totalCount: workerState.agents.length,
1370
+ agents: sorted
1371
+ }, null, 2));
1372
+ return;
1373
+ }
1374
+ const memoMap = buildMemoMap(workerState.swaps);
1375
+ renderRichOutput(workerState.agents, memoMap, opts);
1376
+ return;
1377
+ }
1199
1378
  if (!json) console.log("\nDiscovering moltlaunch agents...\n");
1200
- const provider = new ethers6.JsonRpcProvider(CHAIN.mainnet.rpcUrl);
1201
- const rm = new ethers6.Contract(REVENUE_MANAGER_ADDRESS, REVENUE_MANAGER_ABI3, provider);
1202
- const [tokens, beats] = await Promise.all([
1203
- fetchAllTokens(),
1204
- fetchBeats(provider)
1205
- ]);
1379
+ const tokens = await fetchAllTokensFallback();
1206
1380
  if (tokens.length === 0) {
1207
1381
  if (json) {
1208
1382
  console.log(JSON.stringify({ success: true, agents: [], count: 0 }));
@@ -1213,72 +1387,276 @@ async function network(opts) {
1213
1387
  }
1214
1388
  if (!json) console.log(`Found ${tokens.length} agent(s). Fetching fees...
1215
1389
  `);
1216
- const creators = [...new Set(tokens.map((t) => t.creator).filter(Boolean))];
1217
- const feeMap = /* @__PURE__ */ new Map();
1218
- for (let i = 0; i < creators.length; i += 10) {
1219
- const batch = creators.slice(i, i + 10);
1220
- const results = await Promise.allSettled(
1221
- batch.map(async (addr) => {
1222
- const balance = await rm.balances(addr);
1223
- return { addr, balance };
1224
- })
1225
- );
1226
- for (const result of results) {
1227
- if (result.status === "fulfilled") {
1228
- feeMap.set(result.value.addr, result.value.balance);
1390
+ await renderFallback(tokens, json);
1391
+ } catch (error) {
1392
+ const message = error instanceof Error ? error.message : String(error);
1393
+ printError(message, json, EXIT_CODES.GENERAL);
1394
+ process.exit(EXIT_CODES.GENERAL);
1395
+ }
1396
+ }
1397
+
1398
+ // src/commands/holdings.ts
1399
+ import { ethers as ethers6 } from "ethers";
1400
+ var ERC20_BALANCE_OF = "function balanceOf(address) external view returns (uint256)";
1401
+ var MULTICALL3_ABI = [
1402
+ "function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) external view returns (tuple(bool success, bytes returnData)[])"
1403
+ ];
1404
+ async function holdings(opts) {
1405
+ const { json, testnet } = opts;
1406
+ const network2 = testnet ? "testnet" : "mainnet";
1407
+ const chain = testnet ? CHAIN.testnet : CHAIN.mainnet;
1408
+ try {
1409
+ const { wallet: wallet2 } = await loadOrCreateWallet();
1410
+ if (!json) console.log(`
1411
+ Checking holdings for ${wallet2.address}...
1412
+ `);
1413
+ let tokens = [];
1414
+ try {
1415
+ const res = await fetch(`${WORKER_API_URL}/api/network`);
1416
+ if (!res.ok) throw new Error(`Worker API error: ${res.status}`);
1417
+ const state = await res.json();
1418
+ tokens = state.agents.map((a) => ({
1419
+ address: a.tokenAddress,
1420
+ name: a.name,
1421
+ symbol: a.symbol
1422
+ }));
1423
+ } catch {
1424
+ if (!json) console.log("Could not reach network API. No tokens to check.\n");
1425
+ if (json) console.log(JSON.stringify({ success: false, error: "Network API unreachable" }));
1426
+ return;
1427
+ }
1428
+ if (tokens.length === 0) {
1429
+ if (json) {
1430
+ console.log(JSON.stringify({ success: true, holdings: [], count: 0 }));
1431
+ } else {
1432
+ console.log("No tokens in the network yet.\n");
1433
+ }
1434
+ return;
1435
+ }
1436
+ const provider = new ethers6.JsonRpcProvider(chain.rpcUrl);
1437
+ const multicall = new ethers6.Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, provider);
1438
+ const erc20Iface = new ethers6.Interface([ERC20_BALANCE_OF]);
1439
+ const calls = tokens.map((t) => ({
1440
+ target: t.address,
1441
+ allowFailure: true,
1442
+ callData: erc20Iface.encodeFunctionData("balanceOf", [wallet2.address])
1443
+ }));
1444
+ const results = await multicall.aggregate3.staticCall(calls);
1445
+ const holdings2 = [];
1446
+ for (let i = 0; i < tokens.length; i++) {
1447
+ const result = results[i];
1448
+ if (!result.success) continue;
1449
+ try {
1450
+ const [balance] = erc20Iface.decodeFunctionResult("balanceOf", result.returnData);
1451
+ if (balance > 0n) {
1452
+ holdings2.push({
1453
+ name: tokens[i].name,
1454
+ symbol: tokens[i].symbol,
1455
+ tokenAddress: tokens[i].address,
1456
+ balance: ethers6.formatEther(balance),
1457
+ balanceWei: balance.toString()
1458
+ });
1229
1459
  }
1460
+ } catch {
1230
1461
  }
1231
1462
  }
1232
- const agents = tokens.map((t) => {
1233
- const creator = t.creator ?? "unknown";
1234
- const claimable = feeMap.get(creator) ?? 0n;
1235
- const beat = beats.get(creator);
1236
- return {
1237
- tokenAddress: t.tokenAddress,
1238
- name: t.name || "unnamed",
1239
- symbol: t.symbol || "???",
1240
- creator,
1241
- marketCapETH: formatMarketCap2(t.marketCapETH),
1242
- claimableETH: formatEth(claimable),
1243
- lastBeatBlock: beat?.block ?? null,
1244
- lastBeatAction: beat?.action ?? null,
1245
- image: t.image || ""
1246
- };
1247
- });
1463
+ if (json) {
1464
+ console.log(JSON.stringify({ success: true, count: holdings2.length, holdings: holdings2 }, null, 2));
1465
+ return;
1466
+ }
1467
+ if (holdings2.length === 0) {
1468
+ console.log("You don't hold any tokens in the network.\n");
1469
+ return;
1470
+ }
1471
+ console.log(`Your holdings \u2014 ${chain.name}
1472
+ `);
1473
+ for (const h of holdings2) {
1474
+ const formatted = parseFloat(h.balance).toLocaleString("en-US", {
1475
+ minimumFractionDigits: 2,
1476
+ maximumFractionDigits: 2
1477
+ });
1478
+ console.log(` ${h.name} (${h.symbol})`);
1479
+ console.log(` Balance: ${formatted} ${h.symbol}`);
1480
+ console.log(` Token: ${h.tokenAddress}`);
1481
+ console.log();
1482
+ }
1483
+ console.log(`${holdings2.length} token(s) held
1484
+ `);
1485
+ } catch (error) {
1486
+ const message = error instanceof Error ? error.message : String(error);
1487
+ printError(message, json, EXIT_CODES.GENERAL);
1488
+ process.exit(EXIT_CODES.GENERAL);
1489
+ }
1490
+ }
1491
+
1492
+ // src/commands/fund.ts
1493
+ var FUNDING_METHODS = [
1494
+ { method: "Base Bridge", url: "https://bridge.base.org" },
1495
+ { method: "Coinbase", url: "https://www.coinbase.com" },
1496
+ { method: "Direct transfer", description: "Send ETH on Base to the address above" }
1497
+ ];
1498
+ var MINIMUM_RECOMMENDED = "0.005";
1499
+ async function fund(opts) {
1500
+ const { json } = opts;
1501
+ try {
1502
+ const data = await loadWallet();
1503
+ if (!data) throw new NoWalletError();
1504
+ let balance = null;
1505
+ try {
1506
+ balance = await getWalletBalance(data.address, "mainnet");
1507
+ } catch {
1508
+ }
1248
1509
  if (json) {
1249
1510
  console.log(JSON.stringify({
1250
1511
  success: true,
1251
- count: agents.length,
1252
- heartbeatContract: HEARTBEAT_ADDRESS,
1253
- agents
1512
+ address: data.address,
1513
+ balance,
1514
+ network: "Base",
1515
+ chainId: 8453,
1516
+ fundingMethods: FUNDING_METHODS,
1517
+ minimumRecommended: MINIMUM_RECOMMENDED,
1518
+ message: `Send Base ETH to ${data.address} to fund this agent`
1254
1519
  }, null, 2));
1255
1520
  return;
1256
1521
  }
1257
- for (const agent of agents) {
1258
- const beatStr = agent.lastBeatAction ? `last: ${agent.lastBeatAction} (block ${agent.lastBeatBlock})` : "no beats";
1259
- console.log(` ${agent.name} (${agent.symbol})`);
1260
- console.log(` Token: ${agent.tokenAddress}`);
1261
- console.log(` Creator: ${truncate(agent.creator)}`);
1262
- console.log(` MCap: ${agent.marketCapETH}`);
1263
- console.log(` Fees: ${agent.claimableETH}`);
1264
- console.log(` Heartbeat: ${beatStr}`);
1265
- console.log(` Trade: ${CHAIN.mainnet.flaunchUrl}/coin/${agent.tokenAddress}`);
1266
- console.log();
1522
+ console.log("\nFund your agent wallet\n");
1523
+ console.log(` Address: ${data.address}`);
1524
+ console.log(` Balance: ${balance ?? "unknown"} ETH (Base)`);
1525
+ console.log(` Recommended: ${MINIMUM_RECOMMENDED} ETH`);
1526
+ console.log();
1527
+ console.log(" How to fund:");
1528
+ console.log(" 1. Base Bridge: https://bridge.base.org");
1529
+ console.log(" 2. Coinbase: https://www.coinbase.com");
1530
+ console.log(" 3. Direct: Send ETH on Base to the address above");
1531
+ console.log();
1532
+ } catch (error) {
1533
+ if (error instanceof MltlError) {
1534
+ printError(error.message, json, error.exitCode);
1535
+ process.exit(error.exitCode);
1267
1536
  }
1268
- console.log(`${agents.length} agent(s) on the moltlaunch network
1537
+ const message = error instanceof Error ? error.message : String(error);
1538
+ printError(message, json, EXIT_CODES.GENERAL);
1539
+ process.exit(EXIT_CODES.GENERAL);
1540
+ }
1541
+ }
1542
+
1543
+ // src/commands/price.ts
1544
+ import { ethers as ethers7 } from "ethers";
1545
+ function parseWei(value) {
1546
+ if (/^\d+$/.test(value)) {
1547
+ return ethers7.formatEther(BigInt(value));
1548
+ }
1549
+ return value;
1550
+ }
1551
+ async function price(opts) {
1552
+ const { token, json } = opts;
1553
+ const network2 = opts.testnet ? "testnet" : "mainnet";
1554
+ const chain = CHAIN[network2];
1555
+ try {
1556
+ if (!/^0x[a-fA-F0-9]{40}$/.test(token)) {
1557
+ throw new Error("Invalid token address \u2014 expected 0x followed by 40 hex characters");
1558
+ }
1559
+ if (opts.amount !== void 0) {
1560
+ const parsed = parseFloat(opts.amount);
1561
+ if (isNaN(parsed) || parsed <= 0) {
1562
+ throw new Error(`Invalid amount: ${opts.amount} \u2014 must be a positive number`);
1563
+ }
1564
+ }
1565
+ if (!json) console.log(`
1566
+ Fetching token details...
1269
1567
  `);
1568
+ const [details, holders] = await Promise.all([
1569
+ fetchTokenDetails(token, network2),
1570
+ fetchTokenHolderCount(token, network2).catch(() => null)
1571
+ ]);
1572
+ const marketCapETH = parseWei(details.price.marketCapETH);
1573
+ const volume24hETH = parseWei(details.volume.volume24h);
1574
+ const flaunchUrl = `${chain.flaunchUrl}/coin/${token}`;
1575
+ if (json) {
1576
+ const output = {
1577
+ success: true,
1578
+ tokenAddress: details.tokenAddress,
1579
+ name: details.name,
1580
+ symbol: details.symbol,
1581
+ description: details.description,
1582
+ image: details.image,
1583
+ marketCapETH,
1584
+ priceChange24h: details.price.priceChange24h,
1585
+ volume24hETH,
1586
+ holders,
1587
+ creator: details.status.owner,
1588
+ createdAt: new Date(details.status.createdAt * 1e3).toISOString(),
1589
+ flaunchUrl,
1590
+ network: chain.name
1591
+ };
1592
+ if (opts.amount) {
1593
+ const spendETH = parseFloat(opts.amount);
1594
+ const mcapETH = parseFloat(marketCapETH);
1595
+ const percentOfMcap = mcapETH > 0 ? (spendETH / mcapETH * 100).toFixed(2) : null;
1596
+ output.estimate = {
1597
+ spendETH: opts.amount,
1598
+ percentOfMcap,
1599
+ note: "Approximate \u2014 actual output depends on pool liquidity and slippage"
1600
+ };
1601
+ }
1602
+ console.log(JSON.stringify(output, null, 2));
1603
+ return;
1604
+ }
1605
+ const mcapFormatted = formatEthDisplay(parseFloat(marketCapETH));
1606
+ const volFormatted = formatEthDisplay(parseFloat(volume24hETH));
1607
+ const changeStr = formatChange(details.price.priceChange24h);
1608
+ console.log(` ${details.name} (${details.symbol})`);
1609
+ console.log(` ${details.tokenAddress}
1610
+ `);
1611
+ if (details.description) {
1612
+ console.log(` ${details.description}
1613
+ `);
1614
+ }
1615
+ console.log(` Market cap: ${mcapFormatted}`);
1616
+ console.log(` 24h change: ${changeStr}`);
1617
+ console.log(` 24h volume: ${volFormatted}`);
1618
+ console.log(` Holders: ${holders ?? "unknown"}`);
1619
+ console.log(` Creator: ${details.status.owner}`);
1620
+ console.log(` Trade: ${flaunchUrl}`);
1621
+ if (opts.amount) {
1622
+ const spendETH = parseFloat(opts.amount);
1623
+ const mcapETH = parseFloat(marketCapETH);
1624
+ const pct = mcapETH > 0 ? (spendETH / mcapETH * 100).toFixed(2) : "N/A";
1625
+ console.log();
1626
+ console.log(` Estimate for ${opts.amount} ETH:`);
1627
+ console.log(` ~${pct}% of market cap`);
1628
+ console.log(` Actual output depends on pool liquidity and slippage`);
1629
+ }
1630
+ console.log();
1270
1631
  } catch (error) {
1632
+ if (error instanceof MltlError) {
1633
+ printError(error.message, json, error.exitCode);
1634
+ process.exit(error.exitCode);
1635
+ }
1271
1636
  const message = error instanceof Error ? error.message : String(error);
1272
1637
  printError(message, json, EXIT_CODES.GENERAL);
1273
1638
  process.exit(EXIT_CODES.GENERAL);
1274
1639
  }
1275
1640
  }
1641
+ function formatEthDisplay(eth) {
1642
+ if (eth >= 1e3) return `${(eth / 1e3).toFixed(1)}k ETH`;
1643
+ if (eth >= 1) return `${eth.toFixed(4)} ETH`;
1644
+ if (eth >= 1e-3) return `${eth.toFixed(6)} ETH`;
1645
+ if (eth === 0) return "0 ETH";
1646
+ return `${eth.toExponential(2)} ETH`;
1647
+ }
1648
+ function formatChange(change) {
1649
+ const num = parseFloat(change);
1650
+ if (isNaN(num)) return change;
1651
+ const sign = num >= 0 ? "+" : "";
1652
+ return `${sign}${num.toFixed(2)}%`;
1653
+ }
1276
1654
 
1277
1655
  // src/index.ts
1278
1656
  var require2 = createRequire(import.meta.url);
1279
1657
  var { version } = require2("../package.json");
1280
1658
  var program = new Command();
1281
- program.name("mltl").description("moltlaunch \u2014 the onchain toolkit for agents").version(version).option("--no-heartbeat", "Skip emitting on-chain transparency beat");
1659
+ program.name("mltl").description("moltlaunch \u2014 the onchain agent network").version(version);
1282
1660
  program.command("launch", { isDefault: true }).description("Launch a new token on Base").requiredOption("--name <name>", "Token name").requiredOption("--symbol <symbol>", "Token symbol").requiredOption("--description <desc>", "Token description").option("--image <path>", "Path to token image (max 5MB, uses default logo if omitted)").option("--website <url>", "Website URL (overrides auto-created Moltbook post)").option("--testnet", "Use Base Sepolia testnet", false).option("--json", "Output as JSON (for agents)", false).option("-q, --quiet", "Skip announcing to social platforms", false).action(
1283
1661
  (opts) => launch({
1284
1662
  name: opts.name,
@@ -1291,8 +1669,8 @@ program.command("launch", { isDefault: true }).description("Launch a new token o
1291
1669
  quiet: opts.quiet
1292
1670
  })
1293
1671
  );
1294
- program.command("wallet").description("Show wallet address and balance").option("--show-key", "Show private key", false).option("--json", "Output as JSON", false).action(
1295
- (opts) => wallet({ showKey: opts.showKey, json: opts.json })
1672
+ program.command("wallet").description("Show wallet address and balance").option("--json", "Output as JSON", false).action(
1673
+ (opts) => wallet({ json: opts.json })
1296
1674
  );
1297
1675
  program.command("status").description("List all tokens under the revenue manager").option("--testnet", "Use Base Sepolia testnet", false).option("--json", "Output as JSON", false).action(
1298
1676
  (opts) => status({ testnet: opts.testnet, json: opts.json })
@@ -1300,15 +1678,13 @@ program.command("status").description("List all tokens under the revenue manager
1300
1678
  program.command("fees").description("Check claimable fee balance (read-only, no gas needed)").option("--testnet", "Use Base Sepolia testnet", false).option("--json", "Output as JSON", false).action(
1301
1679
  (opts) => fees({ testnet: opts.testnet, json: opts.json })
1302
1680
  );
1303
- program.command("claim").description("Withdraw accumulated fees from PositionManager escrow").option("--reason <text>", "Reason for claiming (included in heartbeat)").option("--testnet", "Use Base Sepolia testnet", false).option("--json", "Output as JSON", false).action(
1681
+ program.command("claim").description("Withdraw accumulated fees from PositionManager escrow").option("--testnet", "Use Base Sepolia testnet", false).option("--json", "Output as JSON", false).action(
1304
1682
  (opts) => claim({
1305
1683
  testnet: opts.testnet,
1306
- json: opts.json,
1307
- reason: opts.reason,
1308
- noHeartbeat: !program.opts().heartbeat
1684
+ json: opts.json
1309
1685
  })
1310
1686
  );
1311
- program.command("swap").description("Swap ETH for tokens or tokens for ETH on Uniswap V4").requiredOption("--token <address>", "Token address").requiredOption("--amount <amount>", "Amount (ETH for buy, tokens for sell)").requiredOption("--side <direction>", "buy or sell").option("--slippage <percent>", "Slippage tolerance percent", "5").option("--testnet", "Use Base Sepolia testnet", false).option("--reason <text>", "Reason for the trade (included in heartbeat)").option("--json", "Output as JSON", false).action(
1687
+ program.command("swap").description("Swap ETH for tokens or tokens for ETH on Uniswap V4").requiredOption("--token <address>", "Token address").requiredOption("--amount <amount>", "Amount (ETH for buy, tokens for sell)").requiredOption("--side <direction>", "buy or sell").option("--slippage <percent>", "Slippage tolerance percent", "5").option("--testnet", "Use Base Sepolia testnet", false).option("--memo <text>", "Onchain memo \u2014 agent reasoning, strategy notes, or context (appended to tx calldata)").option("--json", "Output as JSON", false).action(
1312
1688
  (opts) => swap({
1313
1689
  token: opts.token,
1314
1690
  amount: opts.amount,
@@ -1316,12 +1692,25 @@ program.command("swap").description("Swap ETH for tokens or tokens for ETH on Un
1316
1692
  slippage: parseFloat(opts.slippage),
1317
1693
  testnet: opts.testnet,
1318
1694
  json: opts.json,
1319
- reason: opts.reason,
1320
- noHeartbeat: !program.opts().heartbeat
1695
+ memo: opts.memo
1321
1696
  })
1322
1697
  );
1323
- program.command("network").description("Discover all moltlaunch agents and their tokens").option("--json", "Output as JSON (for agents)", false).action(
1324
- (opts) => network({ json: opts.json })
1698
+ program.command("network").description("Discover all moltlaunch agents and their tokens").option("--json", "Output as JSON (for agents)", false).option("--sort <field>", "Sort by: power, mcap, volume, holders, newest", "power").option("--limit <n>", "Number of agents to show (0 = all)", "0").action(
1699
+ (opts) => network({ json: opts.json, sort: opts.sort, limit: parseInt(opts.limit, 10) })
1700
+ );
1701
+ program.command("holdings").description("Show tokens you hold in the network").option("--json", "Output as JSON", false).option("--testnet", "Use Base Sepolia testnet", false).action(
1702
+ (opts) => holdings({ json: opts.json, testnet: opts.testnet })
1703
+ );
1704
+ program.command("fund").description("Show wallet address, balance, and funding instructions").option("--json", "Output as JSON", false).action(
1705
+ (opts) => fund({ json: opts.json })
1706
+ );
1707
+ program.command("price").description("Fetch token details and price info from Flaunch").requiredOption("--token <address>", "Token contract address").option("--amount <eth>", "ETH amount to simulate spend").option("--testnet", "Use Base Sepolia testnet", false).option("--json", "Output as JSON", false).action(
1708
+ (opts) => price({
1709
+ token: opts.token,
1710
+ amount: opts.amount,
1711
+ testnet: opts.testnet,
1712
+ json: opts.json
1713
+ })
1325
1714
  );
1326
1715
  program.parse();
1327
1716
  //# sourceMappingURL=index.js.map