fix(staking,payments): token/deposit-unit-consistent amounts for adapter-backed assets#186
Merged
Merged
Conversation
…adapter-backed assets Two adapter unit-mismatch bugs surface only for non-1:1 (rebasing) adapters (StandardAssetAdapter is 1:1, so existing tests are unaffected). HIGH — LiquidDelegationVault share mint: deposit()/mint() computed shares from the RAW token `assets`, but totalAssets() is denominated in staking DEPOSIT-UNITS (adapter shares) and the delegation pool actually grows by the `credited` deposit-units that `_depositAndDelegate` already computes (and previously discarded for the mint). For a rebasing adapter raw != credited, so a post-rebase depositor minted more shares than they funded, diluting earlier holders. Fix: snapshot the pre-deposit rate, then mint against the CREDITED deposit-units. mint() converts the required deposit-units to the raw token cost via the adapter (new _depositUnitsToAssets) and mints on credited. 1:1 assets are unchanged. MEDIUM — PaymentsEffectiveExposure USD weighting: getOperatorStakeForAsset returns deposit-units (adapter shares), which were fed straight into oracle.toUSD(token, amount) — toUSD expects TOKEN units. Under a rebasing adapter the USD-normalized exposure (and thus the payout weight) was mis-priced. Fix: convert deposit-units -> tokens via adapter.sharesToAssets (new _depositUnitsToTokenAmount) before toUSD; 1:1 for native / non-adapter. Tests: test/audit/batch3/AdapterUnitMismatch.t.sol — full-stack rebasing vault deposit (F1: post-rebase deposit mints ~half the shares, not equal) and a mocked exposure harness (F2: exposure priced on converted token units). No regressions across LiquidDelegation, AssetAdapter, LiquidVault, AdaptersMedLow, Payments, PerAssetExposure, RFQ/Quote distribution, Integration, subscriptions, MultiAsset.
tangletools
approved these changes
Jun 19, 2026
tangletools
left a comment
Contributor
There was a problem hiding this comment.
✅ Auto-approved PR — 0540f157
Blanket team auto-approval is enabled for this reviewer service.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.
tangletools · auto-approval · reason: blanket_auto_approve · 2026-06-19T21:39:32Z
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.
Summary
Two adapter unit-mismatch bugs that surface only for non-1:1 (rebasing) adapters (
StandardAssetAdapteris 1:1, so the existing suites are unaffected — butRebasingAssetAdapteris registerable, so these are live, not latent). Both validated end-to-end against the code before fixing.HIGH —
LiquidDelegationVaultmints shares off the raw token amountdeposit()/mint()computed shares from the raw tokenassets, buttotalAssets()is denominated in staking deposit-units (adapter shares), and the delegation pool actually grows by thecrediteddeposit-units that_depositAndDelegatealready computes (and then discarded for the mint). For a rebasing adapterraw != credited, so a depositor after a positive rebase minted more shares than they funded, diluting earlier holders.Fix: snapshot the pre-deposit rate, then mint against the credited deposit-units.
mint()converts the required deposit-units to the raw token cost via the adapter (_depositUnitsToAssets) and mints oncredited. 1:1 assets are unchanged.(The finding's headline — "not share-adjusted" — is imprecise;
deposit()does callconvertToShares. The real defect is the unit of its numerator: raw tokens vs the deposit-unit denomination oftotalAssets()/credited.)MEDIUM —
PaymentsEffectiveExposurefeeds deposit-units into a token-unit statgetOperatorStakeForAssetreturns deposit-units (adapter shares;_operatorDelegatedAggregateis incremented by credited units), which_calculateEffectiveExposuresfed straight intooracle.toUSD(token, amount)—toUSDexpects token units. Under a rebasing adapter the USD-normalized exposure (and thus the operator's payout weight) was mis-priced.Fix: convert deposit-units → tokens via
adapter.sharesToAssets(_depositUnitsToTokenAmount) beforetoUSD; 1:1 for native / non-adapter assets; fails open to the input on a missing adapter so a misconfig degrades rather than reverting distribution.Tests (
test/audit/batch3/AdapterUnitMismatch.t.sol)RebasingAssetAdapter+LiquidDelegationVault; user1 deposits at 1:1, the token rebases +100%, user2 deposits the same nominal amount → mints ~half the shares (pre-fix: equal → dilution). TheassertLtfails under the bug._calculateEffectiveExposureswith mocked staking/adapter/oracle; only the token-unittoUSDcall is mocked, so the pre-fix deposit-unit path reverts → exposure is asserted to be priced on converted token units.No regressions: LiquidDelegation, AssetAdapter, LiquidVault, AdaptersMedLow, AdapterChange, RebasingShareScalePoC, Payments, PerAssetExposure, RFQ/Quote distribution, Integration, EndToEndSubscription, MultiAssetDelegation (243 tests).