Clement/fee on swap#215
Conversation
Co-authored-by: Copilot <copilot@github.com>
- Removed fee collection logic from LidoARM tests and contracts, transitioning to immediate fee transfers during swaps. - Updated tests to reflect changes in fee accrual and total asset calculations, ensuring accurate assertions without relying on accrued fees. - Simplified modifiers and removed unnecessary fee-related assertions and functions across various test files. - Introduced a new deployment script for upgrading the OETH ARM to the swap-only fee accrual model.
…ity and consistency
| FeeData memory feeDataMem = feeData; | ||
| if (feeDataMem.fee == 0) return; | ||
| // Calculate the amount of fees to take in baseAsset terms | ||
| uint256 fees = amountIn * fee / FEE_SCALE; |
There was a problem hiding this comment.
This is charging a fee on the full base amount rather than just a fee on the discounted rate. Using Ethena as an example, if the ARM buys sUSDe, it will convert to a fair USDe amount by dividing by 1.23. It then only charges a 20% performance fee on the discounted amount.
performance fee = (sUSDe converted to fair USDe value - discounted USDe) * 20%
There was a problem hiding this comment.
If we ensure that the fee % is lower than half of the spread, then we have almost the same at the end no?
The idea behind using amountIn is that it reduces gas consumption because we don't convert sUSDe in USDe.
(sUSDe converted to fair USDe value - discounted USDe) * 20% is a % of the spread, while amountIn * fee / FEE_SCALE is a % of the amountIn but we ensure that this is lower than half of the spread.
I agree that fee will change slightly because of the increase of sUSDe value, but we can change the fee every 6 months to match that?
Summary
Replaces the performance-fee-on-asset-growth model with a simpler swap fee charged at swap time.
New fee logic
The fee is now applied directly inside
_accrueSwapFee(AbstractARM.sol), called at the end of every swap:amountIn:fees = amountIn * fee / FEE_SCALE.feeCollectorduring the swap — no storage accumulation, nofeesAccruedtracking, no separatecollectFees()call to coordinate.As a result,
totalAssets()no longer needs to net out outstanding fees,setFee()no longer auto-collects, and the contract surface is significantly smaller.Storage / API changes
feesAccrued(),collectFees(),lastAvailableAssets(storage).lastAvailableAssets→_deprecated_lastAvailableAssetsto preserve the storage layout. A backwards-compatibility getterlastAvailableAssets()now simply returnsint128(totalAssets()).Migration
Upgrade scripts ship for each ARM:
028_UpgradeLidoARMSwapFeeScript029_UpgradeEtherFiARMSwapFeeScript030_UpgradeOETHARMSwapFeeScriptsonic/006_UpgradeOriginARMSwapFeeScriptEach proposal calls
collectFees()first to drain any legacy accrued fees, thenupgradeTo(newImpl).Gas consumption