2026-03-20 22:02:13 +00:00
# formulas/run-protocol.toml
#
# On-chain protocol health snapshot — collect TVL, accumulated fees,
# position count, and rebalance frequency from the deployed LiquidityManager.
# Write a structured JSON evidence file for planner and predictor consumption.
#
# Type: sense. Read-only — produces metrics only, no git artifacts.
#
# Staleness threshold: 1 day (matches evidence/protocol/ schema).
# Cron: daily at 07:00 UTC (staggered 1 h after run-resources).
[ formula ]
id = "run-protocol"
name = "On-Chain Protocol Health Snapshot"
description = "Collect TVL, accumulated fees, position count, and rebalance frequency from the deployed LiquidityManager; write evidence/protocol/{date}.json."
type = "sense"
# "sense" → read-only, produces metrics only
# "act" → produces git artifacts (cf. run-evolution, run-red-team)
# ── Cron ───────────────────────────────────────────────────────────────────────
[ cron ]
schedule = "0 7 * * *" # daily at 07:00 UTC (1 h after run-resources)
description = "Matches 1-day staleness threshold — one snapshot per day keeps the record fresh."
# ── Inputs ─────────────────────────────────────────────────────────────────────
[ inputs . rpc_url ]
type = "string"
required = true
description = "" "
Base network RPC endpoint used to query on-chain state .
Example : https : / / mainnet . base . org or a running Anvil fork URL .
"" "
[ inputs . deployments_file ]
type = "string"
required = false
default = "onchain/deployments-local.json"
description = "" "
Path to the deployments JSON file containing contract addresses .
The formula reads LiquidityManager address from this file .
Use onchain / deployments . json for mainnet ; onchain / deployments-local . json
for a local Anvil fork .
"" "
[ inputs . lookback_blocks ]
type = "integer"
required = false
default = 7200
description = "" "
Number of blocks to scan for Recenter events when computing
rebalance_count_24h ( ~ 24 h of Base blocks at ~ 2 s / block ) .
"" "
# ── Execution ──────────────────────────────────────────────────────────────────
[ execution ]
script = "scripts/harb-evaluator/run-protocol.sh"
invocation = "RPC_URL={rpc_url} DEPLOYMENTS_FILE={deployments_file} LOOKBACK_BLOCKS={lookback_blocks} bash scripts/harb-evaluator/run-protocol.sh"
# Exit codes:
# 0 snapshot written successfully
# 2 infrastructure error (RPC unreachable, missing deployments file, forge unavailable, etc.)
# ── Steps ──────────────────────────────────────────────────────────────────────
[ [ steps ] ]
id = "read-addresses"
description = "" "
Read the LiquidityManager contract address from { deployments_file } .
Fail with exit code 2 if the file is absent or the address is missing .
"" "
[ [ steps ] ]
id = "collect-tvl"
description = "" "
Query LiquidityManager total ETH via forge script LmTotalEth . s . sol
against { rpc_url } .
Records tvl_eth ( wei string ) and tvl_eth_formatted ( ETH , 2 dp ) .
LmTotalEth . s . sol uses exact Uniswap V3 integer math ( LiquidityAmounts +
TickMath ) to sum free ETH , free WETH , and ETH locked across all three
positions ( floor , anchor , discovery ) .
"" "
forge_script = "onchain/script/LmTotalEth.s.sol"
[ [ steps ] ]
id = "collect-fees"
description = "" "
Query accumulated protocol fees from the LiquidityManager via cast call :
2026-03-21 21:00:14 +00:00
cast call $ LM "accumulatedFees()(uint256)"
2026-03-20 22:02:13 +00:00
Records accumulated_fees_eth ( wei string ) and accumulated_fees_eth_formatted
( ETH , 3 dp ) .
2026-03-21 21:00:14 +00:00
Falls back to 0 gracefully if the function reverts or is not present on
the deployed contract ( older deployment without fee tracking ) .
2026-03-20 22:02:13 +00:00
"" "
[ [ steps ] ]
id = "collect-positions"
description = "" "
Query the three Uniswap V3 positions held by the LiquidityManager :
2026-03-21 21:00:14 +00:00
LiquidityManager . positions ( 0 ) → ( liquidity , tickLower , tickUpper ) # FLOOR
LiquidityManager . positions ( 1 ) → ( liquidity , tickLower , tickUpper ) # ANCHOR
LiquidityManager . positions ( 2 ) → ( liquidity , tickLower , tickUpper ) # DISCOVERY
2026-03-20 22:02:13 +00:00
Records position_count ( number of positions with liquidity > 0 ) and the
positions array .
"" "
[ [ steps ] ]
id = "collect-rebalances"
description = "" "
Count Recenter events emitted by the LiquidityManager in the past
{ lookback_blocks } blocks via eth_getLogs .
Records :
- rebalance_count_24h : total Recenter event count in the window .
- last_rebalance_block : block number of the most recent Recenter event
( 0 if none found in the window ) .
"" "
2026-03-21 21:00:14 +00:00
event_signature = "Recentered(int24,bool)"
2026-03-20 22:02:13 +00:00
[ [ steps ] ]
id = "collect"
description = "" "
Assemble all collected metrics into evidence / protocol / { date } . json .
Compute verdict :
- "offline" if tvl_eth = 0 or RPC was unreachable .
- "degraded" if position_count < 3 , or rebalance_count_24h = 0 and the
protocol has been live for > 1 day .
- "healthy" otherwise .
Write the file conforming to the schema in evidence / README . md
## Schema: protocol/YYYY-MM-DD.json.
"" "
output = "evidence/protocol/{date}.json"
schema = "evidence/README.md" # see ## Schema: protocol/YYYY-MM-DD.json
[ [ steps ] ]
id = "deliver"
description = "" "
Commit evidence / protocol / { date } . json to main .
Post a one-line summary comment to the originating issue ( if any ) :
verdict , tvl_eth_formatted , accumulated_fees_eth_formatted ,
position_count , rebalance_count_24h .
On "degraded" or "offline" : highlight the failing dimension and its value .
"" "
# ── Products ───────────────────────────────────────────────────────────────────
[ products . evidence_file ]
path = "evidence/protocol/{date}.json"
delivery = "commit to main"
schema = "evidence/README.md" # see ## Schema: protocol/YYYY-MM-DD.json
[ products . issue_comment ]
delivery = "post to originating issue (if any)"
content = "verdict, tvl_eth_formatted, accumulated_fees_eth_formatted, position_count, rebalance_count_24h"
on_degraded = "highlight failing dimension and its current value"
# ── Resources ──────────────────────────────────────────────────────────────────
[ resources ]
profile = "light"
compute = "local — forge script + cast calls only; no Anvil or Docker startup required"
rpc = "Base network RPC ({rpc_url}) — read-only calls"
concurrency = "safe to run in parallel with other formulas"
# ── Notes ──────────────────────────────────────────────────────────────────────
[ notes ]
tvl_metric = "" "
TVL is measured as LiquidityManager total ETH : free ETH + free WETH + ETH
locked across all three Uniswap V3 positions ( floor , anchor , discovery ) .
Uses the same LmTotalEth . s . sol forge script as run-red-team to ensure
consistent measurement methodology .
"" "
rebalance_staleness = "" "
A zero rebalance_count_24h on an established deployment indicates the
recenter ( ) upkeep bot ( services / txnBot ) has stalled . The "degraded"
verdict triggers a planner alert . On a fresh deployment ( < 1 day old )
zero rebalances is expected and does not trigger degraded .
"" "
fees_fallback = "" "
accumulated_fees_eth falls back to 0 for deployments without fee tracking .
The verdict is not affected by a zero fee value alone — only TVL and
position_count drive the verdict .
"" "