Remove the Balancer hook from the LT pool#5
Open
EndymionJkb wants to merge 9 commits into
Open
Conversation
# Conflicts: # test/fork/balancer/base/Test_BalancerSwapRateOracleBase.t.sol
# Conflicts: # CLAUDE.md # script/Deploy.s.sol # src/factory/templates/BalancerV3DeploymentTemplate.sol # src/factory/templates/base/BaseDeploymentTemplate.sol # src/factory/templates/base/Components.sol # src/kernels/base/quoter/liquidity-tranche/balancer-v3/RoycoDayBalancerV3Hooks.sol # src/kernels/base/quoter/liquidity-tranche/balancer-v3/RoycoDayBalancerV3HooksStandIn.sol # test/fork/balancer/base/Test_BalancerSwapRateOracleBase.t.sol
# Conflicts: # script/Deploy.s.sol
# Conflicts: # script/Deploy.s.sol # src/factory/templates/liquidity-tranche/BalancerV3_GyroECLP_LT_DeploymentTemplate.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Remove the Balancer hook from the Liquidity Tranche pool
Note: CI fails because of
forge fmt. The files in this PR are formatted, but I didn't want to cloud the diff by checking in the 24 files that are also unformatted in main. Since fmt checks the whole tree, it doesn't get past that step in CI, though if you pull the branch locally, you'll see that they all pass.I did merge in changes to main right before... which sent me down quite the rabbit hole, as 191 tests suddenly started failing: but only when the whole suite was run. Everything worked when run individually. This was alarming, as originally I thought maybe I was wrong that nothing in Day needed it.
Thankfully no. It was because I inherited tests that assumed a different setup fixture, so disconnecting that inheritance fixed it. That also led to a whole investigation of premium conservation, as it initially looked like changing the premium add frequency drastically affected the premium amount added. (Not really though, see below.)
I don't like pushing PRs late at night, but I also don't want main to blow past me again, so I thought I'd better get this in :)
I have a few more (much smaller) ones lined up for tomorrow.
Summary
This proposes removing the accounting hook (
RoycoDayBalancerV3Hooks) from the Liquidity Tranche's Gyro E-CLP pool. The pool would be created without a hook, and externally initiated pool operations would no longer trigger a synchronization before execution.Why the hook is not needed
The hook's only effect was to run a kernel accounting sync before an external swap, add, or removal. It did not affect Day's own operations, which already perform their own syncs.
External pool operations read the senior rate through the kernel's
getRate. On a cache miss,getRatepreviews the current rate from the live senior and junior marks, so it does not require a pre-operation sync to be current. That rate depends on senior net asset value and senior share supply, neither of which is moved by an external pool operation.The other relevant reads are also independent of a pre-operation sync. The Liquidity Tranche mark, the liquidity check, redemption, and the premium calculation read from the manipulation-resistant Balancer oracle and the last committed checkpoint. None of those values need a sync around external pool activity.
Evidence
Two mainnet-fork tests in
test/RoycoHookNecessity.t.sol, run against the market's own hookless pool, substantiate this.The disconnect test creates a real gap between the last committed rate and the current rate. It then has a third party swap, add unbalanced twice, and remove on the pool. The test asserts that the rate read by the pool equals the rate a fresh sync would commit, and that it is not the stale committed value. It also confirms that the mark, liquidity utilization check, redemption, and premium remain correct.
The fuzz test covers 6,000 runs across senior moves from -50% to +50%, elapsed times from seconds to four weeks, and three overlay settings. In every case, the cache-miss rate equals both the committed sync rate and an independent recomputation to the wei. No counterexample was found. The full suite passes.
What removal buys
External swaps, adds, and removals would no longer depend on Day's sync succeeding. A paused hook or kernel, or a reverting price read, would no longer halt third-party pool activity. Pausing the hook also could no longer stop all pool traffic.
Removal also prevents an external swap from forcing Day to mint and reinvest premium inside a stranger's transaction.
Finally, the pool becomes an ordinary Balancer pool with a senior rate provider. Aggregators and the interface can route through it without extra integration, which supports the outside arbitrage assumed by the liquidity analysis.
The premium trade
Removing the hook reduces how often accounting is synced. External swaps, adds, and removals would no longer trigger a sync; only Day's own operations would.
This does not change any value a user reads at the moment of an operation. The senior rate read by an external operation is the current previewed rate. The Liquidity Tranche mark is a live Balancer oracle read. The liquidity check is computed at sync time from those live inputs. The tests in this change establish each of these properties directly.
When Day earns senior yield, it sets aside a slice of that yield for the Liquidity Tranche at each sync. Syncing less often does not reduce the slice that gets set aside. That amount is identical whether syncs are frequent or rare, verified equal to the wei, and asserted as a guard in the tests.
What changes is only the amount ultimately delivered to the Liquidity Tranche, and only slightly, in the tranche's favor. The size of the effect depends on how much yield accumulates between syncs. It is about 0.05% of the premium when yield over the period is around 1%. It grows in proportion to that yield, reaching about 0.4% at an 8% period yield and about 4% only at an extreme 80% period yield. Because premium is deployed at every sync, yield between syncs should stay small in normal operation, keeping the effect near the low end and shrinking it further as the yield per period decreases.
The delivered amount changes because the accounting uses the last committed values when calculating each slice. Syncing less often prices more of the window at the values standing at its start. That behavior exists with or without a hook; the hook only changes how often those syncs fire. The trade is therefore small, bounded, in the Liquidity Tranche's favor, and not newly introduced by the removal.
Separately, and independent of this change, delivered premium is sensitive to sync frequency at all. Under the hook, that meant it was also sensitive to external trading volume. That property is worth confirming as intended whether or not the hook is removed.
Regression guard
The fuzz test is the standing guard for the property this removal relies on: on a cache miss,
getRatereturns the same rate that a sync would commit across the tested state space.The staged-premium conservation tests guard that the mint is conserved across sync frequency.
A future change to the sync, the preview, or the senior-rate expression that breaks either property will fail these tests.