22pragma solidity 0.8.19 ;
33
44import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol " ;
5+ import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol " ;
56import { Address } from "@openzeppelin/contracts/utils/Address.sol " ;
67import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol " ;
78import { Math } from "@openzeppelin/contracts/utils/math/Math.sol " ;
@@ -36,12 +37,12 @@ import { PPM_RESOLUTION, MAX_GAP } from "../utility/Constants.sol";
3637contract CarbonVortex is ICarbonVortex , Upgradeable , ReentrancyGuardUpgradeable , Utils {
3738 using Address for address payable ;
3839 using SafeCast for uint256 ;
40+ using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
3941
4042 uint128 private constant INITIAL_PRICE_SOURCE_AMOUNT = type (uint128 ).max;
4143 uint128 private constant INITIAL_PRICE_TARGET_AMOUNT = 1e12 ;
4244
43- // addresses for token withdrawal
44- ICarbonController private immutable _carbonController;
45+ // immutable vault address for token withdrawal
4546 IVault private immutable _vault;
4647
4748 // first token for swapping
@@ -89,19 +90,20 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
8990 // address for token collection - collects all swapped target/final target tokens
9091 address payable private _transferAddress;
9192
93+ // set of controller addresses from which fees can be withdrawn
94+ EnumerableSetUpgradeable.AddressSet private _controllers;
95+
9296 // upgrade forward-compatibility storage gap
93- uint256 [MAX_GAP - 8 ] private __gap;
97+ uint256 [MAX_GAP - 10 ] private __gap;
9498
9599 /**
96100 * @dev used to set immutable state variables
97101 */
98102 constructor (
99- ICarbonController carbonController ,
100103 IVault vault ,
101104 Token targetTokenInit ,
102105 Token finalTargetTokenInit
103106 ) validAddress (Token.unwrap (targetTokenInit)) {
104- _carbonController = carbonController;
105107 _vault = vault;
106108
107109 _targetToken = targetTokenInit;
@@ -112,26 +114,32 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
112114 /**
113115 * @dev fully initializes the contract and its parents
114116 */
115- function initialize (address payable transferAddressInit ) public initializer {
116- __CarbonVortex_init (transferAddressInit);
117+ function initialize (address payable transferAddressInit , address [] calldata controllersInit ) public initializer {
118+ __CarbonVortex_init (transferAddressInit, controllersInit );
117119 }
118120
119121 // solhint-disable func-name-mixedcase
120122
121123 /**
122124 * @dev initializes the contract and its parents
123125 */
124- function __CarbonVortex_init (address payable transferAddressInit ) internal onlyInitializing {
126+ function __CarbonVortex_init (
127+ address payable transferAddressInit ,
128+ address [] calldata controllersInit
129+ ) internal onlyInitializing {
125130 __Upgradeable_init ();
126131 __ReentrancyGuard_init ();
127132
128- __CarbonVortex_init_unchained (transferAddressInit);
133+ __CarbonVortex_init_unchained (transferAddressInit, controllersInit );
129134 }
130135
131136 /**
132137 * @dev performs contract-specific initialization
133138 */
134- function __CarbonVortex_init_unchained (address payable transferAddressInit ) internal onlyInitializing {
139+ function __CarbonVortex_init_unchained (
140+ address payable transferAddressInit ,
141+ address [] calldata controllersInit
142+ ) internal onlyInitializing {
135143 // set rewards PPM to 1000
136144 _setRewardsPPM (1000 );
137145 // set price reset multiplier to 2x
@@ -150,6 +158,8 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
150158 _setMinTokenSaleAmount (_targetToken, uint128 (10 ) * uint128 (10 ) ** _targetToken.decimals ());
151159 // set transfer address
152160 _setTransferAddress (transferAddressInit);
161+ // set controller addresses
162+ _addControllerAddresses (controllersInit);
153163 }
154164
155165 /**
@@ -304,6 +314,28 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
304314 _setTransferAddress (newTransferAddress);
305315 }
306316
317+ /**
318+ * @notice add a controller address
319+ *
320+ * requirements:
321+ *
322+ * - the caller must be the current admin of the contract
323+ */
324+ function addController (address controller ) external onlyAdmin {
325+ _addController (controller);
326+ }
327+
328+ /**
329+ * @notice remove a controller address
330+ *
331+ * requirements:
332+ *
333+ * - the caller must be the current admin of the contract
334+ */
335+ function removeController (address controller ) external onlyAdmin {
336+ _removeController (controller);
337+ }
338+
307339 /**
308340 * @dev withdraws funds held by the contract and sends them to an account
309341 *
@@ -363,13 +395,24 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
363395 return _transferAddress;
364396 }
365397
398+ /**
399+ * @inheritdoc ICarbonVortex
400+ */
401+ function controllers () external view returns (address [] memory ) {
402+ return _controllers.values ();
403+ }
404+
366405 /**
367406 * @inheritdoc ICarbonVortex
368407 */
369408 function availableTokens (Token token ) external view returns (uint256 ) {
370409 uint256 totalFees = 0 ;
371- if (address (_carbonController) != address (0 )) {
372- totalFees += _carbonController.accumulatedFees (token);
410+ uint256 controllersLength = _controllers.length ();
411+ if (controllersLength > 0 ) {
412+ for (uint256 i = 0 ; i < controllersLength; i = uncheckedInc (i)) {
413+ ICarbonController controller = ICarbonController (_controllers.at (i));
414+ totalFees += controller.accumulatedFees (token);
415+ }
373416 }
374417 if (address (_vault) != address (0 )) {
375418 totalFees += token.balanceOf (address (_vault));
@@ -389,18 +432,30 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
389432 uint256 [] memory rewardAmounts = new uint256 [](len);
390433 // cache rewardsPPM to save gas
391434 uint256 rewardsPPMValue = _rewardsPPM;
435+ // cache controllers length to save gas
436+ uint256 controllersLength = _controllers.length ();
437+ address [] memory __controllers;
392438
393439 // cache address checks to save gas
394- bool carbonControllerIsNotZero = address (_carbonController) != address ( 0 ) ;
440+ bool controllersNotEmpty = controllersLength != 0 ;
395441 bool vaultIsNotZero = address (_vault) != address (0 );
396442
443+ // cache controllers to save gas
444+ if (controllersNotEmpty) {
445+ __controllers = _controllers.values ();
446+ }
447+
397448 // withdraw fees from carbon vault
398449 for (uint256 i = 0 ; i < len; i = uncheckedInc (i)) {
399450 Token token = tokens[i];
400451 // withdraw token fees
401452 uint256 totalFeeAmount = 0 ;
402- if (carbonControllerIsNotZero) {
403- totalFeeAmount += _carbonController.withdrawFees (token, type (uint256 ).max, address (this ));
453+ if (controllersNotEmpty) {
454+ // withdraw fees from all controllers
455+ for (uint256 j = 0 ; j < controllersLength; j = uncheckedInc (j)) {
456+ ICarbonController controller = ICarbonController (__controllers[j]);
457+ totalFeeAmount += controller.withdrawFees (token, type (uint256 ).max, address (this ));
458+ }
404459 }
405460 if (vaultIsNotZero) {
406461 // get vault token balance
@@ -925,6 +980,32 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
925980 });
926981 }
927982
983+ function _addController (address controller ) private {
984+ // add the controller to the set ; revert if it already exists
985+ if (! _controllers.add (controller)) {
986+ revert ControllerAlreadyAdded ();
987+ }
988+ // emit event for controller address added
989+ emit ControllerAdded (controller);
990+ }
991+
992+ function _removeController (address controller ) private {
993+ // remove the controller from the set ; revert if it doesn't exist
994+ if (! _controllers.remove (controller)) {
995+ revert ControllerDoesNotExist ();
996+ }
997+ // emit event for controller address removed
998+ emit ControllerRemoved (controller);
999+ }
1000+
1001+ function _addControllerAddresses (address [] calldata __controllers ) private {
1002+ // add the new controllers
1003+ uint256 length = __controllers.length ;
1004+ for (uint256 i = 0 ; i < length; i = uncheckedInc (i)) {
1005+ _addController (__controllers[i]);
1006+ }
1007+ }
1008+
9281009 /**
9291010 * @dev returns true if the auction price is below or equal to the minimum possible price
9301011 * @dev check if timeElapsed / priceDecayHalfLife >= 128
0 commit comments