Bedrock uniBTC monitoring: Safe monitor + state polling (single-EOA mint path, supply feeder, PoR)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 13
- Forks
- 13
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 19
Description
Add monitoring for Bedrock uniBTC (Ethereum) using the existing Safe monitor plus state polling only, with no event subscriptions. There is no coverage today.
Source: risk reassessment yearn/risk-score#468 (report, snapshot evidence). Baselines are from Ethereum block 25,981,132 (Sep 15, 2026).
Why
- Single-EOA unbacked mint. EOA
0x3eea50ba10952e5e0dfaa50ecfcc5ab19ad591efis the proxy admin of the legacy withdrawal router0xbb45b3a09bffc15747d1a331775fa408e587f38d, which holds VaultOPERATOR_ROLE.- By upgrading the router, the EOA can call
Vault.execute→uniBTC.mintfor any amount, with no reserve check. - An anvil fork confirmed it: 100,000 uniBTC minted, about 21 times the Chainlink reserves.
- By upgrading the router, the EOA can call
- EOA roles outside the Safes. The EOAs below can change the reserve gate, the supply feeder and CCIP limits without any Safe transaction.
- Unreliable supply feeder. The manually written supply value is 700.93 uniBTC (15.42%) below Bedrock's dashboard, and has been since Sep 13.
1. Safe monitor: add two Safes to protocols/safe/addresses.py
| Safe | Address | What its queued transactions reveal |
|---|---|---|
| uniBTC ops Safe (3/5) | 0xC9dA980fFABbE2bbe15d4734FDae5761B86b5Fc3 |
ProxyAdmin upgrades (token, Vault, CCIPPeer, live router, asset feeder, directBTC) and ProxyAdmin ownership; uniBTC token MINTER_ROLE / DEFAULT_ADMIN_ROLE / FREEZER_ROLE grants; token freezes (freezeUsers, setFreezeToRecipient, which emit no events); Vault admin (operator grants/revokes, allowed targets/tokens, caps); live router fees, delays, quotas and blacklist; owner/threshold changes |
| Bedrock admin Safe (3/5) | 0xAeE017052DF6Ac002647229D58B786E380B9721A |
CCIP pool owner actions (lanes, remote pools, registry administrator); BurnProxy / TransferProxy use (moving Vault assets to the ops Safe); CCIPPeer and directBTC admin; owner/threshold changes |
2. Polling script (hourly unless noted), for what the Safe monitor cannot see
| Severity | Check | Call | Baseline | Alert when |
|---|---|---|---|---|
| CRITICAL | Unexpected uniBTC minting | uniBTC totalSupply() delta |
2,981.12556288; 30-day mints were 3.07 uniBTC in total | +10 uniBTC in 1h (CRITICAL); +2 uniBTC in 24h (HIGH) |
| CRITICAL | Reserve gate changed by operational EOA (0x9251fd3d79522bb2243a58fff1db43e25a495aab, Vault MANAGER_ROLE) |
Vault adequacyRatio(), chainlinkReserveFeeder(), uniBTCSupplyFeeder(), feederHeartbeat() |
900, 0xc590D9fb8eE78a0909dFF341ccf717000b7b7fF2, 0xE542919E4b281f10b437F947c8Ba224DdfaBc716, 86400 |
Any change |
| HIGH | Vault stopped or paused (six pauser EOAs) | Vault outOfService(), paused(); router 0xAA732c9c110A84d090a72da230eAe1E779f89246 paused() |
all false |
Any true |
| CRITICAL | Reserves below supply | PoR 0xc590D9fb8eE78a0909dFF341ccf717000b7b7fF2 latestRoundData().answer (18 dec) / reserve API data.total_supply |
4,640.515623 / 4,546.67793 = 102.06% | < 100% (CRITICAL), < 101% (HIGH) |
| HIGH | PoR stale | latestRoundData().updatedAt |
38,534s old | Older than 86,400s (Vault mint() reverts) |
| HIGH | Supply feeder wrong or stale | Feeder totalTokenSupply() (8 dec) vs API total_supply |
3,845.74449304 vs 4,546.67793 (15.42% gap; firing since Sep 13) | Gap > 2%; value unchanged for 48h (it normally changes daily) |
| HIGH | Redemptions underfunded | Router tokenDebts(WBTC) (totalDebts − totalCleared) vs WBTC balanceOf(Vault 0x047D41F2544B7F63A8e991aF2068a363d210d6Da) |
0.75152598 vs 0.46065725 (includes requests still inside the 8-day delay) | Uncleared > Vault WBTC for > 24h and growing |
| HIGH | Peg | CoinGecko universal-btc or other price provider in BTC |
0.992417 | < 0.98 |
Gaps accepted by skipping events
- Lag: detection comes up to an hour after an EOA action or mint, not in real time.
- No mint attribution: a supply jump shows that minting happened, not which minter did it. Check the transaction manually when the alert fires.
- Safe transactions executed without being queued in the Safe tx service aren't seen beforehand; the implementation-slot and state polls only catch them afterwards. Role grants made that way (e.g., a new minter) show up only through the supply-delta check.
Checklist
- Add both Safes to the Safe monitor
- Hourly polling script (the CRITICAL/HIGH rows above), added to
automation/jobs.yaml
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the existing Safe monitor and protocols/safe/addresses.py, then inspect automation/jobs.yaml and the repository’s current polling jobs. Add both listed Safes and implement the specified hourly state checks and thresholds without event subscriptions; done means the checklist is complete and all CRITICAL/HIGH conditions are covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- devops, observability-sre
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100