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.
- package/dist/{chunk-HFYBK2VA.js → chunk-A3U6JDMN.js} +64 -12
- package/dist/{chunk-S7X53LWF.js → chunk-CSYGLMZR.js} +19 -19
- package/dist/{chunk-V4XS5DKD.js → chunk-Q5DMQN67.js} +19 -5
- package/dist/cli.js +60 -24
- package/dist/feeConfigs-S4E5GQ7Q.js +19 -0
- package/dist/index.d.ts +21 -13
- package/dist/index.js +19 -15
- package/dist/{mcp-LITBQHBF.js → mcp-O45PSOVU.js} +1 -1
- package/dist/packs/README.md +33 -0
- package/dist/packs/jito-bundle-zero-tip/README.md +33 -0
- package/dist/packs/jito-bundle-zero-tip/brainblast-pack.yaml +5 -0
- package/dist/packs/jito-bundle-zero-tip/fixtures/jito-bundle-zero-tip/fixed/bundle.ts +13 -0
- package/dist/packs/jito-bundle-zero-tip/fixtures/jito-bundle-zero-tip/vulnerable/bundle.ts +16 -0
- package/dist/packs/jito-bundle-zero-tip/rules/jito-bundle-zero-tip.yaml +54 -0
- package/dist/packs/jupiter-quote-zero-slippage/brainblast-pack.yaml +5 -0
- package/dist/packs/jupiter-quote-zero-slippage/fixtures/jupiter-quote-zero-slippage/fixed/arb.ts +17 -0
- package/dist/packs/jupiter-quote-zero-slippage/fixtures/jupiter-quote-zero-slippage/vulnerable/arb.ts +17 -0
- package/dist/packs/jupiter-quote-zero-slippage/rules/jupiter-quote-zero-slippage.yaml +51 -0
- package/dist/packs/metaplex-nft-royalty-zero/README.md +39 -0
- package/dist/packs/metaplex-nft-royalty-zero/brainblast-pack.yaml +5 -0
- package/dist/packs/metaplex-nft-royalty-zero/fixtures/metaplex-nft-royalty-zero/fixed/mint.ts +11 -0
- package/dist/packs/metaplex-nft-royalty-zero/fixtures/metaplex-nft-royalty-zero/vulnerable/mint.ts +11 -0
- package/dist/packs/metaplex-nft-royalty-zero/rules/metaplex-nft-royalty-zero.yaml +39 -0
- package/dist/packs/meteora-dlmm-zero-min-out/README.md +32 -0
- package/dist/packs/meteora-dlmm-zero-min-out/brainblast-pack.yaml +5 -0
- package/dist/packs/meteora-dlmm-zero-min-out/fixtures/meteora-dlmm-zero-min-out/fixed/swap.ts +19 -0
- package/dist/packs/meteora-dlmm-zero-min-out/fixtures/meteora-dlmm-zero-min-out/vulnerable/swap.ts +19 -0
- package/dist/packs/meteora-dlmm-zero-min-out/rules/meteora-dlmm-zero-min-out.yaml +47 -0
- package/dist/packs/pyth-price-unchecked-staleness/README.md +34 -0
- package/dist/packs/pyth-price-unchecked-staleness/brainblast-pack.yaml +5 -0
- package/dist/packs/pyth-price-unchecked-staleness/fixtures/pyth-price-unchecked-staleness/fixed/price.ts +14 -0
- package/dist/packs/pyth-price-unchecked-staleness/fixtures/pyth-price-unchecked-staleness/vulnerable/price.ts +14 -0
- package/dist/packs/pyth-price-unchecked-staleness/rules/pyth-price-unchecked-staleness.yaml +47 -0
- package/dist/packs/raydium-compute-zero-slippage/README.md +43 -0
- package/dist/packs/raydium-compute-zero-slippage/brainblast-pack.yaml +5 -0
- package/dist/packs/raydium-compute-zero-slippage/fixtures/raydium-compute-zero-slippage/fixed/swap.ts +12 -0
- package/dist/packs/raydium-compute-zero-slippage/fixtures/raydium-compute-zero-slippage/vulnerable/swap.ts +12 -0
- package/dist/packs/raydium-compute-zero-slippage/rules/raydium-compute-zero-slippage.yaml +40 -0
- package/dist/packs/solana-sendtx-unconfirmed/README.md +34 -0
- package/dist/packs/solana-sendtx-unconfirmed/brainblast-pack.yaml +5 -0
- package/dist/packs/solana-sendtx-unconfirmed/fixtures/solana-sendtx-unconfirmed/fixed/payment.ts +10 -0
- package/dist/packs/solana-sendtx-unconfirmed/fixtures/solana-sendtx-unconfirmed/vulnerable/payment.ts +10 -0
- package/dist/packs/solana-sendtx-unconfirmed/rules/solana-sendtx-unconfirmed.yaml +40 -0
- package/dist/packs/spl-transfer-not-checked-in-payout/brainblast-pack.yaml +5 -0
- package/dist/packs/spl-transfer-not-checked-in-payout/fixtures/spl-transfer-not-checked-in-payout/fixed/payout-processor.ts +29 -0
- package/dist/packs/spl-transfer-not-checked-in-payout/fixtures/spl-transfer-not-checked-in-payout/vulnerable/payout-processor.ts +22 -0
- package/dist/packs/spl-transfer-not-checked-in-payout/rules/spl-transfer-not-checked-in-payout.yaml +49 -0
- package/dist/rules/metaplex-seller-fee-zero.yaml +2 -2
- package/package.json +1 -1
- package/dist/tokenEconomics-HBF3DYNH.js +0 -19
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
loadPack,
|
|
8
8
|
resolveRules,
|
|
9
9
|
walk
|
|
10
|
-
} from "./chunk-
|
|
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
|
|
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
|
|
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 =
|
|
538
|
-
if (!
|
|
587
|
+
const configPath = join3(targetDir, ".agent-research", "config.json");
|
|
588
|
+
if (!existsSync3(configPath)) return false;
|
|
539
589
|
try {
|
|
540
|
-
const cfg = JSON.parse(
|
|
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 =
|
|
597
|
+
const idPath = join3(homedir(), ".brainblast", "telemetry-id");
|
|
548
598
|
let id;
|
|
549
|
-
if (
|
|
550
|
-
id =
|
|
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
|
|
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 (!
|
|
627
|
+
if (!existsSync3(file)) {
|
|
578
628
|
return { submitted: 0, accepted: 0, rejected: 0, graduations: [] };
|
|
579
629
|
}
|
|
580
|
-
const events =
|
|
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/
|
|
2
|
-
var
|
|
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
|
|
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
|
|
55
|
-
return
|
|
54
|
+
function getFeeConfig(id) {
|
|
55
|
+
return FEE_CONFIGS.find((e) => e.id === id || e.ruleId === id);
|
|
56
56
|
}
|
|
57
|
-
function
|
|
58
|
-
return
|
|
57
|
+
function feeConfigsByCategory(cat) {
|
|
58
|
+
return FEE_CONFIGS.filter((e) => e.category === cat);
|
|
59
59
|
}
|
|
60
|
-
function enforcedCount(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
|
|
69
|
-
const L = ["##
|
|
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
|
|
94
|
+
function renderFeeConfigsText(patterns = FEE_CONFIGS) {
|
|
95
95
|
const L = [];
|
|
96
|
-
L.push("\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
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
FEE_CONFIGS,
|
|
123
|
+
getFeeConfig,
|
|
124
|
+
feeConfigsByCategory,
|
|
125
125
|
enforcedCount,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
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/
|
|
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
|
|
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}';
|
|
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
|
-
"
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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] === "
|
|
571
|
-
await
|
|
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
|
|
726
|
+
async function runFeeConfigs(argv) {
|
|
712
727
|
const {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
} = await import("./
|
|
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
|
|
721
|
-
console.log("
|
|
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(` ${
|
|
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 =
|
|
745
|
+
const e = getFeeConfig(id);
|
|
731
746
|
if (!e) {
|
|
732
|
-
console.error(`error: no
|
|
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(
|
|
751
|
+
else console.log(renderFeeConfigDetailText(e));
|
|
737
752
|
return;
|
|
738
753
|
}
|
|
739
754
|
if (json) {
|
|
740
|
-
console.log(JSON.stringify(
|
|
755
|
+
console.log(JSON.stringify(FEE_CONFIGS, null, 2));
|
|
741
756
|
return;
|
|
742
757
|
}
|
|
743
|
-
console.log(
|
|
758
|
+
console.log(renderFeeConfigsText());
|
|
744
759
|
const outDir2 = join3(process.cwd(), ".agent-research");
|
|
745
760
|
mkdirSync2(outDir2, { recursive: true });
|
|
746
|
-
writeFileSync2(join3(outDir2, "
|
|
761
|
+
writeFileSync2(join3(outDir2, "fee-configs.md"), renderFeeConfigsMd());
|
|
747
762
|
console.log(`
|
|
748
|
-
catalog: ${join3(outDir2, "
|
|
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
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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:
|
|
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:
|
|
947
|
+
status: FeeConfigStatus;
|
|
940
948
|
/** Optional reference / docs URL. */
|
|
941
949
|
docsUrl?: string;
|
|
942
950
|
}
|
|
943
|
-
declare const
|
|
944
|
-
declare function
|
|
945
|
-
declare function
|
|
946
|
-
declare function enforcedCount(patterns?:
|
|
947
|
-
declare function
|
|
948
|
-
declare function
|
|
949
|
-
declare function
|
|
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,
|
|
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
|
-
|
|
8
|
-
economicPatternsByCategory,
|
|
7
|
+
FEE_CONFIGS,
|
|
9
8
|
enforcedCount,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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-
|
|
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-
|
|
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,
|