brainblast 0.7.5 → 0.7.6

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.
Files changed (50) hide show
  1. package/dist/{chunk-HFYBK2VA.js → chunk-A3U6JDMN.js} +64 -12
  2. package/dist/{chunk-S7X53LWF.js → chunk-CSYGLMZR.js} +19 -19
  3. package/dist/{chunk-V4XS5DKD.js → chunk-Q5DMQN67.js} +19 -5
  4. package/dist/cli.js +60 -24
  5. package/dist/feeConfigs-S4E5GQ7Q.js +19 -0
  6. package/dist/index.d.ts +21 -13
  7. package/dist/index.js +19 -15
  8. package/dist/{mcp-LITBQHBF.js → mcp-O45PSOVU.js} +1 -1
  9. package/dist/packs/README.md +33 -0
  10. package/dist/packs/jito-bundle-zero-tip/README.md +33 -0
  11. package/dist/packs/jito-bundle-zero-tip/brainblast-pack.yaml +5 -0
  12. package/dist/packs/jito-bundle-zero-tip/fixtures/jito-bundle-zero-tip/fixed/bundle.ts +13 -0
  13. package/dist/packs/jito-bundle-zero-tip/fixtures/jito-bundle-zero-tip/vulnerable/bundle.ts +16 -0
  14. package/dist/packs/jito-bundle-zero-tip/rules/jito-bundle-zero-tip.yaml +54 -0
  15. package/dist/packs/jupiter-quote-zero-slippage/brainblast-pack.yaml +5 -0
  16. package/dist/packs/jupiter-quote-zero-slippage/fixtures/jupiter-quote-zero-slippage/fixed/arb.ts +17 -0
  17. package/dist/packs/jupiter-quote-zero-slippage/fixtures/jupiter-quote-zero-slippage/vulnerable/arb.ts +17 -0
  18. package/dist/packs/jupiter-quote-zero-slippage/rules/jupiter-quote-zero-slippage.yaml +51 -0
  19. package/dist/packs/metaplex-nft-royalty-zero/README.md +39 -0
  20. package/dist/packs/metaplex-nft-royalty-zero/brainblast-pack.yaml +5 -0
  21. package/dist/packs/metaplex-nft-royalty-zero/fixtures/metaplex-nft-royalty-zero/fixed/mint.ts +11 -0
  22. package/dist/packs/metaplex-nft-royalty-zero/fixtures/metaplex-nft-royalty-zero/vulnerable/mint.ts +11 -0
  23. package/dist/packs/metaplex-nft-royalty-zero/rules/metaplex-nft-royalty-zero.yaml +39 -0
  24. package/dist/packs/meteora-dlmm-zero-min-out/README.md +32 -0
  25. package/dist/packs/meteora-dlmm-zero-min-out/brainblast-pack.yaml +5 -0
  26. package/dist/packs/meteora-dlmm-zero-min-out/fixtures/meteora-dlmm-zero-min-out/fixed/swap.ts +19 -0
  27. package/dist/packs/meteora-dlmm-zero-min-out/fixtures/meteora-dlmm-zero-min-out/vulnerable/swap.ts +19 -0
  28. package/dist/packs/meteora-dlmm-zero-min-out/rules/meteora-dlmm-zero-min-out.yaml +47 -0
  29. package/dist/packs/pyth-price-unchecked-staleness/README.md +34 -0
  30. package/dist/packs/pyth-price-unchecked-staleness/brainblast-pack.yaml +5 -0
  31. package/dist/packs/pyth-price-unchecked-staleness/fixtures/pyth-price-unchecked-staleness/fixed/price.ts +14 -0
  32. package/dist/packs/pyth-price-unchecked-staleness/fixtures/pyth-price-unchecked-staleness/vulnerable/price.ts +14 -0
  33. package/dist/packs/pyth-price-unchecked-staleness/rules/pyth-price-unchecked-staleness.yaml +47 -0
  34. package/dist/packs/raydium-compute-zero-slippage/README.md +43 -0
  35. package/dist/packs/raydium-compute-zero-slippage/brainblast-pack.yaml +5 -0
  36. package/dist/packs/raydium-compute-zero-slippage/fixtures/raydium-compute-zero-slippage/fixed/swap.ts +12 -0
  37. package/dist/packs/raydium-compute-zero-slippage/fixtures/raydium-compute-zero-slippage/vulnerable/swap.ts +12 -0
  38. package/dist/packs/raydium-compute-zero-slippage/rules/raydium-compute-zero-slippage.yaml +40 -0
  39. package/dist/packs/solana-sendtx-unconfirmed/README.md +34 -0
  40. package/dist/packs/solana-sendtx-unconfirmed/brainblast-pack.yaml +5 -0
  41. package/dist/packs/solana-sendtx-unconfirmed/fixtures/solana-sendtx-unconfirmed/fixed/payment.ts +10 -0
  42. package/dist/packs/solana-sendtx-unconfirmed/fixtures/solana-sendtx-unconfirmed/vulnerable/payment.ts +10 -0
  43. package/dist/packs/solana-sendtx-unconfirmed/rules/solana-sendtx-unconfirmed.yaml +40 -0
  44. package/dist/packs/spl-transfer-not-checked-in-payout/brainblast-pack.yaml +5 -0
  45. package/dist/packs/spl-transfer-not-checked-in-payout/fixtures/spl-transfer-not-checked-in-payout/fixed/payout-processor.ts +29 -0
  46. package/dist/packs/spl-transfer-not-checked-in-payout/fixtures/spl-transfer-not-checked-in-payout/vulnerable/payout-processor.ts +22 -0
  47. package/dist/packs/spl-transfer-not-checked-in-payout/rules/spl-transfer-not-checked-in-payout.yaml +49 -0
  48. package/dist/rules/metaplex-seller-fee-zero.yaml +2 -2
  49. package/package.json +1 -1
  50. package/dist/tokenEconomics-HBF3DYNH.js +0 -19
