@bankr/cli 0.3.13 → 0.3.32

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.
@@ -19,14 +19,20 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
19
19
  import { join } from "node:path";
20
20
  import chalk from "chalk";
21
21
  import { input, select, confirm } from "@inquirer/prompts";
22
+ import { authHeaders, handleResponse } from "../lib/api.js";
22
23
  import * as output from "../lib/output.js";
23
- import { getErrorMessage } from "../lib/errors.js";
24
- import { getApiUrl, requireApiKey, CLI_USER_AGENT } from "../lib/config.js";
24
+ import { getApiUrl } from "../lib/config.js";
25
25
  // ── Config file helpers ─────────────────────────────────────────────────
26
26
  const CONFIG_FILENAME = "bankr.x402.json";
27
27
  function findProjectRoot() {
28
28
  return process.cwd();
29
29
  }
30
+ // Config values come from user-edited JSON cast without validation, so fields
31
+ // feeding select() defaults are plain strings; narrow them to the offered
32
+ // choices (select's default must match the literal union of choice values).
33
+ function pickDefault(value, allowed) {
34
+ return allowed.find((v) => v === value);
35
+ }
30
36
  function loadConfigFile(projectRoot) {
31
37
  const candidates = [
32
38
  join(projectRoot, CONFIG_FILENAME),
@@ -38,8 +44,7 @@ function loadConfigFile(projectRoot) {
38
44
  return JSON.parse(readFileSync(p, "utf-8"));
39
45
  }
40
46
  catch {
41
- output.error(`Failed to parse ${p}. Check that it is valid JSON.`);
42
- process.exit(1);
47
+ output.fatal(`Failed to parse ${p}. Check that it is valid JSON.`);
43
48
  }
44
49
  }
45
50
  }
@@ -49,24 +54,6 @@ function saveConfigFile(projectRoot, config) {
49
54
  const configPath = join(projectRoot, CONFIG_FILENAME);
50
55
  writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
51
56
  }
52
- // ── API helpers ─────────────────────────────────────────────────────────
53
- function authHeaders() {
54
- return {
55
- "X-API-Key": requireApiKey(),
56
- "Content-Type": "application/json",
57
- "User-Agent": CLI_USER_AGENT,
58
- };
59
- }
60
- async function handleResponse(res) {
61
- if (!res.ok) {
62
- const body = (await res
63
- .json()
64
- .catch(() => ({ message: res.statusText })));
65
- const msg = body.message || body.error || res.statusText;
66
- throw new Error(`API error (${res.status}): ${msg}`);
67
- }
68
- return res.json();
69
- }
70
57
  // ── Default handler template ────────────────────────────────────────────
