ROHME Decree: RWA bonds priceable across closures, ROHM/NVDA + ROHM/GME LP bonds, unstick the fee pipeline, and install LpBondLister
Supersedes proposal 0x26949413..., cancelled while Pending (itself superseding 0x35263541... and 0x28798732...).
All seven adapters below are freshly deployed: the LastPriceFeed backstop is tightened from
7 days to 90h, and the LP price bands are re-derived against the corrected valuation formula.
PART 1 (actions 1-5) — RWA bonds priceable across market closures.
The tokenized-equity Chainlink feeds publish in bursts during trading hours and then go
dark. Observed gaps run 34-45h against the 24h heartbeat these reserves are registered
with, and Friday close to Monday open is about 65h. Treasury.deposit prices the quote
through tokenValue, so every equity bond reverts StaleFeed all weekend and createMarket
cannot open one, while the underlying tokens trade on-chain throughout. Treasury
MAX_HEARTBEAT is 48h and is a constant, so NO legal heartbeat spans a weekend.
Actions 1-5 repoint the five equity reserves at LastPriceFeed adapters, which republish
the underlying feed's last answer with updatedAt re-stamped to the current block. Every
other registry parameter - heartbeat, 20 pct haircut, F02 band, checkPaused - is
preserved EXACTLY as registered today; only the aggregator changes.
1. SNDK -> 0xAAa7638eDdb8D2f8F120703C30ca53a583fffd5f
2. MU -> 0x83D807b89A2E791b603B38CD004eb4caD21529C8
3. SPCX -> 0x43FcCea810579A386AdCB698e28a34811DAfA858
4. NVDA -> 0x76cD10364fd88369914255FBCe0e315db3F6F7F0
5. GME -> 0xb8cff824B2d46ecC28F3C3c544C916B505f24310
TRADE-OFF, stated plainly: inside the adapter's 7-day backstop, staleness protection
against the upstream feed is surrendered. A Sunday deposit is priced at Friday's close.
An equity gapping down over a weekend by more than the 20 pct haircut is an uncompensated
loss. This is a deliberate choice of bond liveness over mark accuracy. Still enforced: a
hard 90h maxAge so an abandoned feed fails closed, and the underlying's own integrity
checks verbatim - non-positive answer, zero updatedAt, incomplete round all still revert.
WHY 90h: Friday close (16:00 ET) to Tuesday open (09:30 ET) - a long weekend with a Monday
market holiday - is 89.5h, the worst LEGITIMATE closure for these feeds. The previous
adapters used 7 days, roughly double their own stated design target. Because LastPriceFeed
neuters the heartbeat by construction and Treasury.sequencerFeed is the zero address, maxAge
is the ONLY remaining guard, so the extra 79h bought no legitimate coverage and only delayed
failing closed on a genuinely abandoned feed.
Only the heartbeat dimension is relaxed. NOTE: the L2 sequencer-uptime gate is separately
inert because Treasury.sequencerFeed is the zero address, so these five reserves will have
neither a heartbeat nor a sequencer guard - only maxAge. Wiring a sequencer feed is
recommended follow-up work.
PART 2 (actions 6-9) — ROHM/NVDA and ROHM/GME LP bonds.
Registers the two UniV2 LP tokens as reserve assets so holders can supply their own
liquidity and bond the LP. The pairs already exist; the feeds are redeployed with the
audit fixes below and read the Part 1 adapters, so LP bonds inherit the weekend fix.
6. setPriceFeed ROHM/NVDA LP 0xCB549c9C9E8cf3e52BF37B26Fc208AeD3b3f7D16 -> 0x749F443D3f1AEC830D47F767A4Dbb96912b2801a
7. enable RESERVETOKEN ROHM/NVDA LP
8. setPriceFeed ROHM/GME LP 0x744270585eDFfB51E9783574c0b95c4063BCF639 -> 0xBD746e0c56b8b7784c00552850778812A5565238
9. enable RESERVETOKEN ROHM/GME LP
WHAT THE AUDIT FOUND AND WHAT CHANGED. The previous feed marked the pool at
2*sqrt(vRohm*vOther), i.e. as if BOTH legs were recoverable backing. The ROHM leg is the
protocol's own liability - unwinding returns ROHM, not reserves - so every LP was
over-marked by 2*sqrt(backing/marketPrice): 2x at par and worse below it. Measured
end-to-end, a bond at the derived floor took $4.00 of real assets for 5.818 ROHM of
liability, 31 pct instant dilution, while reported backing per ROHM rose. Separately, the
ROHM leg read treasury.totalReserves()/baseSupply(); because _auditReserves WRITES
totalReserves from a loop that READS it back through the feed, the permissionless
auditReserves() became a fixed-point ratchet on the mint budget (measured excessReserves
399.99e9 -> 420.99e9 with no economic activity). The feed now uses
min(vOther, sqrt(vRohm*vOther)) with the ROHM leg at a flat $1.00 target and no Treasury
read at all - the same math UniV2PolLpFeed has always used.
The F02 price bands are also corrected. They were previously anchored on the PRE-AUDIT
formula (2*sqrt(backing*spot)), which made them 2.40x looser than intended - an effective
~9.6x ceiling rather than 4x. They are now anchored on the corrected feed's own ceiling,
sqrt(spot)*10**12.5, which is independent of pool size and ratio and needs no treasury read:
ROHM/NVDA 178469044934969 and ROHM/GME 59018641122953 (8-dec, 4x fair).
checkPaused is false on the two LP registrations because a UniswapV2Pair has no
oraclePaused(), and the Treasury treats a reverting staticcall on a flagged token as
UNPRICEABLE - true would brick the LP permanently. The ERC-8056 gate runs inside the feed
against the equity leg. Both pools are EMPTY at filing: the first liquidity provider sets
the ratio, so each pair should be seeded with a small honest amount before bonding opens.
PART 3 (actions 10-11) — unstick the fee pipeline.
FeeCollector.harvest() has never successfully run and currently holds ~674 ROHM of protocol
fees. _minWethOut derives its floor from the SPOT reserve ratio, which ignores the 0.30 pct
pool fee and price impact entirely, so the reachable ceiling is rR*(1000s-3)/((1-s)*997) -
about 30.7 ROHM today against a 500 ROHM cap. Note that below 30 bps of slippage that
expression is negative: no amount can ever clear a floor that does not cover the pool fee.
10. FeeCollector.setSlippageBps(1000) (MAX_SLIPPAGE_BPS; raises the ceiling to ~67 ROHM)
11. FeeCollector.setMaxHarvest(25e9) (25 ROHM, deliberately under the ceiling)
25 ROHM leaves headroom on purpose: the ceiling is a fraction of pool depth, so anyone can
grief a harvest sitting at the edge by buying ROHM in the same block to shrink reserveRohm.
Each harvest sells into the pool and raises the ceiling, so draining the balance takes
roughly 16 calls and self-accelerates. The proper fix is to derive the floor from
router.getAmountsOut rather than spot, which needs a FeeCollector redeploy - follow-up work.
PART 4 (action 12) — install LpBondLister 0x5c288FfD4E766C8932E62F064A463380b39C89f4.
Today every new LP bond asset costs a full ~8-day governance cycle. This installs a contract
that can list ROHM/<reserve> LP tokens without a vote, so ROHM/SNDK or any other approved RWA
pair becomes a single operator transaction.
12. Authority.pushGovernor(LpBondLister, effectiveImmediately=false)
`false` means only `newGovernor` is set, so the Timelock stays the live governor for the rest
of this batch; the lister then claims the role through its own permissionless acceptGovernor().
That is why this action is ordered last.
WHY THIS IS NOT A GENERAL LOOSENING OF THE MINT PERMISSION. setPriceFeed + enable is the mint
permission, and this does not open it for arbitrary assets - only for a DERIVED class where the
caller supplies no trusted input: the paired token must already be an enabled RESERVETOKEN that
governance vetted AND must not itself be a Uniswap pair; the aggregator is read from the
Treasury's own registry rather than passed in; the pair is resolved through the canonical
factory so no look-alike can be substituted; the valuation feed is deployed by the contract
itself so its bytecode cannot lie; heartbeat and the ERC-8056 pause flag are inherited, and the
haircut is max(inherited, 2000 bps) - never looser than the leg. Listing is also inert on its
own: PolicyGuard.createMarket (onlyOperator) still has to price it before anything can be
minted.
GOVERNANCE LOSES NOTHING. relay(target, data) lets the Timelock execute any call this contract
could make, so every power governance holds today it still holds afterwards, routed through
here. It can also hand the role back, rotate the operator (2-step), re-point itself at a new
Timelock after a Governor migration, and delist a pair.
AUDIT NOTE. An earlier revision of this contract was audited and the findings are fixed in the
deployed bytecode: the anti-nesting guard is now STRUCTURAL (it asks the factory whether the leg
is one of its own pairs) rather than local bookkeeping, which previously let the genesis
ROHM/WETH POL LP - registered directly by governance, so invisible to that bookkeeping - be
paired into a doubly self-referential nested LP; a governance de-listing is no longer reversible
by the operator; relay rejects a code-less target instead of silently succeeding; and the
timelock reference is no longer immutable. One residual is documented and NOT fixed: the
valuation requires a HOOKLESS paired token, which governance vetted each leg as a reserve
without ever asking. The 4x price band caps that to a PriceOutOfBounds skip rather than an
over-mint, but if a hook-bearing token is ever approved as a reserve this path should be gated
behind an explicit hookless allowlist first.