@@ -7,7 +7,7 @@ import {
7
7
  loadPack,
8
8
  resolveRules,
9
9
  walk
10
- } from "./chunk-V4XS5DKD.js";
10
+ } from "./chunk-Q5DMQN67.js";
11
11
 
12
12
  // src/costAnalysis.ts
13
13
  import { Project, SyntaxKind } from "ts-morph";
@@ -521,12 +521,62 @@ function validatePack(dir) {
521
521
  return { manifest, rules, ruleResults, ok };
522
522
  }
523
523
 
524
+ // src/bundledPacks.ts
525
+ import { existsSync as existsSync2, readdirSync, statSync, readFileSync as readFileSync2 } from "fs";
526
+ import { join as join2 } from "path";
527
+ import { fileURLToPath } from "url";
528
+ import { parse } from "yaml";
529
+ function packsRoot() {
530
+ const here = fileURLToPath(new URL(".", import.meta.url));
531
+ const candidates = [
532
+ join2(here, "packs"),
533
+ // dist/packs (npm — copied by postbuild)
534
+ join2(here, "..", "..", "..", "packs"),
535
+ // src/../../../packs (dev — repo root)
536
+ join2(here, "..", "packs")
537
+ // fallback
538
+ ];
539
+ return candidates.find((p) => existsSync2(p)) ?? null;
540
+ }
541
+ function listBundledPacks() {
542
+ const root = packsRoot();
543
+ if (!root) return [];
544
+ const out = [];
545
+ for (const entry of readdirSync(root)) {
546
+ const dir = join2(root, entry);
547
+ const manifestPath = join2(dir, PACK_MANIFEST_FILE);
548
+ let isDir = false;
549
+ try {
550
+ isDir = statSync(dir).isDirectory();
551
+ } catch {
552
+ continue;
553
+ }
554
+ if (!isDir || !existsSync2(manifestPath)) continue;
555
+ try {
556
+ const manifest = parse(readFileSync2(manifestPath, "utf8"));
557
+ if (manifest?.id) out.push({ id: manifest.id, dir, manifest });
558
+ } catch {
559
+ }
560
+ }
561
+ return out.sort((a, b) => a.id.localeCompare(b.id));
562
+ }
563
+ function resolveBundledPackToken(token) {
564
+ const packs = listBundledPacks();
565
+ const exact = packs.find((p) => p.id === token);
566
+ if (exact) return exact.dir;
567
+ const lead = packs.filter((p) => p.id === token || p.id.startsWith(token + "-"));
568
+ if (lead.length === 1) return lead[0].dir;
569
+ const sub = packs.filter((p) => p.id.includes(token));
570
+ if (sub.length === 1) return sub[0].dir;
571
+ return null;
572
+ }
573
+
524
574
  // src/telemetry.ts
525
575
  import { createHash, randomUUID } from "crypto";