71
58
  const HANDLER_TEMPLATE = `/**
72
59
  * x402 service handler.
@@ -138,19 +125,16 @@ function validateServiceName(name) {
138
125
  export async function x402AddCommand(name) {
139
126
  const nameError = validateServiceName(name);
140
127
  if (nameError) {
141
- output.error(nameError);
142
- process.exit(1);
128
+ output.fatal(nameError);
143
129
  }
144
130
  const root = findProjectRoot();
145
131
  const x402Dir = join(root, "x402");
146
132
  if (!existsSync(x402Dir)) {
147
- output.error("No x402/ directory found. Run 'bankr x402 init' first.");
148
- process.exit(1);
133
+ output.fatal("No x402/ directory found. Run 'bankr x402 init' first.");
149
134
  }
150
135
  const serviceDir = join(x402Dir, name);
151
136
  if (existsSync(serviceDir)) {
152
- output.error(`Service "${name}" already exists at x402/${name}/`);
153
- process.exit(1);
137
+ output.fatal(`Service "${name}" already exists at x402/${name}/`);
154
138
  }
155
139
  // Ask for method
156
140
  const method = await select({
@@ -238,9 +222,7 @@ export default async function handler(req: Request): Promise<Response> {
238
222
  };
239
223
  // Add to config
240
224
  let config = loadConfigFile(root);
241
- if (!config) {
242
- config = { services: {} };
243
- }
225
+ config || (config = { services: {} });
244
226
  config.services[name] = {
245
227
  price: "0.001",
246
228
  description: `${name} service`,
@@ -258,9 +240,7 @@ export default async function handler(req: Request): Promise<Response> {
258
240
  export async function x402ConfigureCommand(name) {
259
241
  const root = findProjectRoot();
260
242
  let config = loadConfigFile(root);
261
- if (!config) {
262
- config = { services: {} };
263
- }
243
+ config || (config = { services: {} });
264
244
  const existing = config.services[name] ?? { price: "0.001" };
265
245
  const description = await input({
266
246
  message: "Service description:",
@@ -273,12 +253,14 @@ export async function x402ConfigureCommand(name) {
273
253
  theme: output.bankrTheme,
274
254
  });
275
255
  const currency = "USDC";
256
+ const networkDefault = pickDefault(existing.network, ["base"]) ?? "base";
276
257
  const network = await select({
277
258
  message: "Network:",
278
259
  choices: [{ name: "Base", value: "base" }],
279
- default: existing.network ?? "base",
260
+ default: networkDefault,
280
261
  theme: output.bankrTheme,
281
262
  });
263
+ const methodDefault = pickDefault(existing.methods?.[0], ["GET", "POST", "*"]) ?? "GET";
282
264
  const method = await select({
283
265
  message: "HTTP method:",
284
266
  choices: [
@@ -286,7 +268,7 @@ export async function x402ConfigureCommand(name) {
286
268
  { name: "POST — JSON body", value: "POST" },
287
269
  { name: "Any — accept all methods", value: "*" },
288
270
  ],
289
- default: existing.methods?.[0] ?? "GET",
271
+ default: methodDefault,
290
272
  theme: output.bankrTheme,
291
273
  });
292
274
  const paymentScheme = await select({
@@ -323,14 +305,11 @@ export async function x402DeployCommand(name) {
323
305
  const root = findProjectRoot();
324
306
  const x402Dir = join(root, "x402");
325
307
  if (!existsSync(x402Dir)) {
326
- output.error("No x402/ directory found. Run 'bankr x402 init' first.");
327
- process.exit(1);
308
+ output.fatal("No x402/ directory found. Run 'bankr x402 init' first.");
328
309
  }
329
310
  // Load or bootstrap config
330
311
  let config = loadConfigFile(root);
331
- if (!config) {
332
- config = { services: {} };
333
- }
312
+ config || (config = { services: {} });
334
313
  // Discover services
335
314
  const { readdirSync, statSync } = await import("node:fs");
336
315
  const entries = readdirSync(x402Dir);
@@ -343,21 +322,18 @@ export async function x402DeployCommand(name) {
343
322
  }
344
323
  }
345
324
  if (services.length === 0) {
346
- output.error("No services found. Run 'bankr x402 add <name>' first.");
347
- process.exit(1);
325
+ output.fatal("No services found. Run 'bankr x402 add <name>' first.");
348
326
  }
349
327
  // Filter to single service if name provided
350
328
  const toDeploy = name ? services.filter((s) => s === name) : services;
351
329
  if (name && toDeploy.length === 0) {
352
- output.error(`Service "${name}" not found in x402/`);
353
- process.exit(1);
330
+ output.fatal(`Service "${name}" not found in x402/`);
354
331
  }
355
332
  // Validate service names and ensure config entries
356
333
  for (const svc of toDeploy) {
357
334
  const nameError = validateServiceName(svc);
358
335
  if (nameError) {
359
- output.error(nameError);
360
- process.exit(1);
336
+ output.fatal(nameError);
361
337
  }
362
338
  if (!config.services[svc]) {
363
339
  config.services[svc] = {
@@ -422,9 +398,7 @@ export async function x402DeployCommand(name) {
422
398
  }
423
399
  }
424
400
  catch (err) {
425
- spin.fail("Deploy failed");
426
- output.error(getErrorMessage(err));
427
- process.exit(1);
401
+ output.failWith(spin, "Deploy failed", err);
428
402
  }
429
403
  }
430
404
  /**
@@ -452,9 +426,7 @@ export async function x402ListCommand() {
452
426
  }
453
427
  }
454
428
  catch (err) {
455
- spin.fail("Failed to list endpoints");
456
- output.error(getErrorMessage(err));
457
- process.exit(1);
429
+ output.failWith(spin, "Failed to list endpoints", err);
458
430
  }
459
431
  }
460
432
  /**
@@ -473,9 +445,7 @@ export async function x402PauseResumeCommand(name, action) {
473
445
  spin.succeed(`${name} ${action === "pause" ? "paused" : "resumed"}`);
474
446
  }
475
447
  catch (err) {
476
- spin.fail(`Failed to ${action} ${name}`);
477
- output.error(getErrorMessage(err));
478
- process.exit(1);
448
+ output.failWith(spin, `Failed to ${action} ${name}`, err);
479
449
  }
480
450
  }
481
451
  /**
@@ -499,9 +469,7 @@ export async function x402DeleteCommand(name) {
499
469
  spin.succeed(`${name} deleted`);
500
470
  }
501
471
  catch (err) {
502
- spin.fail(`Failed to delete ${name}`);
503
- output.error(getErrorMessage(err));
504
- process.exit(1);
472
+ output.failWith(spin, `Failed to delete ${name}`, err);
505
473
  }
506
474
  }
507
475
  /**
@@ -533,9 +501,7 @@ export async function x402RevenueCommand(name) {
533
501
  console.log();
534
502
  }
535
503
  catch (err) {
536
- spin.fail("Failed to fetch revenue");
537
- output.error(getErrorMessage(err));
538
- process.exit(1);
504
+ output.failWith(spin, "Failed to fetch revenue", err);
539
505
  }
540
506
  }
541
507
  /**
@@ -544,14 +510,12 @@ export async function x402RevenueCommand(name) {
544
510
  export async function x402EnvSetCommand(keyValue) {
545
511
  const eqIdx = keyValue.indexOf("=");
546
512
  if (eqIdx === -1) {
547
- output.error("Usage: bankr x402 env set KEY=VALUE");
548
- process.exit(1);
513
+ output.fatal("Usage: bankr x402 env set KEY=VALUE");
549
514
  }
550
515
  const key = keyValue.slice(0, eqIdx);
551
516
  const value = keyValue.slice(eqIdx + 1);
552
517
  if (!key) {
553
- output.error("Key cannot be empty");
554
- process.exit(1);
518
+ output.fatal("Key cannot be empty");
555
519
  }
556
520
  const spin = output.spinner(`Setting ${key}...`);
557
521
  try {
@@ -564,9 +528,7 @@ export async function x402EnvSetCommand(keyValue) {
564
528
  spin.succeed(`Set ${key}`);
565
529
  }
566
530
  catch (err) {
567
- spin.fail(`Failed to set ${key}`);
568
- output.error(getErrorMessage(err));
569
- process.exit(1);
531
+ output.failWith(spin, `Failed to set ${key}`, err);
570
532
  }
571
533
  }
572
534
  /**
@@ -589,9 +551,7 @@ export async function x402EnvListCommand() {
589
551
  }
590
552
  }
591
553
  catch (err) {
592
- spin.fail("Failed to list env vars");
593
- output.error(getErrorMessage(err));
594
- process.exit(1);
554
+ output.failWith(spin, "Failed to list env vars", err);
595
555
  }
596
556
  }
597
557
  /**
@@ -608,9 +568,7 @@ export async function x402EnvUnsetCommand(key) {
608
568
  spin.succeed(`Removed ${key}`);
609
569
  }
610
570
  catch (err) {
611
- spin.fail(`Failed to remove ${key}`);
612
- output.error(getErrorMessage(err));
613
- process.exit(1);
571
+ output.failWith(spin, `Failed to remove ${key}`, err);
614
572
  }
615
573
  }
616
574
  // ── Schema display helpers ──────────────────────────────────────────────
@@ -766,34 +724,28 @@ function printServiceFormatted(svc, rank, total) {
766
724
  // ── Schema ────────────────────────────────────────────────────────────
767
725
  export async function x402SchemaCommand(url, opts = {}) {
768
726
  if (!url) {
769
- output.error("Usage: bankr x402 schema <url>\n Example: bankr x402 schema https://x402.bankr.bot/0xabc.../my-service");
770
- process.exit(1);
727
+ output.fatal("Usage: bankr x402 schema <url>\n Example: bankr x402 schema https://x402.bankr.bot/0xabc.../my-service");
771
728
  }
772
729
  let parsed;
773
730
  try {
774
731
  parsed = new URL(url);
775
732
  }
776
733
  catch {
777
- output.error("Invalid URL");
778
- process.exit(1);
779
- return; // unreachable but makes TS happy
734
+ output.fatal("Invalid URL");
780
735
  }
781
736
  if (!parsed.hostname.endsWith("x402.bankr.bot")) {
782
- output.error("Schema lookup is only available for Bankr-hosted endpoints (x402.bankr.bot).");
783
- process.exit(1);
737
+ output.fatal("Schema lookup is only available for Bankr-hosted endpoints (x402.bankr.bot).");
784
738
  }
785
739
  const parts = parsed.pathname.split("/").filter(Boolean);
786
740
  if (parts.length < 2) {
787
- output.error("Invalid Bankr x402 URL. Expected: https://x402.bankr.bot/<address>/<service>");
788
- process.exit(1);
741
+ output.fatal("Invalid Bankr x402 URL. Expected: https://x402.bankr.bot/<address>/<service>");
789
742
  }
790
743
  const spin = output.spinner("Fetching endpoint schema...");
791
744
  try {
792
745
  const res = await fetch(`${getApiUrl()}/x402/endpoints/schema/${parts[0]}/${parts[1]}`);
793
746
  if (res.status === 404) {
794
747
  spin.fail("Endpoint not found");
795
- output.error(`No active endpoint found for ${parts[0]}/${parts[1]}`);
796
- process.exit(1);
748
+ output.fatal(`No active endpoint found for ${parts[0]}/${parts[1]}`);
797
749
  }
798
750
  const result = await handleResponse(res);
799
751
  spin.stop();
@@ -875,9 +827,7 @@ export async function x402SchemaCommand(url, opts = {}) {
875
827
  console.log();
876
828
  }
877
829
  catch (err) {
878
- spin.fail("Schema lookup failed");
879
- output.error(getErrorMessage(err));
880
- process.exit(1);
830
+ output.failWith(spin, "Schema lookup failed", err);
881
831
  }
882
832
  }
883
833
  async function fetchSchemaForInteractive(url) {
@@ -999,19 +949,44 @@ async function fetchSchemaForInteractive(url) {
999
949
  return null;
1000
950
  }
1001
951
  // ── Call ──────────────────────────────────────────────────────────────
952
+ // The caller's --max-payment is the only value ever sent as the payment cap;
953
+ // an endpoint-advertised price may never raise it — fail closed instead.
954
+ // Exported for tests.
955
+ export function checkAdvertisedPrice(advertisedUsd, callerCapUsd) {
956
+ if (advertisedUsd !== null && advertisedUsd > callerCapUsd) {
957
+ return `Endpoint requires $${advertisedUsd} but --max-payment is $${callerCapUsd}. Re-run with a higher --max-payment (up to 10) to allow it.`;
958
+ }
959
+ return null;
960
+ }
961
+ // Advertised prices that are non-positive, unparseable, or token-denominated
962
+ // (non-USD currency) are treated as unknown: only a USD value can safely feed
963
+ // the USD cap clamp or the fail-closed comparison. Exported for tests.
964
+ export function parseAdvertisedPrice(price, currency) {
965
+ const upper = currency.toUpperCase();
966
+ if (upper !== "USDC" && upper !== "USD")
967
+ return null;
968
+ const parsed = parseFloat(price);
969
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
970
+ }
971
+ // The cap we send: never above the caller's --max-payment, and never above the
972
+ // advertised price the user was shown and confirmed. Exported for tests.
973
+ export function outboundMaxPayment(callerCapUsd, advertisedUsd) {
974
+ return advertisedUsd !== null
975
+ ? Math.min(advertisedUsd, callerCapUsd)
976
+ : callerCapUsd;
977
+ }
1002
978
  export async function x402CallCommand(url, opts = {}) {
1003
979
  if (!url) {
1004
- output.error("Usage: bankr x402 call <url> [--method POST] [--body '{}']");
1005
- process.exit(1);
980
+ output.fatal("Usage: bankr x402 call <url> [--method POST] [--body '{}']");
1006
981
  }
1007
982
  let method = opts.method?.toUpperCase() ?? "GET";
1008
983
  const maxPaymentUsd = opts.maxPayment ? parseFloat(opts.maxPayment) : 1;
1009
984
  let requestBody = opts.body;
1010
985
  if (isNaN(maxPaymentUsd) || maxPaymentUsd <= 0 || maxPaymentUsd > 10) {
1011
- output.error("--max-payment must be between 0 and 10 (USD)");
1012
- process.exit(1);
986
+ output.fatal("--max-payment must be between 0 and 10 (USD)");
1013
987
  }
1014
- // Interactive mode: fetch schema and prompt for input values
988
+ // Endpoint-advertised price. Shown at confirm time; the outbound cap is
989
+ // clamped to min(advertised, caller's maxPaymentUsd) — see outboundMaxPayment.
1015
990
  let resolvedPrice = null;
1016
991
  if (opts.interactive) {
1017
992
  const schema = await fetchSchemaForInteractive(url);
@@ -1025,7 +1000,11 @@ export async function x402CallCommand(url, opts = {}) {
1025
1000
  console.log(` ${chalk.dim("Price")} ${chalk.green(`$${schema.price} ${schema.currency}`)}`);
1026
1001
  console.log();
1027
1002
  // Use the endpoint's actual price and preferred method
1028
- resolvedPrice = parseFloat(schema.price);
1003
+ resolvedPrice = parseAdvertisedPrice(schema.price, schema.currency);
1004
+ const capError = checkAdvertisedPrice(resolvedPrice, maxPaymentUsd);
1005
+ if (capError) {
1006
+ output.fatal(capError);
1007
+ }
1029
1008
  if (schema.methods.length > 0) {
1030
1009
  method = schema.methods.includes("POST") ? "POST" : schema.methods[0];
1031
1010
  }
@@ -1041,7 +1020,7 @@ export async function x402CallCommand(url, opts = {}) {
1041
1020
  : `${key} ${chalk.dim(`(${typedProp.type}, optional)`)}`;
1042
1021
  const hint = typedProp.description ?? "";
1043
1022
  // Enum fields → select prompt
1044
- if (typedProp.enum && typedProp.enum.length > 0) {
1023
+ if (typedProp.enum?.length) {
1045
1024
  const selected = await select({
1046
1025
  message: `${label}${hint ? ` — ${chalk.dim(hint)}` : ""}`,
1047
1026
  choices: typedProp.enum.map((v) => ({
@@ -1066,8 +1045,7 @@ export async function x402CallCommand(url, opts = {}) {
1066
1045
  if (typedProp.type === "number" || typedProp.type === "integer") {
1067
1046
  const num = Number(answer);
1068
1047
  if (isNaN(num)) {
1069
- output.error(`Invalid number for ${key}: "${answer}"`);
1070
- process.exit(1);
1048
+ output.fatal(`Invalid number for ${key}: "${answer}"`);
1071
1049
  }
1072
1050
  values[key] = num;
1073
1051
  }
@@ -1098,6 +1076,7 @@ export async function x402CallCommand(url, opts = {}) {
1098
1076
  // No schema available — fallback: prompt for method and body
1099
1077
  output.warn("No schema found for this endpoint. Falling back to manual input.");
1100
1078
  console.log();
1079
+ const manualMethodDefault = pickDefault(method, ["GET", "POST", "PUT", "DELETE"]) ?? "GET";
1101
1080
  method = await select({
1102
1081
  message: "HTTP method",
1103
1082
  choices: [
@@ -1106,7 +1085,7 @@ export async function x402CallCommand(url, opts = {}) {
1106
1085
  { name: "PUT", value: "PUT" },
1107
1086
  { name: "DELETE", value: "DELETE" },
1108
1087
  ],
1109
- default: method,
1088
+ default: manualMethodDefault,
1110
1089
  theme: output.bankrTheme,
1111
1090
  });
1112
1091
  if (method === "POST" || method === "PUT") {
@@ -1120,8 +1099,7 @@ export async function x402CallCommand(url, opts = {}) {
1120
1099
  requestBody = bodyInput;
1121
1100
  }
1122
1101
  catch {
1123
- output.error("Invalid JSON body");
1124
- process.exit(1);
1102
+ output.fatal("Invalid JSON body");
1125
1103
  }
1126
1104
  }
1127
1105
  }
@@ -1144,13 +1122,21 @@ export async function x402CallCommand(url, opts = {}) {
1144
1122
  }
1145
1123
  console.log();
1146
1124
  }
1147
- // Non-interactive: probe the endpoint to show the actual price before confirming
1148
- if (!opts.interactive && !opts.yes && resolvedPrice === null) {
1125
+ // Probe the endpoint to show the actual price before confirming. Skipped in
1126
+ // non-interactive mode, where the confirm is suppressed — --ni matches --yes.
1127
+ if (!opts.interactive &&
1128
+ !opts.yes &&
1129
+ !output.isNonInteractive() &&
1130
+ resolvedPrice === null) {
1149
1131
  const spin = output.spinner("Checking endpoint price...");
1150
1132
  const schema = await fetchSchemaForInteractive(url);
1151
1133
  spin.stop();
1152
1134
  if (schema) {
1153
- resolvedPrice = parseFloat(schema.price);
1135
+ resolvedPrice = parseAdvertisedPrice(schema.price, schema.currency);
1136
+ const capError = checkAdvertisedPrice(resolvedPrice, maxPaymentUsd);
1137
+ if (capError) {
1138
+ output.fatal(capError);
1139
+ }
1154
1140
  }
1155
1141
  }
1156
1142
  if (!opts.yes && !opts.interactive && !output.isNonInteractive()) {
@@ -1185,7 +1171,7 @@ export async function x402CallCommand(url, opts = {}) {
1185
1171
  url,
1186
1172
  method,
1187
1173
  body: requestBody,
1188
- maxPaymentUsd: resolvedPrice ?? maxPaymentUsd,
1174
+ maxPaymentUsd: outboundMaxPayment(maxPaymentUsd, resolvedPrice),
1189
1175
  }),
1190
1176
  });
1191
1177
  const result = await handleResponse(res);
@@ -1195,8 +1181,7 @@ export async function x402CallCommand(url, opts = {}) {
1195
1181
  return;
1196
1182
  }
1197
1183
  if (!result.success) {
1198
- output.error(result.error ?? "Call failed");
1199
- process.exit(1);
1184
+ output.fatal(result.error ?? "Call failed");
1200
1185
  }
1201
1186
  // Show endpoint schema info (Bankr-hosted endpoints only)
1202
1187
  if (result.endpointSchema) {
@@ -1229,17 +1214,14 @@ export async function x402CallCommand(url, opts = {}) {
1229
1214
  console.log();
1230
1215
  }
1231
1216
  catch (err) {
1232
- spin.fail("Call failed");
1233
- output.error(getErrorMessage(err));
1234
- process.exit(1);
1217
+ output.failWith(spin, "Call failed", err);
1235
1218
  }
1236
1219
  }
1237
1220
  // ── Search ────────────────────────────────────────────────────────────
1238
1221
  export async function x402SearchCommand(queryParts, opts = {}) {
1239
1222
  const query = queryParts.join(" ").trim();
1240
1223
  if (!query) {
1241
- output.error("Usage: bankr x402 search <query>");
1242
- process.exit(1);
1224
+ output.fatal("Usage: bankr x402 search <query>");
1243
1225
  }
1244
1226
  const spin = output.spinner("Searching services...");
1245
1227
  try {
@@ -1276,9 +1258,7 @@ export async function x402SearchCommand(queryParts, opts = {}) {
1276
1258
  }
1277
1259
  }
1278
1260
  catch (err) {
1279
- spin.fail("Search failed");
1280
- output.error(getErrorMessage(err));
1281
- process.exit(1);
1261
+ output.failWith(spin, "Search failed", err);
1282
1262
  }
1283
1263
  }
1284
1264
  //# sourceMappingURL=x402.js.map
package/dist/lib/api.d.ts CHANGED
@@ -43,6 +43,8 @@ export interface JobStatusResponse {
43
43
  richData?: unknown[];
44
44
  cancellable?: boolean;
45
45
  }
46
+ export declare function authHeaders(): Record<string, string>;
47
+ export declare function handleResponse<T>(res: Response): Promise<T>;
46
48
  export interface ApiErrorBody {
47
49
  error: string;
48
50
  message: string;
@@ -69,7 +71,11 @@ export declare function getUserInfo(): Promise<UserInfoResponse>;
69
71
  export interface TokenBalanceInfo {
70
72
  network: string;
71
73
  token: {
72
- balance: number;
74
+ /**
75
+ * Wire shape of `GET /wallet/portfolio`: an exact decimal string.
76
+ * Coerce with `Number(...)` before arithmetic or comparison.
77
+ */
78
+ balance: string;
73
79
  balanceUSD: number;
74
80
  baseToken: {
75
81
  name: string;
@@ -192,7 +198,7 @@ export interface SubmitResponse {
192
198
  export declare function submit(request: SubmitRequest): Promise<SubmitResponse>;
193
199
  export type FeeRecipientType = "wallet" | "x" | "farcaster" | "ens";
194
200
  /** Chains the deploy endpoint accepts (mirrors the API's zod enum). */
195
- export type LaunchChain = "base" | "robinhood";
201
+ export type LaunchChain = "base" | "robinhood" | "arbitrum";
196
202
  export interface DeployTokenRequest {
197
203
  tokenName: string;
198
204
  tokenSymbol?: string;
@@ -206,6 +212,11 @@ export interface DeployTokenRequest {
206
212
  value: string;
207
213
  };
208
214
  simulateOnly?: boolean;
215
+ /** Turn off the default 15% creator vesting (100% of supply sold into the pool). */
216
+ disableVesting?: boolean;
217
+ /** Collect all creator fees in the pool's quote token (e.g. WETH) instead of
218
+ * a mix of the launched token and the quote token. */
219
+ quoteOnlyFees?: boolean;
209
220
  }
210
221
  export interface DeployTokenResponse {
211
222
  success: boolean;
@@ -225,7 +236,7 @@ export interface CreatorFeesTokenResult {
225
236
  tokenAddress: string;
226
237
  name: string;
227
238
  symbol: string;
228
- /** Launch chain ("base" | "robinhood"); absent on older API versions → base. */
239
+ /** Launch chain ("base" | "robinhood" | "arbitrum"); absent on older API versions → base. */
229
240
  chain?: string;
230
241
  poolId: string;
231
242
  initializer?: string;
@@ -264,6 +275,9 @@ export interface BeneficiaryFeesResponse {
264
275
  totalLaunches: number;
265
276
  poolsWithShares: number;
266
277
  tokens: CreatorFeesTokenResult[];
278
+ /** Server stopped the scan early — more claimable fees may exist. */
279
+ truncated?: boolean;
280
+ scanned?: number;
267
281
  }
268
282
  export interface DopplerClaimTxResponse {
269
283
  to: string;