Skip to content

Commit 676a67c

Browse files
Merge pull request #190 from bancorprotocol/vortex-add-multiple-controllers
Vortex add multiple controllers
2 parents e8502e6 + 53550f3 commit 676a67c

File tree

13 files changed

+314
-39
lines changed

13 files changed

+314
-39
lines changed

contracts/vortex/CarbonVortex.sol

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
pragma solidity 0.8.19;
33

44
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
5+
import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
56
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
67
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
78
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
@@ -36,12 +37,12 @@ import { PPM_RESOLUTION, MAX_GAP } from "../utility/Constants.sol";
3637
contract 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

contracts/vortex/interfaces/ICarbonVortex.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ interface ICarbonVortex is IUpgradeable {
2020
error InsufficientNativeTokenSent();
2121
error InsufficientAmountForTrading();
2222
error UnnecessaryNativeTokenReceived();
23+
error ControllerAlreadyAdded();
24+
error ControllerDoesNotExist();
2325

2426
struct Price {
2527
uint128 sourceAmount;
@@ -101,6 +103,16 @@ interface ICarbonVortex is IUpgradeable {
101103
*/
102104
event TransferAddressUpdated(address indexed prevTransferAddress, address indexed newTransferAddress);
103105

106+
/**
107+
* @notice triggered when a controller address is added
108+
*/
109+
event ControllerAdded(address indexed controller);
110+
111+
/**
112+
* @notice triggered when a controller address is removed
113+
*/
114+
event ControllerRemoved(address indexed controller);
115+
104116
/**
105117
* @notice returns the rewards ppm
106118
*/
@@ -203,6 +215,11 @@ interface ICarbonVortex is IUpgradeable {
203215
*/
204216
function transferAddress() external view returns (address);
205217

218+
/**
219+
* @notice returns the controller addresses
220+
*/
221+
function controllers() external view returns (address[] memory);
222+
206223
/**
207224
* @notice trades *targetToken* for *targetAmount* of *token* based on the current token price (trade by target amount)
208225
* @notice if token == *targetToken*, trades *finalTargetToken* for amount of *targetToken* and also

deploy/tests/mainnet/0004-fee-burner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describeDeployment(__filename, () => {
3434
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
3535
const carbonVortexImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
3636
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
37-
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
37+
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
3838
await expect(tx.wait()).to.be.reverted;
3939
});
4040
});

deploy/tests/mainnet/0006-carbon-vortex-upgrade.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describeDeployment(__filename, () => {
3939
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
4040
const carbonVortexImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
4141
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
42-
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
42+
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
4343
await expect(tx.wait()).to.be.reverted;
4444
});
4545
});

deploy/tests/mainnet/0012-carbon-vortex-upgrade.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describeDeployment(__filename, () => {
3939
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
4040
const carbonVortexImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
4141
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
42-
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
42+
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
4343
await expect(tx.wait()).to.be.reverted;
4444
});
4545
});

deploy/tests/mainnet/0016-carbon-vortex-upgrade.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describeDeployment(__filename, () => {
2222
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
2323
const carbonControllerImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
2424
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
25-
const tx = await carbonControllerImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
25+
const tx = await carbonControllerImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
2626
await expect(tx.wait()).to.be.reverted;
2727
});
2828

deploy/tests/mainnet/0017-carbon-vortex-upgrade.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describeDeployment(__filename, () => {
2222
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
2323
const carbonControllerImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
2424
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
25-
const tx = await carbonControllerImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
25+
const tx = await carbonControllerImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
2626
await expect(tx.wait()).to.be.reverted;
2727
});
2828

test/carbon/accuracy/data/ArbitraryTrade.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30799,4 +30799,4 @@
3079930799
"spec": "138.888888888889"
3080030800
}
3080130801
}
30802-
]
30802+
]

test/carbon/accuracy/data/EthUsdcTrade.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3023,4 +3023,4 @@
30233023
"spec": "118349999999.999999999941"
30243024
}
30253025
}
3026-
]
3026+
]

test/carbon/accuracy/data/ExtremeSrcTrade.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7167,4 +7167,4 @@
71677167
"spec": "924248818871128843570750.388118986304"
71687168
}
71697169
}
7170-
]
7170+
]

0 commit comments

Comments
 (0)