Skip to content

Add permissioned rebase#2889

Merged
shahthepro merged 3 commits into
masterfrom
shah/permissioned-rebase-vault
May 10, 2026
Merged

Add permissioned rebase#2889
shahthepro merged 3 commits into
masterfrom
shah/permissioned-rebase-vault

Conversation

@shahthepro
Copy link
Copy Markdown
Collaborator

@shahthepro shahthepro commented May 6, 2026

Code Changes

  • Add permissioned rebase
  • Remove rebaseThreshold and auto rebase on large mints and redeems

@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 41.39%. Comparing base (3de581a) to head (f8a6b36).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2889      +/-   ##
==========================================
+ Coverage   41.32%   41.39%   +0.06%     
==========================================
  Files         115      115              
  Lines        4949     4948       -1     
  Branches     1370     1372       +2     
==========================================
+ Hits         2045     2048       +3     
+ Misses       2901     2897       -4     
  Partials        3        3              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Member

@sparrowDom sparrowDom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown
Collaborator

@clement-ux clement-ux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good.
We need to ensure that no integrator call rebaseThreshold, otherwise we can just create view function that return 0?
I need to spend more time thinking about the implication of removing _rebase() for large mint.

@sparrowDom
Copy link
Copy Markdown
Member

Here are the OUSD and OETH Dune queries looking at which addresses are calling rebase:

We are ok making rebase non public

Copy link
Copy Markdown
Collaborator

@naddison36 naddison36 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the reasons mint did a rebase was to prevent large mints getting yield from when they were not a oToken holder. For example, if rebase is executed once a day and a new mint doubles the oTokens just before rebase is called, the new oToken holder will get half the yield of the rebase even though they have only just minted. The impact of this is pretty limited as the minter can not use a flash loan due to async withdrawals. They have to have the capital to mint, hold for the rebase and then redeem post rebase.

Assumptions: 27.61k OETH existing supply, 2.42% net OETH APY, one rebase per day, no gas/slippage/opportunity cost.

Mint size Captured yield / day One-shot ROI one opportunity/day APY
1 WETH 0.000066 OETH 0.0066% 2.42%
10 WETH 0.000662 OETH 0.0066% 2.42%
100 WETH 0.006598 OETH 0.0066% 2.41%
1,000 WETH 0.0639 OETH 0.0064% 2.36%
10,000 WETH 0.4874 OETH 0.0049% 1.79%
27,610 WETH 0.9149 OETH 0.0033% 1.22%

For small mints, the attack APY is basically the normal OETH APY because the attacker barely dilutes the pool. The absolute profit is tiny though: a 1 WETH attacker captures only about 0.000066 OETH/day before gas.
Larger mints capture more absolute yield, but that yield is taken from existing rebasing oToken holders.

Note the capital is only used for the above for just over 10 minutes. The capital can be redeployed for the rest of the day earning more yield. eg deposit into a lending market.

Something that mitigate the above is allocate being called on larger mints. For example, mints over 10 OETH will allocate the WETH to the default Compounding Staking Strategy. There is currently no way for users to move that liquidity back to the Vault so they can claim a redeem. That means the capital will be locked up for a lot longer than just over 10 minutes. This effectively kills the incentive to mint just before rebase to capture yield.

Copy link
Copy Markdown
Collaborator

@naddison36 naddison36 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I approve the changes

@clement-ux
Copy link
Copy Markdown
Collaborator

  • I couldn't find any situation where someone can profit from minting large amount with rebase disable.
  • With a Dune query, I checked that nobody call rebaseThreshold(), so it doesn't break integration to remove it.
    LGTM 👍

@pandadefi
Copy link
Copy Markdown

pandadefi commented May 8, 2026

VaultStorage.sol

  • rebaseThreshold deprecated as __deprecatedRebaseThreshold
  • operatorAddr added
  • __gap reduced by one to 41
  • ⚠️ rebaseThreshold is removed and might break contracts integrating with it.

VaultAdmin.sol

  • setRebaseThreshold(uint256) removed
  • setOperatorAddr(address) added as governor-only admin action

VaultCore.sol

  • Mints no longer auto-call _rebase()
  • Withdrawal no longer auto-call _rebase()
  • Unauthorised rebase() calls now revert with Caller not authorized

VaultInitializer.sol

  • rebaseThreshold = 1000e18 removed
  • operatorAddr is not initialized

