Foundry plugin
Read live BRI scores inside Forge tests. Gate your audit pipeline on protocol risk. Ships as a Solidity library that uses vm.ffi to hit the public BRI API, plus a tiny Node CI helper.
Install
Solidity library
Installs the Solidity library + helper script via forge.
forge install hartmade/blackhart-publicCI helper (optional)
For pre-test BRI gating in GitHub Actions etc.
npm install --save-dev @blackhart/foundry-pluginSolidity usage
Import the library and call BlackHart.bri(slug) from any test. The library shells out to the BRI API via FFI and parses the response.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Test} from "forge-std/Test.sol";
import {BlackHart} from "@blackhart/foundry-plugin/src/BlackHart.sol";
contract AaveIntegrationTest is Test {
function testAaveBRI() public {
uint16 score = BlackHart.bri("aave-v3");
require(score >= 700, "Aave BRI too low");
}
function testFullBreakdown() public view {
BlackHart.ProtocolScore memory s = BlackHart.fullScore("uniswap-v4");
assertGe(s.bri, 850); // MITHRIL or above
assertGe(s.dimensions[2], 90); // D3 Oracle Integrity
assertEq(s.stale, false);
}
}foundry.toml requirements
The library needs FFI enabled and write access to its local cache file. Without these two settings the Solidity calls will revert with FFI not enabled.
# foundry.toml
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
ffi = true
# Allow the plugin's curl/node helper to write its cache file
fs_permissions = [
{ access = "read-write", path = "./.blackhart-cache" }
]
[rpc_endpoints]
# No on-chain RPC required for BlackHart.bri()If you keep your dependencies under a non-default path, add a remapping:
# remappings.txt
@blackhart/foundry-plugin/=lib/blackhart-public/packages/foundry-plugin/CI helper (blackhart-check.mjs)
The npm package ships a tiny Node script that gates your build on a BRI threshold, then runs your Forge tests. Use it as the single step in a GitHub Actions job.
#!/usr/bin/env node
// blackhart-check.mjs
// Pre-test BRI gate. Fails the build if any pinned dependency is below threshold.
import { execSync } from "node:child_process";
const GATES = {
"aave-v3": 700,
"uniswap-v4": 750,
"lido": 650,
};
const API = process.env.BLACKHART_API_URL ?? "https://oracle.blackhart.io/api/v1";
const KEY = process.env.BLACKHART_API_KEY;
let failed = false;
for (const [slug, min] of Object.entries(GATES)) {
const res = await fetch(`${API}/scores/${slug}`, {
headers: KEY ? { "x-api-key": KEY } : {},
});
if (!res.ok) {
console.error(`${slug}: HTTP ${res.status}`);
failed = true;
continue;
}
const { bri, forgeScale, stale } = await res.json();
const pass = !stale && bri >= min;
console.log(`${slug.padEnd(14)} BRI ${bri} forge=${forgeScale} min=${min} ${pass ? "PASS" : "FAIL"}`);
if (!pass) failed = true;
}
if (failed) process.exit(1);
execSync("forge test", { stdio: "inherit" });Wired into GitHub Actions:
name: Forge + BRI Gate
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { submodules: recursive }
- uses: foundry-rs/foundry-toolchain@v1
with: { version: nightly }
- uses: actions/setup-node@v4
with: { node-version: 20 }
- name: Install BlackHart Foundry plugin
run: forge install hartmade/blackhart-public
- name: Install CI helper
run: npm install --save-dev @blackhart/foundry-plugin
- name: Gate BRI + run Forge tests
env:
BLACKHART_API_KEY: ${{ secrets.BLACKHART_API_KEY }}
run: node node_modules/@blackhart/foundry-plugin/bin/blackhart-check.mjsMIT licensed. Issues, PRs, and forks welcome.