526
- import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
576
+ import { appendFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
527
577
  import { execFileSync } from "child_process";
528
578
  import { homedir } from "os";
529
- import { dirname, join as join2, resolve } from "path";
579
+ import { dirname, join as join3, resolve } from "path";
530
580
  function sha256Hex(s) {
531
581
  return createHash("sha256").update(s).digest("hex");
532
582
  }
@@ -534,20 +584,20 @@ function isTelemetryEnabled(targetDir) {
534
584
  const env = process.env.BRAINBLAST_TELEMETRY;
535
585
  if (env === "1" || env === "true") return true;
536
586
  if (env === "0" || env === "false") return false;
537
- const configPath = join2(targetDir, ".agent-research", "config.json");
538
- if (!existsSync2(configPath)) return false;
587
+ const configPath = join3(targetDir, ".agent-research", "config.json");
588
+ if (!existsSync3(configPath)) return false;
539
589
  try {
540
- const cfg = JSON.parse(readFileSync2(configPath, "utf8"));
590
+ const cfg = JSON.parse(readFileSync3(configPath, "utf8"));
541
591
  return cfg?.telemetry === true;
542
592
  } catch {
543
593
  return false;
544
594
  }
545
595
  }
546
596
  function getUserHash() {
547
- const idPath = join2(homedir(), ".brainblast", "telemetry-id");
597
+ const idPath = join3(homedir(), ".brainblast", "telemetry-id");
548
598
  let id;
549
- if (existsSync2(idPath)) {
550
- id = readFileSync2(idPath, "utf8").trim();
599
+ if (existsSync3(idPath)) {
600
+ id = readFileSync3(idPath, "utf8").trim();
551
601
  } else {
552
602
  id = randomUUID();
553
603
  mkdirSync2(dirname(idPath), { recursive: true });
@@ -569,15 +619,15 @@ function getRepoHash(targetDir) {
569
619
  return sha256Hex(key).slice(0, 16);
570
620
  }
571
621
  function telemetryFilePath(targetDir) {
572
- return join2(targetDir, ".agent-research", "telemetry.ndjson");
622
+ return join3(targetDir, ".agent-research", "telemetry.ndjson");
573
623
  }
574
624
  var DEFAULT_REGISTRY_URL = "https://registry.brainblast.tech";
575
625
  async function submitTelemetry(targetDir, registryUrl = process.env.BRAINBLAST_REGISTRY_URL || DEFAULT_REGISTRY_URL) {
576
626
  const file = telemetryFilePath(targetDir);
577
- if (!existsSync2(file)) {
627
+ if (!existsSync3(file)) {
578
628
  return { submitted: 0, accepted: 0, rejected: 0, graduations: [] };
579
629
  }
580
- const events = readFileSync2(file, "utf8").trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
630
+ const events = readFileSync3(file, "utf8").trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
581
631
  if (events.length === 0) {
582
632
  return { submitted: 0, accepted: 0, rejected: 0, graduations: [] };
583
633
  }
@@ -622,6 +672,8 @@ export {
622
672
  applyDiffToFile,
623
673
  initPack,
624
674
  validatePack,
675
+ listBundledPacks,
676
+ resolveBundledPackToken,
625
677
  isTelemetryEnabled,
626
678
  getUserHash,
627
679
  getRepoHash,
@@ -1,5 +1,5 @@
1
- // src/tokenEconomics.ts
2
- var ECONOMIC_PATTERNS = [
1
+ // src/feeConfigs.ts
2
+ var FEE_CONFIGS = [
3
3
  {
4
4
  id: "metaplex-seller-fee",
5
5
  category: "royalty",
@@ -46,18 +46,18 @@ var ECONOMIC_PATTERNS = [
46
46
  call: "initialize / configureReward (varies)",
47
47
  field: "rewardRate / emissionsPerSecond",
48
48
  whatZeroMeans: "A reward-rate or emissions field omitted/zeroed \u2192 stakers and LPs accrue nothing while the pool looks live. A silent, ongoing zero-yield misconfiguration.",
49
- fix: "Set the reward-rate field explicitly and assert it is non-zero in your deploy script; add a project-local economic-value-zero-or-missing rule for your SDK's call shape.",
49
+ fix: "Set the reward-rate field explicitly and assert it is non-zero in your deploy script; add a project-local fee-configs-zero-or-missing rule for your SDK's call shape.",
50
50
  ruleId: null,
51
51
  status: "advisory"
52
52
  }
53
53
  ];
54
- function getEconomicPattern(id) {
55
- return ECONOMIC_PATTERNS.find((e) => e.id === id || e.ruleId === id);
54
+ function getFeeConfig(id) {
55
+ return FEE_CONFIGS.find((e) => e.id === id || e.ruleId === id);
56
56
  }
57
- function economicPatternsByCategory(cat) {
58
- return ECONOMIC_PATTERNS.filter((e) => e.category === cat);
57
+ function feeConfigsByCategory(cat) {
58
+ return FEE_CONFIGS.filter((e) => e.category === cat);
59
59
  }
60
- function enforcedCount(patterns = ECONOMIC_PATTERNS) {
60
+ function enforcedCount(patterns = FEE_CONFIGS) {
61
61
  return patterns.filter((e) => e.status === "enforced").length;
62
62
  }
63
63
  var CATEGORY_LABEL = {
@@ -65,8 +65,8 @@ var CATEGORY_LABEL = {
65
65
  fee: "Fees",
66
66
  reward: "Reward distribution"
67
67
  };
68
- function renderEconomicsMd(patterns = ECONOMIC_PATTERNS) {
69
- const L = ["## Token Economics \u2014 silent zero-revenue class\n"];
68
+ function renderFeeConfigsMd(patterns = FEE_CONFIGS) {
69
+ const L = ["## Fee Configs \u2014 silent zero-revenue class\n"];
70
70
  L.push(
71
71
  "The Bags exploit generalized: a revenue-bearing field that, if omitted or zeroed, silently collects nothing \u2014 forever. Watch these across every protocol that touches fees, royalties, or rewards.\n"
72
72
  );
@@ -91,9 +91,9 @@ function renderEconomicsMd(patterns = ECONOMIC_PATTERNS) {
91
91
  }
92
92
  return L.join("\n");
93
93
  }
94
- function renderEconomicsText(patterns = ECONOMIC_PATTERNS) {
94
+ function renderFeeConfigsText(patterns = FEE_CONFIGS) {
95
95
  const L = [];
96
- L.push("\u2500\u2500 Token Economics \u2014 silent zero-revenue class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
96
+ L.push("\u2500\u2500 Fee Configs \u2014 silent zero-revenue class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
97
97
  L.push(" fields that default to zero and silently collect nothing");
98
98
  L.push("");
99
99
  for (const e of patterns) {
@@ -106,7 +106,7 @@ function renderEconomicsText(patterns = ECONOMIC_PATTERNS) {
106
106
  L.push(` ${enforcedCount(patterns)}/${patterns.length} enforced by a bundled rule`);
107
107
  return L.join("\n");
108
108
  }
109
- function renderEconomicDetailText(e) {
109
+ function renderFeeConfigDetailText(e) {
110
110
  const L = [];
111
111
  L.push(`\u2500\u2500 ${e.sdk} \u2014 ${e.field} \u2500\u2500`);
112
112
  L.push(` category: ${CATEGORY_LABEL[e.category]}`);
@@ -119,11 +119,11 @@ function renderEconomicDetailText(e) {
119
119
  }
120
120
 
121
121
  export {
122
- ECONOMIC_PATTERNS,
123
- getEconomicPattern,
124
- economicPatternsByCategory,
122
+ FEE_CONFIGS,
123
+ getFeeConfig,
124
+ feeConfigsByCategory,
125
125
  enforcedCount,
126
- renderEconomicsMd,
127
- renderEconomicsText,
128
- renderEconomicDetailText
126
+ renderFeeConfigsMd,
127
+ renderFeeConfigsText,
128
+ renderFeeConfigDetailText
129
129
  };
@@ -430,6 +430,19 @@ var objectArgPropertyLiteralEquals = (c, p) => {
430
430
 
431
431
  // src/checkers/objectArgPropertyForbiddenLiteral.ts
432
432
  import { SyntaxKind as SyntaxKind7 } from "ts-morph";
433
+ function isBnWrappedZero(init) {
434
+ const isNewOrCall = init.getKind() === SyntaxKind7.NewExpression || init.getKind() === SyntaxKind7.CallExpression;
435
+ if (!isNewOrCall) return false;
436
+ const calleeText = init.getExpression?.()?.getText?.() ?? "";
437
+ if (!/(^|\.)bn$/i.test(calleeText)) return false;
438
+ const args = init.getArguments?.() ?? [];
439
+ if (args.length !== 1) return false;
440
+ const a = args[0];
441
+ const k = a.getKind();
442
+ if (k === SyntaxKind7.NumericLiteral) return Number(a.getLiteralValue()) === 0;
443
+ if (k === SyntaxKind7.StringLiteral) return a.getLiteralValue() === "0";
444
+ return false;
445
+ }
433
446
  var objectArgPropertyForbiddenLiteral = (c, p) => {
434
447
  const calls = c.fn.getDescendantsOfKind(SyntaxKind7.CallExpression).filter((ce2) => {
435
448
  const expr = ce2.getExpression();
@@ -469,7 +482,8 @@ var objectArgPropertyForbiddenLiteral = (c, p) => {
469
482
  const kind = init.getKind();
470
483
  const text = init.getText();
471
484
  const forbidden = JSON.stringify(p.forbiddenValue);
472
- const isForbidden = (kind === SyntaxKind7.NumericLiteral || kind === SyntaxKind7.StringLiteral) && (text === forbidden || text === String(p.forbiddenValue));
485
+ const isLiteralForbidden = (kind === SyntaxKind7.NumericLiteral || kind === SyntaxKind7.StringLiteral) && (text === forbidden || text === String(p.forbiddenValue));
486
+ const isForbidden = isLiteralForbidden || p.forbiddenValue === 0 && isBnWrappedZero(init);
473
487
  if (isForbidden) {
474
488
  return {
475
489
  result: "fail",
@@ -1050,7 +1064,7 @@ function anchorCpiUnverifiedProgram(c, p) {
1050
1064
  };
1051
1065
  }
1052
1066
 
1053
- // src/checkers/economicValueZeroOrMissing.ts
1067
+ // src/checkers/feeConfigsZeroOrMissing.ts
1054
1068
  import { SyntaxKind as SyntaxKind12 } from "ts-morph";
1055
1069
  function callName6(call) {
1056
1070
  const exp = call.getExpression();
@@ -1079,14 +1093,14 @@ function isLiteralZero(expr) {
1079
1093
  if (big) return /^0n?$/.test(big.getText().replace(/_/g, ""));
1080
1094
  return false;
1081
1095
  }
1082
- var economicValueZeroOrMissing = (c, p) => {
1096
+ var feeConfigsZeroOrMissing = (c, p) => {
1083
1097
  const names = Array.isArray(p.calls) ? p.calls : [p.calls];
1084
1098
  const field = p.field;
1085
1099
  const calls = c.fn.getDescendantsOfKind(SyntaxKind12.CallExpression).filter((x) => names.includes(callName6(x)));
1086
1100
  if (calls.length === 0) {
1087
1101
  return {
1088
1102
  result: "cant_tell",
1089
- detail: p.absentDetail ?? `No call to ${names.join("/")} in '${c.fnName}'; economic-value check does not apply.`
1103
+ detail: p.absentDetail ?? `No call to ${names.join("/")} in '${c.fnName}'; fee-configs check does not apply.`
1090
1104
  };
1091
1105
  }
1092
1106
  for (const call of calls) {
@@ -1138,7 +1152,7 @@ var registry = {
1138
1152
  "anchor-forbidden-account-type": anchorForbiddenAccountType,
1139
1153
  "anchor-body-call-pattern": anchorBodyCallPattern,
1140
1154
  "anchor-cpi-unverified-program": anchorCpiUnverifiedProgram,
1141
- "economic-value-zero-or-missing": economicValueZeroOrMissing
1155
+ "fee-configs-zero-or-missing": feeConfigsZeroOrMissing
1142
1156
  };
1143
1157
  function runChecker(kind, c, params) {
1144
1158
  const fn = registry[kind];
package/dist/cli.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  initPack,
8
8
  isTelemetryEnabled,
9
9
  lamportsToSol,
10
+ listBundledPacks,
10
11
  parseDiff,
11
12
  recordGraduationEvents,
12
13
  renderCostReportMd,
@@ -14,11 +15,12 @@ import {
14
15
  renderExploitsMd,
15
16
  renderExploitsText,
16
17
  rentExemptMinimum,
18
+ resolveBundledPackToken,
17
19
  startWatch,
18
20
  submitTelemetry,
19
21
  telemetryFilePath,
20
22
  validatePack
21
- } from "./chunk-HFYBK2VA.js";
23
+ } from "./chunk-A3U6JDMN.js";
22
24
  import {
23
25
  renderTrustGraphMd
24
26
  } from "./chunk-QMJEZ6NO.js";
@@ -32,7 +34,7 @@ import {
32
34
  audit,
33
35
  getChangedRanges,
34
36
  resolveRules
35
- } from "./chunk-V4XS5DKD.js";
37
+ } from "./chunk-Q5DMQN67.js";
36
38
  import "./chunk-2XJORJPQ.js";
37
39
  import "./chunk-O5Z4ZJHC.js";
38
40
  import "./chunk-XSVQSK53.js";
@@ -42,7 +44,7 @@ import {
42
44
  import "./chunk-3RG5ZIWI.js";
43
45
 
44
46
  // src/cli.ts
45
- import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
47
+ import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
46
48
  import { join as join3 } from "path";
47
49
 
48
50
  // src/memory.ts
@@ -469,7 +471,16 @@ function parsePackDirs(argv) {
469
471
  if (idx < 0) return [];
470
472
  const value = argv[idx + 1];
471
473
  if (!value || value.startsWith("--")) return [];
472
- return value.split(",").map((s) => s.trim()).filter(Boolean);
474
+ const tokens = value.split(",").map((s) => s.trim()).filter(Boolean);
475
+ return tokens.map((t) => {
476
+ if (existsSync3(join3(t, "brainblast-pack.yaml"))) return t;
477
+ const resolved = resolveBundledPackToken(t);
478
+ if (resolved) return resolved;
479
+ console.error(
480
+ `brainblast: --packs '${t}' is not a known bundled pack or a pack directory. Run 'brainblast packs' to list bundled packs.`
481
+ );
482
+ process.exit(2);
483
+ });
473
484
  }
474
485
  if (args[0] === "diff") {
475
486
  await runDiff(args.slice(1));
@@ -480,7 +491,7 @@ if (args[0] === "drift") {
480
491
  process.exit(0);
481
492
  }
482
493
  if (args[0] === "mcp") {
483
- const { startMcpServer } = await import("./mcp-LITBQHBF.js");
494
+ const { startMcpServer } = await import("./mcp-O45PSOVU.js");
484
495
  await startMcpServer();
485
496
  process.exit(0);
486
497
  }
@@ -492,6 +503,10 @@ if (args[0] === "pack") {
492
503
  runPack(args.slice(1));
493
504
  process.exit(0);
494
505
  }
506
+ if (args[0] === "packs") {
507
+ runPacks(args.slice(1));
508
+ process.exit(0);
509
+ }
495
510
  if (args[0] === "telemetry") {
496
511
  await runTelemetry(args.slice(1));
497
512
  process.exit(0);
@@ -567,8 +582,8 @@ if (args[0] === "oracle") {
567
582
  await runOracle(args.slice(1));
568
583
  process.exit(0);
569
584
  }
570
- if (args[0] === "economics") {
571
- await runEconomics(args.slice(1));
585
+ if (args[0] === "fee-configs") {
586
+ await runFeeConfigs(args.slice(1));
572
587
  process.exit(0);
573
588
  }
574
589
  if (args[0] === "fix") {
@@ -708,44 +723,44 @@ function runDeployPlan(argv) {
708
723
  writeFileSync2(mdPath, renderDeployPlanMd(plan));
709
724
  console.log(` deploy plan: ${mdPath}`);
710
725
  }
711
- async function runEconomics(argv) {
726
+ async function runFeeConfigs(argv) {
712
727
  const {
713
- ECONOMIC_PATTERNS,
714
- getEconomicPattern,
715
- renderEconomicsText,
716
- renderEconomicsMd,
717
- renderEconomicDetailText
718
- } = await import("./tokenEconomics-HBF3DYNH.js");
728
+ FEE_CONFIGS,
729
+ getFeeConfig,
730
+ renderFeeConfigsText,
731
+ renderFeeConfigsMd,
732
+ renderFeeConfigDetailText
733
+ } = await import("./feeConfigs-S4E5GQ7Q.js");
719
734
  if (argv.includes("--help") || argv.includes("-h")) {
720
- console.log("usage: brainblast economics [id] [--json]");
721
- console.log(" Token Economics Validator: the silent zero-revenue class (fees, royalties,");
735
+ console.log("usage: brainblast fee-configs [id] [--json]");
736
+ console.log(" Fee Config Validator: the silent zero-revenue class (fees, royalties,");
722
737
  console.log(" rewards) \u2014 fields that default to zero and quietly collect nothing. Pass an");
723
738
  console.log(" id to see one in detail. Known ids:");
724
- console.log(` ${ECONOMIC_PATTERNS.map((e) => e.id).join(", ")}`);
739
+ console.log(` ${FEE_CONFIGS.map((e) => e.id).join(", ")}`);
725
740
  process.exit(0);
726
741
  }
727
742
  const json = argv.includes("--json");
728
743
  const id = argv.find((a) => !a.startsWith("--"));
729
744
  if (id) {
730
- const e = getEconomicPattern(id);
745
+ const e = getFeeConfig(id);
731
746
  if (!e) {
732
- console.error(`error: no economic pattern '${id}'. Known: ${ECONOMIC_PATTERNS.map((x) => x.id).join(", ")}`);
747
+ console.error(`error: no fee-config '${id}'. Known: ${FEE_CONFIGS.map((x) => x.id).join(", ")}`);
733
748
  process.exit(2);
734
749
  }
735
750
  if (json) console.log(JSON.stringify(e, null, 2));
736
- else console.log(renderEconomicDetailText(e));
751
+ else console.log(renderFeeConfigDetailText(e));
737
752
  return;
738
753
  }
739
754
  if (json) {
740
- console.log(JSON.stringify(ECONOMIC_PATTERNS, null, 2));
755
+ console.log(JSON.stringify(FEE_CONFIGS, null, 2));
741
756
  return;
742
757
  }
743
- console.log(renderEconomicsText());
758
+ console.log(renderFeeConfigsText());
744
759
  const outDir2 = join3(process.cwd(), ".agent-research");
745
760
  mkdirSync2(outDir2, { recursive: true });
746
- writeFileSync2(join3(outDir2, "token-economics.md"), renderEconomicsMd());
761
+ writeFileSync2(join3(outDir2, "fee-configs.md"), renderFeeConfigsMd());
747
762
  console.log(`
748
- catalog: ${join3(outDir2, "token-economics.md")}`);
763
+ catalog: ${join3(outDir2, "fee-configs.md")}`);
749
764
  }
750
765
  async function runOracle(argv) {
751
766
  if (argv.includes("--help") || argv.includes("-h") || argv.filter((a) => !a.startsWith("--")).length === 0) {
@@ -822,6 +837,27 @@ function runExploits(argv) {
822
837
  console.log(`
823
838
  database: ${mdPath}`);
824
839
  }
840
+ function runPacks(argv) {
841
+ const packs = listBundledPacks();
842
+ if (argv.includes("--json")) {
843
+ console.log(JSON.stringify(packs.map((p) => ({ ...p.manifest, dir: p.dir })), null, 2));
844
+ return;
845
+ }
846
+ if (packs.length === 0) {
847
+ console.log("No bundled protocol packs found.");
848
+ return;
849
+ }
850
+ console.log("Protocol Pack Library \u2014 opt into the exact stack you build on:\n");
851
+ console.log(" brainblast --packs <name>[,<name>...] .\n");
852
+ for (const p of packs) {
853
+ const lead = p.id.split("-")[0];
854
+ const shortName = resolveBundledPackToken(lead) === p.dir ? lead : p.id;
855
+ console.log(` ${shortName.padEnd(12)} ${p.manifest.name}`);
856
+ if (p.manifest.description) console.log(` ${" ".repeat(12)} ${p.manifest.description}`);
857
+ console.log("");
858
+ }
859
+ console.log(`${packs.length} pack(s). Each ships RED\u2192GREEN fixtures; run 'brainblast pack validate <dir>' to verify.`);
860
+ }
825
861
  function runPack(argv) {
826
862
  const sub = argv[0];
827
863
  if (sub === "init") {
@@ -0,0 +1,19 @@
1
+ import {
2
+ FEE_CONFIGS,
3
+ enforcedCount,
4
+ feeConfigsByCategory,
5
+ getFeeConfig,
6
+ renderFeeConfigDetailText,
7
+ renderFeeConfigsMd,
8
+ renderFeeConfigsText
9
+ } from "./chunk-CSYGLMZR.js";
10
+ import "./chunk-3RG5ZIWI.js";
11
+ export {
12
+ FEE_CONFIGS,
13
+ enforcedCount,
14
+ feeConfigsByCategory,
15
+ getFeeConfig,
16
+ renderFeeConfigDetailText,
17
+ renderFeeConfigsMd,
18
+ renderFeeConfigsText
19
+ };
package/dist/index.d.ts CHANGED
@@ -918,12 +918,20 @@ declare function checkOracleFreshness(account: string, opts?: OracleOpts): Promi
918
918
  declare function renderOracleText(f: OracleFreshness): string;
919
919
  declare function renderOracleMd(f: OracleFreshness): string;
920
920
 
921
- type EconomicCategory = "royalty" | "fee" | "reward";
922
- type EconomicStatus = "enforced" | "advisory";
923
- interface EconomicPattern {
921
+ interface BundledPack {
922
+ id: string;
923
+ dir: string;
924
+ manifest: PackManifest;
925
+ }
926
+ declare function listBundledPacks(): BundledPack[];
927
+ declare function resolveBundledPackToken(token: string): string | null;
928
+
929
+ type FeeConfigCategory = "royalty" | "fee" | "reward";
930
+ type FeeConfigStatus = "enforced" | "advisory";
931
+ interface FeeConfig {
924
932
  /** Stable slug. */
925
933
  id: string;
926
- category: EconomicCategory;
934
+ category: FeeConfigCategory;
927
935
  /** SDK / protocol the field belongs to. */
928
936
  sdk: string;
929
937
  /** The setup/config call that takes the field. */
@@ -936,17 +944,17 @@ interface EconomicPattern {
936
944
  fix: string;
937
945
  /** Bundled rule that detects this, or null for an advisory entry. */
938
946
  ruleId: string | null;
939
- status: EconomicStatus;
947
+ status: FeeConfigStatus;
940
948
  /** Optional reference / docs URL. */
941
949
  docsUrl?: string;
942
950
  }
943
- declare const ECONOMIC_PATTERNS: EconomicPattern[];
944
- declare function getEconomicPattern(id: string): EconomicPattern | undefined;
945
- declare function economicPatternsByCategory(cat: EconomicCategory): EconomicPattern[];
946
- declare function enforcedCount(patterns?: EconomicPattern[]): number;
947
- declare function renderEconomicsMd(patterns?: EconomicPattern[]): string;
948
- declare function renderEconomicsText(patterns?: EconomicPattern[]): string;
949
- declare function renderEconomicDetailText(e: EconomicPattern): string;
951
+ declare const FEE_CONFIGS: FeeConfig[];
952
+ declare function getFeeConfig(id: string): FeeConfig | undefined;
953
+ declare function feeConfigsByCategory(cat: FeeConfigCategory): FeeConfig[];
954
+ declare function enforcedCount(patterns?: FeeConfig[]): number;
955
+ declare function renderFeeConfigsMd(patterns?: FeeConfig[]): string;
956
+ declare function renderFeeConfigsText(patterns?: FeeConfig[]): string;
957
+ declare function renderFeeConfigDetailText(e: FeeConfig): string;
950
958
 
951
959
  interface ExploitPattern {
952
960
  /** Stable slug, e.g. "wormhole". */
@@ -977,4 +985,4 @@ declare function renderExploitsMd(patterns?: ExploitPattern[]): string;
977
985
  declare function renderExploitsText(patterns?: ExploitPattern[]): string;
978
986
  declare function renderExploitDetailText(e: ExploitPattern): string;
979
987
 
980
- export { type AccountFlow, type AnchorIdl, type AuditRef, type AuthorityClassification, type BatchResult, type BatchRow, type BatchScanOpts, type BuildOpts, CANONICAL_BY_MINT, CANONICAL_MINTS, type Candidate, type CanonicalMint, type ChainEvent, type ChainWatchOpts, type ChainWatchState, type ChangedRanges, type CheckOutcome, type CheckResult, type CheckResultKind, type Checker, type ConfigCandidate, type ConfigChecker, type CostReport, DEFAULT_REGISTRY_URL, DEFAULT_STALENESS_SLOTS, DEFAULT_TTL_HOURS, type DecodedInstruction, type DecodedTx, type DiffResult, type DriftAdvisory, type DriftBaseline, type DriftPackage, type DriftResult, ECONOMIC_PATTERNS, EXPLOIT_PATTERNS, type EconomicCategory, type EconomicPattern, type EconomicStatus, type ExploitPattern, type FirewallFinding, type FirewallOpts, type FirewallProgram, type FirewallReport, type FirewallSeverity, type FirewallVerdict, type Grade, type GraduationEvent, type IdentityStatus, type IdlAccount, type IdlConstraintParams, type IdlInstruction, KNOWN_AUTHORITY_OWNERS, KNOWN_PROGRAMS, type MintInfo, type OnChainProgram, type OracleFreshness, type OracleOpts, type OracleVerdict, type OsvAdvisory, PACK_MANIFEST_FILE, type PackInitOptions, type PackManifest, type PackRuleValidation, type PackValidateResult, type ParityNote, type ParsedDiff, type PreflightCheck, type PreflightOpts, type PreflightReport, type PreflightStatus, type PreflightVerdict, type PriorityFeePosture, type ProgramCache, type ProgramCacheEntry, type Recoverability, type RicoOutcome, type RicoResult, type RicoTokenSecurity, type Rule, type RustAccountField, type RustCandidate, type RustChecker, SYSTEM_PROGRAM, type ScoreFactor, type Severity, type TelemetrySubmitResult, type TokenIdentity, type TrustGraph, type TrustScore, type UpgradeAuthority, type UpgradeAuthorityKind, type UpgradeAuthoritySource, type VerifiedBuildState, type VerifyOpts, type WatchEvent, type WatchOptions, analyzeCosts, analyzeInstructions, analyzeToken, applyDiffToFile, audit, auditWithRule, base58Decode, base58Encode, batchScan, buildConstraintParams, buildTrustGraph, rules as bundledRules, cacheSize, canonicalMintForSymbol, checkDrift, checkOracleFreshness, checkerKinds, classifyUpgradeAuthority, decodeTransaction, defaultCachePath, deployerFlagsFrom, diffVersions, economicPatternsByCategory, enforcedCount, enrichAuthorityClassification, fileChanged, findCandidates, findConfigCandidates, formatUsd, generateRulesFromIdl, generateTestForResult, getCacheEntry, getCacheEntryMeta, getChangedRanges, getEconomicPattern, getExploitPattern, getRepoHash, getUserHash, getWorkingTreeChanges, gradeAtLeast, gradeForScore, idlProgramName, initPack, initialChainWatchState, inspectTransaction, isCanonicalMint, isEntryExpired, isTelemetryEnabled, isValidSolanaAddress, lamportsToSol, loadDirectory, loadPack, loadPacksFromDir, loadProgramCache, loadRules, parseCpiPrograms, parseDiff, parseIdl, parseMintAccount, parseMintList, pollChainOnce, pumpPreflight, putCacheEntry, queryOsv, rangeChanged, recordGraduationEvents, renderBatchText, renderCostReportMd, renderDiffMd, renderDiffText, renderDriftText, renderEconomicDetailText, renderEconomicsMd, renderEconomicsText, renderExploitDetailText, renderExploitsMd, renderExploitsText, renderFirewallText, renderOracleMd, renderOracleText, renderPreflightText, renderRicoText, renderRulesYaml, renderScoreText, renderTest, renderTrustGraphMd, rentExemptMinimum, resolveRules, riskScore, runChecker, runIncrementalScan, saveProgramCache, scoreFromProgram, scoreProgram, seedPackages, startChainWatch, startWatch, submitTelemetry, telemetryFilePath, testKinds, toSnakeCase, totalLossUsd, validatePack, validatePackManifest, verifyTokenIdentity };
988
+ export { type AccountFlow, type AnchorIdl, type AuditRef, type AuthorityClassification, type BatchResult, type BatchRow, type BatchScanOpts, type BuildOpts, type BundledPack, CANONICAL_BY_MINT, CANONICAL_MINTS, type Candidate, type CanonicalMint, type ChainEvent, type ChainWatchOpts, type ChainWatchState, type ChangedRanges, type CheckOutcome, type CheckResult, type CheckResultKind, type Checker, type ConfigCandidate, type ConfigChecker, type CostReport, DEFAULT_REGISTRY_URL, DEFAULT_STALENESS_SLOTS, DEFAULT_TTL_HOURS, type DecodedInstruction, type DecodedTx, type DiffResult, type DriftAdvisory, type DriftBaseline, type DriftPackage, type DriftResult, EXPLOIT_PATTERNS, type ExploitPattern, FEE_CONFIGS, type FeeConfig, type FeeConfigCategory, type FeeConfigStatus, type FirewallFinding, type FirewallOpts, type FirewallProgram, type FirewallReport, type FirewallSeverity, type FirewallVerdict, type Grade, type GraduationEvent, type IdentityStatus, type IdlAccount, type IdlConstraintParams, type IdlInstruction, KNOWN_AUTHORITY_OWNERS, KNOWN_PROGRAMS, type MintInfo, type OnChainProgram, type OracleFreshness, type OracleOpts, type OracleVerdict, type OsvAdvisory, PACK_MANIFEST_FILE, type PackInitOptions, type PackManifest, type PackRuleValidation, type PackValidateResult, type ParityNote, type ParsedDiff, type PreflightCheck, type PreflightOpts, type PreflightReport, type PreflightStatus, type PreflightVerdict, type PriorityFeePosture, type ProgramCache, type ProgramCacheEntry, type Recoverability, type RicoOutcome, type RicoResult, type RicoTokenSecurity, type Rule, type RustAccountField, type RustCandidate, type RustChecker, SYSTEM_PROGRAM, type ScoreFactor, type Severity, type TelemetrySubmitResult, type TokenIdentity, type TrustGraph, type TrustScore, type UpgradeAuthority, type UpgradeAuthorityKind, type UpgradeAuthoritySource, type VerifiedBuildState, type VerifyOpts, type WatchEvent, type WatchOptions, analyzeCosts, analyzeInstructions, analyzeToken, applyDiffToFile, audit, auditWithRule, base58Decode, base58Encode, batchScan, buildConstraintParams, buildTrustGraph, rules as bundledRules, cacheSize, canonicalMintForSymbol, checkDrift, checkOracleFreshness, checkerKinds, classifyUpgradeAuthority, decodeTransaction, defaultCachePath, deployerFlagsFrom, diffVersions, enforcedCount, enrichAuthorityClassification, feeConfigsByCategory, fileChanged, findCandidates, findConfigCandidates, formatUsd, generateRulesFromIdl, generateTestForResult, getCacheEntry, getCacheEntryMeta, getChangedRanges, getExploitPattern, getFeeConfig, getRepoHash, getUserHash, getWorkingTreeChanges, gradeAtLeast, gradeForScore, idlProgramName, initPack, initialChainWatchState, inspectTransaction, isCanonicalMint, isEntryExpired, isTelemetryEnabled, isValidSolanaAddress, lamportsToSol, listBundledPacks, loadDirectory, loadPack, loadPacksFromDir, loadProgramCache, loadRules, parseCpiPrograms, parseDiff, parseIdl, parseMintAccount, parseMintList, pollChainOnce, pumpPreflight, putCacheEntry, queryOsv, rangeChanged, recordGraduationEvents, renderBatchText, renderCostReportMd, renderDiffMd, renderDiffText, renderDriftText, renderExploitDetailText, renderExploitsMd, renderExploitsText, renderFeeConfigDetailText, renderFeeConfigsMd, renderFeeConfigsText, renderFirewallText, renderOracleMd, renderOracleText, renderPreflightText, renderRicoText, renderRulesYaml, renderScoreText, renderTest, renderTrustGraphMd, rentExemptMinimum, resolveBundledPackToken, resolveRules, riskScore, runChecker, runIncrementalScan, saveProgramCache, scoreFromProgram, scoreProgram, seedPackages, startChainWatch, startWatch, submitTelemetry, telemetryFilePath, testKinds, toSnakeCase, totalLossUsd, validatePack, validatePackManifest, verifyTokenIdentity };
package/dist/index.js CHANGED
@@ -4,14 +4,14 @@ import {
4
4
  renderBatchText
5
5
  } from "./chunk-QC27GNQ7.js";
6
6
  import {
7
- ECONOMIC_PATTERNS,
8
- economicPatternsByCategory,
7
+ FEE_CONFIGS,
9
8
  enforcedCount,
10
- getEconomicPattern,
11
- renderEconomicDetailText,
12
- renderEconomicsMd,
13
- renderEconomicsText
14
- } from "./chunk-S7X53LWF.js";
9
+ feeConfigsByCategory,
10
+ getFeeConfig,
11
+ renderFeeConfigDetailText,
12
+ renderFeeConfigsMd,
13
+ renderFeeConfigsText
14
+ } from "./chunk-CSYGLMZR.js";
15
15
  import {
16
16
  DEFAULT_STALENESS_SLOTS,
17
17
  checkOracleFreshness,
@@ -63,6 +63,7 @@ import {
63
63
  initPack,
64
64
  isTelemetryEnabled,
65
65
  lamportsToSol,
66
+ listBundledPacks,
66
67
  parseDiff,
67
68
  recordGraduationEvents,
68
69
  renderCostReportMd,
@@ -70,13 +71,14 @@ import {
70
71
  renderExploitsMd,
71
72
  renderExploitsText,
72
73
  rentExemptMinimum,
74
+ resolveBundledPackToken,
73
75
  runIncrementalScan,
74
76
  startWatch,
75
77
  submitTelemetry,
76
78
  telemetryFilePath,
77
79
  totalLossUsd,
78
80
  validatePack
79
- } from "./chunk-HFYBK2VA.js";
81
+ } from "./chunk-A3U6JDMN.js";
80
82
  import {
81
83
  renderTrustGraphMd
82
84
  } from "./chunk-QMJEZ6NO.js";
@@ -117,7 +119,7 @@ import {
117
119
  runChecker,
118
120
  testKinds,
119
121
  validatePackManifest
120
- } from "./chunk-V4XS5DKD.js";
122
+ } from "./chunk-Q5DMQN67.js";
121
123
  import {
122
124
  CANONICAL_BY_MINT,
123
125
  CANONICAL_MINTS,
@@ -171,8 +173,8 @@ export {
171
173
  DEFAULT_REGISTRY_URL,
172
174
  DEFAULT_STALENESS_SLOTS,
173
175
  DEFAULT_TTL_HOURS,
174
- ECONOMIC_PATTERNS,
175
176
  EXPLOIT_PATTERNS,
177
+ FEE_CONFIGS,
176
178
  KNOWN_AUTHORITY_OWNERS,
177
179
  KNOWN_PROGRAMS,
178
180
  PACK_MANIFEST_FILE,
@@ -199,9 +201,9 @@ export {
199
201
  defaultCachePath,
200
202
  deployerFlagsFrom,
201
203
  diffVersions,
202
- economicPatternsByCategory,
203
204
  enforcedCount,
204
205
  enrichAuthorityClassification,
206
+ feeConfigsByCategory,
205
207
  fileChanged,
206
208
  findCandidates,
207
209
  findConfigCandidates,
@@ -211,8 +213,8 @@ export {
211
213
  getCacheEntry,
212
214
  getCacheEntryMeta,
213
215
  getChangedRanges,
214
- getEconomicPattern,
215
216
  getExploitPattern,
217
+ getFeeConfig,
216
218
  getRepoHash,
217
219
  getUserHash,
218
220
  getWorkingTreeChanges,
@@ -227,6 +229,7 @@ export {
227
229
  isTelemetryEnabled,
228
230
  isValidSolanaAddress,
229
231
  lamportsToSol,
232
+ listBundledPacks,
230
233
  loadDirectory,
231
234
  loadPack,
232
235
  loadPacksFromDir,
@@ -248,12 +251,12 @@ export {
248
251
  renderDiffMd,
249
252
  renderDiffText,
250
253
  renderDriftText,
251
- renderEconomicDetailText,
252
- renderEconomicsMd,
253
- renderEconomicsText,
254
254
  renderExploitDetailText,
255
255
  renderExploitsMd,
256
256
  renderExploitsText,
257
+ renderFeeConfigDetailText,
258
+ renderFeeConfigsMd,
259
+ renderFeeConfigsText,
257
260
  renderFirewallText,
258
261
  renderOracleMd,
259
262
  renderOracleText,
@@ -264,6 +267,7 @@ export {
264
267
  renderTest,
265
268
  renderTrustGraphMd,
266
269
  rentExemptMinimum,
270
+ resolveBundledPackToken,
267
271
  resolveRules,
268
272
  riskScore,
269
273
  runChecker,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  audit,
3
3
  resolveRules
4
- } from "./chunk-V4XS5DKD.js";
4
+ } from "./chunk-Q5DMQN67.js";
5
5
  import "./chunk-2XJORJPQ.js";
6
6
  import "./chunk-O5Z4ZJHC.js";
7
7
  import {