@@ -5,28 +5,33 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
55import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol " ;
66import {Controllable, IControllable} from "../core/base/Controllable.sol " ;
77import {IXSTBL} from "../interfaces/IXSTBL.sol " ;
8- import {IRevenueRouter} from "../interfaces/IRevenueRouter.sol " ;
8+ import {IRevenueRouter, EnumerableSet } from "../interfaces/IRevenueRouter.sol " ;
99import {ISwapper} from "../interfaces/ISwapper.sol " ;
1010import {IPlatform} from "../interfaces/IPlatform.sol " ;
1111import {IXStaking} from "../interfaces/IXStaking.sol " ;
1212import {IFeeTreasury} from "../interfaces/IFeeTreasury.sol " ;
13+ import {IStabilityVault} from "../interfaces/IStabilityVault.sol " ;
14+ import {IFactory} from "../interfaces/IFactory.sol " ;
1315import {IPool} from "../integrations/aave/IPool.sol " ;
1416import {IAToken} from "../integrations/aave/IAToken.sol " ;
15- import {IStabilityVault} from "../interfaces/IStabilityVault.sol " ;
1617
1718/// @title Platform revenue distributor
1819/// Changelog:
20+ /// 1.5.0: processAccumulatedVaults
21+ /// 1.4.0: processUnitRevenue use try..catch for Aave aToken withdrawals; view vaultsAccumulated
22+ /// 1.3.0: vaultsAccumulated; updateUnit; units
1923/// 1.2.0: Units; Aave unit; all revenue via buy-back
2024/// @author Alien Deployer (https://github.com/a17)
2125contract RevenueRouter is Controllable , IRevenueRouter {
2226 using SafeERC20 for IERC20 ;
27+ using EnumerableSet for EnumerableSet.AddressSet;
2328
2429 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
2530 /* CONSTANTS */
2631 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
2732
2833 /// @inheritdoc IControllable
29- string public constant VERSION = "1.2 .0 " ;
34+ string public constant VERSION = "1.5 .0 " ;
3035
3136 // keccak256(abi.encode(uint256(keccak256("erc7201:stability.RevenueRouter")) - 1)) & ~bytes32(uint256(0xff))
3237 bytes32 private constant REVENUE_ROUTER_STORAGE_LOCATION =
@@ -58,7 +63,21 @@ contract RevenueRouter is Controllable, IRevenueRouter {
5863 RevenueRouterStorage storage $ = _getRevenueRouterStorage ();
5964 uint unitIndex = $.units.length ;
6065 $.units.push (Unit ({unitType: unitType, name: name, pendingRevenue: 0 , feeTreasury: feeTreasury}));
61- emit NewUnit (unitIndex, name, feeTreasury);
66+ emit AddedUnit (unitIndex, unitType, name, feeTreasury);
67+ }
68+
69+ /// @inheritdoc IRevenueRouter
70+ function updateUnit (
71+ uint unitIndex ,
72+ UnitType unitType ,
73+ string calldata name ,
74+ address feeTreasury
75+ ) external onlyGovernanceOrMultisig {
76+ RevenueRouterStorage storage $ = _getRevenueRouterStorage ();
77+ $.units[unitIndex].unitType = unitType;
78+ $.units[unitIndex].name = name;
79+ $.units[unitIndex].feeTreasury = feeTreasury;
80+ emit UpdatedUnit (unitIndex, unitType, name, feeTreasury);
6281 }
6382
6483 /// @inheritdoc IRevenueRouter
@@ -138,27 +157,15 @@ contract RevenueRouter is Controllable, IRevenueRouter {
138157
139158 /// @inheritdoc IRevenueRouter
140159 function processFeeVault (address vault , uint amount ) external {
141- RevenueRouterStorage storage $ = _getRevenueRouterStorage ();
142- address stbl = $.stbl;
143- ISwapper swapper = ISwapper (IPlatform (platform ()).swapper ());
144- address [] memory assets = IStabilityVault (vault).assets ();
145- uint len = assets.length ;
146- try IStabilityVault (vault).withdrawAssets (assets, amount, new uint [](len), address (this ), msg .sender ) {
147- uint stblBalanceWas = IERC20 (stbl).balanceOf (address (this ));
148- for (uint i; i < len; ++ i) {
149- address asset = assets[i];
150- if (asset != stbl) {
151- uint threshold = swapper.threshold (asset);
152- uint amountToSwap = IERC20 (asset).balanceOf (address (this ));
153- if (amountToSwap > threshold) {
154- IERC20 (asset).forceApprove (address (swapper), amountToSwap);
155- try swapper.swap (asset, stbl, amountToSwap, 20_000 ) {} catch {}
156- }
157- }
160+ if (_isDeployedVault (vault)) {
161+ RevenueRouterStorage storage $ = _getRevenueRouterStorage ();
162+ if (IStabilityVault (vault).lastBlockDefenseDisabled ()) {
163+ _withdrawVaultSharesAndBuyBack ($, vault, amount, msg .sender );
164+ } else {
165+ IERC20 (vault).safeTransferFrom (msg .sender , address (this ), amount);
166+ $.vaultsAccumulated.add (vault);
158167 }
159- uint stblGot = IERC20 (stbl).balanceOf (address (this )) - stblBalanceWas;
160- IERC20 (stbl).safeTransfer ($.feeTreasury, stblGot);
161- } catch {}
168+ }
162169 }
163170
164171 /// @inheritdoc IRevenueRouter
@@ -176,9 +183,12 @@ contract RevenueRouter is Controllable, IRevenueRouter {
176183 for (uint i; i < outAssets.length ; ++ i) {
177184 address asset = IAToken (outAssets[i]).UNDERLYING_ASSET_ADDRESS ();
178185 if (amounts[i] != 0 ) {
179- IPool (IAToken (outAssets[i]).POOL ()).withdraw (asset, amounts[i], address (this ));
180- IERC20 (asset).forceApprove (address (swapper), amounts[i]);
181- try swapper.swap (asset, stbl, amounts[i], 20_000 ) {} catch {}
186+ // here we use all because extra amounts from failed withdraw can be
187+ amounts[i] = IERC20 (outAssets[i]).balanceOf (address (this ));
188+ try IPool (IAToken (outAssets[i]).POOL ()).withdraw (asset, amounts[i], address (this )) {
189+ IERC20 (asset).forceApprove (address (swapper), amounts[i]);
190+ try swapper.swap (asset, stbl, amounts[i], 20_000 ) {} catch {}
191+ } catch {}
182192 }
183193 }
184194
@@ -208,10 +218,33 @@ contract RevenueRouter is Controllable, IRevenueRouter {
208218 }
209219 }
210220
221+ /// @inheritdoc IRevenueRouter
222+ function processAccumulatedVaults (uint maxVaultsForWithdraw ) external {
223+ RevenueRouterStorage storage $ = _getRevenueRouterStorage ();
224+
225+ // process accumulatedVaults
226+ address [] memory _vaultsAccumulated = $.vaultsAccumulated.values ();
227+ uint len = _vaultsAccumulated.length ;
228+ for (uint i; i < len; ++ i) {
229+ if (i == maxVaultsForWithdraw) {
230+ break ;
231+ }
232+ _withdrawVaultSharesAndBuyBack (
233+ $, _vaultsAccumulated[i], IERC20 (_vaultsAccumulated[i]).balanceOf (address (this )), address (this )
234+ );
235+ $.vaultsAccumulated.remove (_vaultsAccumulated[i]);
236+ }
237+ }
238+
211239 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
212240 /* VIEW FUNCTIONS */
213241 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
214242
243+ /// @inheritdoc IRevenueRouter
244+ function units () external view returns (Unit[] memory ) {
245+ return _getRevenueRouterStorage ().units;
246+ }
247+
215248 /// @inheritdoc IRevenueRouter
216249 function getPeriod () public view returns (uint ) {
217250 return (block .timestamp / 1 weeks);
@@ -237,10 +270,54 @@ contract RevenueRouter is Controllable, IRevenueRouter {
237270 return _getRevenueRouterStorage ().aavePools;
238271 }
239272
273+ /// @inheritdoc IRevenueRouter
274+ function vaultsAccumulated () external view returns (address [] memory ) {
275+ return _getRevenueRouterStorage ().vaultsAccumulated.values ();
276+ }
277+
240278 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
241279 /* INTERNAL LOGIC */
242280 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
243281
282+ function _withdrawVaultSharesAndBuyBack (
283+ RevenueRouterStorage storage $,
284+ address vault ,
285+ uint amount ,
286+ address owner
287+ ) internal {
288+ address stbl = $.stbl;
289+ ISwapper swapper = ISwapper (IPlatform (platform ()).swapper ());
290+ address [] memory assets = IStabilityVault (vault).assets ();
291+ uint len = assets.length ;
292+ try IStabilityVault (vault).withdrawAssets (assets, amount, new uint [](len), address (this ), owner) {
293+ uint stblBalanceWas = IERC20 (stbl).balanceOf (address (this ));
294+ for (uint i; i < len; ++ i) {
295+ address asset = assets[i];
296+ if (asset != stbl) {
297+ uint threshold = swapper.threshold (asset);
298+ uint amountToSwap = IERC20 (asset).balanceOf (address (this ));
299+ if (amountToSwap > threshold) {
300+ IERC20 (asset).forceApprove (address (swapper), amountToSwap);
301+ try swapper.swap (asset, stbl, amountToSwap, 20_000 ) {} catch {}
302+ }
303+ }
304+ }
305+ uint stblGot = IERC20 (stbl).balanceOf (address (this )) - stblBalanceWas;
306+ IERC20 (stbl).safeTransfer ($.feeTreasury, stblGot);
307+ } catch {}
308+ }
309+
310+ function _isDeployedVault (address vault ) internal view returns (bool ) {
311+ address [] memory deployedVaults = IFactory (IPlatform (platform ()).factory ()).deployedVaults ();
312+ uint len = deployedVaults.length ;
313+ for (uint i; i < len; ++ i) {
314+ if (deployedVaults[i] == vault) {
315+ return true ;
316+ }
317+ }
318+ return false ;
319+ }
320+
244321 function _getRevenueRouterStorage () internal pure returns (RevenueRouterStorage storage $) {
245322 //slither-disable-next-line assembly
246323 assembly {
0 commit comments