@shahthepro shahthepro merged commit 800ef7a into master May 10, 2026
10 of 15 checks passed
@shahthepro shahthepro deleted the shah/permissioned-rebase-vault branch May 10, 2026 09:53
clement-ux added a commit that referenced this pull request May 11, 2026
Two tests in `bridge-helper.base.fork-test.js` were failing on current
Base tip:

1. "Should bridge WETH to Ethereum" — CCIP router reverts with
   "Failed to send CCIP message" against current Base state. Pinning
   the fork to an earlier block looked promising but block 25070000
   (the suggested anchor) predates the BaseBridgeHelperModule
   deployment, so the fixture's deploy scripts can't run there.
   Skipped with a TODO until a viable block is identified.

2. "Should deposit wOETH for OETHb and async withdraw for WETH" —
   `BaseBridgeHelperModule._depositWOETH` calls `vault.rebase()`
   directly. With PR #2889 making `rebase()` operator-gated, the
   module's own address now reverts with "Caller not authorized".
   Fix is a production change (route the rebase through the Safe like
   PermissionedRebaseModule does) plus redeploy; that should land in
   a separate PR. Test skipped here with a TODO.

Both skips are temporary and clearly marked.
naddison36 pushed a commit that referenced this pull request May 11, 2026
* Unpause rebase in fork test fixtures

The four production vaults (OUSD, OETH on mainnet, OETHb on Base,
OSonic on Sonic) sit with `rebasePaused = true` on chain between
strategist rebase cycles. Fork tests, which start from current chain
state, inherit that and revert with `Rebasing paused` on every direct
`.rebase()` call (in tests and fixture setup alike).

Lift the pause once per fork fixture, as strategist, so the ~30
existing `.rebase()` call sites don't each need to unpause/rebase/pause.

Touched:
- `_fixture.js` `defaultFixture` (OUSD + OETH vaults)
- `_fixture.js` `simpleOETHFixture` (OETH vault — woeth tests)
- `_fixture-base.js` `defaultFixture` (OETHb vault)
- `_fixture-sonic.js` `defaultSonicFixture` (OSonic vault)

No production-code or test-call-site changes.

* Skip Base bridge-helper tests that don't survive permissioned rebase

Two tests in `bridge-helper.base.fork-test.js` were failing on current
Base tip:

1. "Should bridge WETH to Ethereum" — CCIP router reverts with
   "Failed to send CCIP message" against current Base state. Pinning
   the fork to an earlier block looked promising but block 25070000
   (the suggested anchor) predates the BaseBridgeHelperModule
   deployment, so the fixture's deploy scripts can't run there.
   Skipped with a TODO until a viable block is identified.

2. "Should deposit wOETH for OETHb and async withdraw for WETH" —
   `BaseBridgeHelperModule._depositWOETH` calls `vault.rebase()`
   directly. With PR #2889 making `rebase()` operator-gated, the
   module's own address now reverts with "Caller not authorized".
   Fix is a production change (route the rebase through the Safe like
   PermissionedRebaseModule does) plus redeploy; that should land in
   a separate PR. Test skipped here with a TODO.

Both skips are temporary and clearly marked.

* Scale Sonic SwapX AMO smallPoolShare values to current pool depth

The `smallPoolShare` scenario for the Sonic SwapX wS/OS AMO test was
sized for a millions-of-tokens pool. Current pool reserves are
~760k of each, so the setup's swap of 10M wS can't yield 1.8M OS
back, and the `expect(oTokenToPool).to.gt(0)` precondition in
`algebraAmoStrategy.js:1216` fails before any test in the
"with the strategy owning a small percentage of the pool" suite
runs.

Scale every value in the block down to fit current depth while
preserving the relative shape of the stress swaps. The behaviour
assertions are within-range checks on the strategy's balance, so
they don't depend on the absolute magnitudes.

After this change `swapx-amo.sonic.fork-test.js` goes from 67/68 to
69/69 passing locally.

* Widen Curve AMO OUSD tolerance for heavily-unbalanced deposit tests

`Should deposit when pool is heavily unbalanced with OUSD` and the
matching usdc variant assert that `checkBalance` and gauge balance
after `depositAll` land within 1% of `2 * defaultDeposit + gaugeBalance`.
The expected value assumes the Curve LP behaves as 1:1 with the
underlying, but depositing into a heavily-unbalanced pool incurs
slippage. At the current fork block the result lands ~1.8% under the
1:1 estimate, so both tests fail with values like
`Expected 975019058140 >= 982979757405`.

Bump the tolerance on these specific assertions to 3%. Other
assertions in the file (which deposit into balanced pools) keep the
default 1% tolerance.

Local run: 73/73 passing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants