BlackHartBlackHart

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

1

Solidity library

Installs the Solidity library + helper script via forge.

Terminal
forge install hartmade/blackhart-public
2

CI helper (optional)

For pre-test BRI gating in GitHub Actions etc.

Terminal
npm install --save-dev @blackhart/foundry-plugin
code_blocks

Solidity 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.

test/AaveIntegration.t.sol
// 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);
    }
}
settings

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
# 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
# remappings.txt
@blackhart/foundry-plugin/=lib/blackhart-public/packages/foundry-plugin/
sync_alt

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.

blackhart-check.mjs
#!/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:

.github/workflows/test.yml
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.mjs
Open source

MIT licensed. Issues, PRs, and forks welcome.

codeView source