Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 96 additions & 15 deletions contracts/vortex/CarbonVortex.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.19;

import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
Expand Down Expand Up @@ -36,12 +37,12 @@ import { PPM_RESOLUTION, MAX_GAP } from "../utility/Constants.sol";
contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable, Utils {
using Address for address payable;
using SafeCast for uint256;
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

uint128 private constant INITIAL_PRICE_SOURCE_AMOUNT = type(uint128).max;
uint128 private constant INITIAL_PRICE_TARGET_AMOUNT = 1e12;

// addresses for token withdrawal
ICarbonController private immutable _carbonController;
// immutable vault address for token withdrawal
IVault private immutable _vault;

// first token for swapping
Expand Down Expand Up @@ -89,19 +90,20 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
// address for token collection - collects all swapped target/final target tokens
address payable private _transferAddress;

// set of controller addresses from which fees can be withdrawn
EnumerableSetUpgradeable.AddressSet private _controllers;

// upgrade forward-compatibility storage gap
uint256[MAX_GAP - 8] private __gap;
uint256[MAX_GAP - 10] private __gap;

/**
* @dev used to set immutable state variables
*/
constructor(
ICarbonController carbonController,
IVault vault,
Token targetTokenInit,
Token finalTargetTokenInit
) validAddress(Token.unwrap(targetTokenInit)) {
_carbonController = carbonController;
_vault = vault;

_targetToken = targetTokenInit;
Expand All @@ -112,26 +114,32 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
/**
* @dev fully initializes the contract and its parents
*/
function initialize(address payable transferAddressInit) public initializer {
__CarbonVortex_init(transferAddressInit);
function initialize(address payable transferAddressInit, address[] calldata controllersInit) public initializer {
__CarbonVortex_init(transferAddressInit, controllersInit);
}

// solhint-disable func-name-mixedcase

/**
* @dev initializes the contract and its parents
*/
function __CarbonVortex_init(address payable transferAddressInit) internal onlyInitializing {
function __CarbonVortex_init(
address payable transferAddressInit,
address[] calldata controllersInit
) internal onlyInitializing {
__Upgradeable_init();
__ReentrancyGuard_init();

__CarbonVortex_init_unchained(transferAddressInit);
__CarbonVortex_init_unchained(transferAddressInit, controllersInit);
}

/**
* @dev performs contract-specific initialization
*/
function __CarbonVortex_init_unchained(address payable transferAddressInit) internal onlyInitializing {
function __CarbonVortex_init_unchained(
address payable transferAddressInit,
address[] calldata controllersInit
) internal onlyInitializing {
// set rewards PPM to 1000
_setRewardsPPM(1000);
// set price reset multiplier to 2x
Expand All @@ -150,6 +158,8 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
_setMinTokenSaleAmount(_targetToken, uint128(10) * uint128(10) ** _targetToken.decimals());
// set transfer address
_setTransferAddress(transferAddressInit);
// set controller addresses
_addControllerAddresses(controllersInit);
}

/**
Expand Down Expand Up @@ -304,6 +314,28 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
_setTransferAddress(newTransferAddress);
}

/**
* @notice add a controller address
*
* requirements:
*
* - the caller must be the current admin of the contract
*/
function addController(address controller) external onlyAdmin {
_addController(controller);
}

/**
* @notice remove a controller address
*
* requirements:
*
* - the caller must be the current admin of the contract
*/
function removeController(address controller) external onlyAdmin {
_removeController(controller);
}

/**
* @dev withdraws funds held by the contract and sends them to an account
*
Expand Down Expand Up @@ -363,13 +395,24 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
return _transferAddress;
}

/**
* @inheritdoc ICarbonVortex
*/
function controllers() external view returns (address[] memory) {
return _controllers.values();
}

/**
* @inheritdoc ICarbonVortex
*/
function availableTokens(Token token) external view returns (uint256) {
uint256 totalFees = 0;
if (address(_carbonController) != address(0)) {
totalFees += _carbonController.accumulatedFees(token);
uint256 controllersLength = _controllers.length();
if (controllersLength > 0) {
for (uint256 i = 0; i < controllersLength; i = uncheckedInc(i)) {
ICarbonController controller = ICarbonController(_controllers.at(i));
totalFees += controller.accumulatedFees(token);
}
}
if (address(_vault) != address(0)) {
totalFees += token.balanceOf(address(_vault));
Expand All @@ -389,18 +432,30 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
uint256[] memory rewardAmounts = new uint256[](len);
// cache rewardsPPM to save gas
uint256 rewardsPPMValue = _rewardsPPM;
// cache controllers length to save gas
uint256 controllersLength = _controllers.length();
address[] memory __controllers;

// cache address checks to save gas
bool carbonControllerIsNotZero = address(_carbonController) != address(0);
bool controllersNotEmpty = controllersLength != 0;
bool vaultIsNotZero = address(_vault) != address(0);

// cache controllers to save gas
if (controllersNotEmpty) {
__controllers = _controllers.values();
}

// withdraw fees from carbon vault
for (uint256 i = 0; i < len; i = uncheckedInc(i)) {
Token token = tokens[i];
// withdraw token fees
uint256 totalFeeAmount = 0;
if (carbonControllerIsNotZero) {
totalFeeAmount += _carbonController.withdrawFees(token, type(uint256).max, address(this));
if (controllersNotEmpty) {
// withdraw fees from all controllers
for (uint256 j = 0; j < controllersLength; j = uncheckedInc(j)) {
ICarbonController controller = ICarbonController(__controllers[j]);
totalFeeAmount += controller.withdrawFees(token, type(uint256).max, address(this));
}
}
if (vaultIsNotZero) {
// get vault token balance
Expand Down Expand Up @@ -925,6 +980,32 @@ contract CarbonVortex is ICarbonVortex, Upgradeable, ReentrancyGuardUpgradeable,
});
}

function _addController(address controller) private {
// add the controller to the set ; revert if it already exists
if (!_controllers.add(controller)) {
revert ControllerAlreadyAdded();
}
// emit event for controller address added
emit ControllerAdded(controller);
}

function _removeController(address controller) private {
// remove the controller from the set ; revert if it doesn't exist
if (!_controllers.remove(controller)) {
revert ControllerDoesNotExist();
}
// emit event for controller address removed
emit ControllerRemoved(controller);
}

function _addControllerAddresses(address[] calldata __controllers) private {
// add the new controllers
uint256 length = __controllers.length;
for (uint256 i = 0; i < length; i = uncheckedInc(i)) {
_addController(__controllers[i]);
}
}

/**
* @dev returns true if the auction price is below or equal to the minimum possible price
* @dev check if timeElapsed / priceDecayHalfLife >= 128
Expand Down
17 changes: 17 additions & 0 deletions contracts/vortex/interfaces/ICarbonVortex.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ interface ICarbonVortex is IUpgradeable {
error InsufficientNativeTokenSent();
error InsufficientAmountForTrading();
error UnnecessaryNativeTokenReceived();
error ControllerAlreadyAdded();
error ControllerDoesNotExist();

struct Price {
uint128 sourceAmount;
Expand Down Expand Up @@ -101,6 +103,16 @@ interface ICarbonVortex is IUpgradeable {
*/
event TransferAddressUpdated(address indexed prevTransferAddress, address indexed newTransferAddress);

/**
* @notice triggered when a controller address is added
*/
event ControllerAdded(address indexed controller);

/**
* @notice triggered when a controller address is removed
*/
event ControllerRemoved(address indexed controller);

/**
* @notice returns the rewards ppm
*/
Expand Down Expand Up @@ -203,6 +215,11 @@ interface ICarbonVortex is IUpgradeable {
*/
function transferAddress() external view returns (address);

/**
* @notice returns the controller addresses
*/
function controllers() external view returns (address[] memory);

/**
* @notice trades *targetToken* for *targetAmount* of *token* based on the current token price (trade by target amount)
* @notice if token == *targetToken*, trades *finalTargetToken* for amount of *targetToken* and also
Expand Down
2 changes: 1 addition & 1 deletion deploy/tests/mainnet/0004-fee-burner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describeDeployment(__filename, () => {
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
const carbonVortexImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
await expect(tx.wait()).to.be.reverted;
});
});
2 changes: 1 addition & 1 deletion deploy/tests/mainnet/0006-carbon-vortex-upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describeDeployment(__filename, () => {
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
const carbonVortexImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
await expect(tx.wait()).to.be.reverted;
});
});
2 changes: 1 addition & 1 deletion deploy/tests/mainnet/0012-carbon-vortex-upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describeDeployment(__filename, () => {
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
const carbonVortexImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
const tx = await carbonVortexImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
await expect(tx.wait()).to.be.reverted;
});
});
2 changes: 1 addition & 1 deletion deploy/tests/mainnet/0016-carbon-vortex-upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describeDeployment(__filename, () => {
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
const carbonControllerImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
const tx = await carbonControllerImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
const tx = await carbonControllerImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
await expect(tx.wait()).to.be.reverted;
});

Expand Down
2 changes: 1 addition & 1 deletion deploy/tests/mainnet/0017-carbon-vortex-upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describeDeployment(__filename, () => {
const implementationAddress = await proxyAdmin.getProxyImplementation(carbonVortex.address);
const carbonControllerImpl: CarbonVortex = await ethers.getContractAt('CarbonVortex', implementationAddress);
// hardcoding gas limit to avoid gas estimation attempts (which get rejected instead of reverted)
const tx = await carbonControllerImpl.initialize(ZERO_ADDRESS, { gasLimit: 6000000 });
const tx = await carbonControllerImpl.initialize(ZERO_ADDRESS, [], { gasLimit: 6000000 });
await expect(tx.wait()).to.be.reverted;
});

Expand Down
2 changes: 1 addition & 1 deletion test/carbon/accuracy/data/ArbitraryTrade.json
Original file line number Diff line number Diff line change
Expand Up @@ -30799,4 +30799,4 @@
"spec": "138.888888888889"
}
}
]
]
2 changes: 1 addition & 1 deletion test/carbon/accuracy/data/EthUsdcTrade.json
Original file line number Diff line number Diff line change
Expand Up @@ -3023,4 +3023,4 @@
"spec": "118349999999.999999999941"
}
}
]
]
2 changes: 1 addition & 1 deletion test/carbon/accuracy/data/ExtremeSrcTrade.json
Original file line number Diff line number Diff line change
Expand Up @@ -7167,4 +7167,4 @@
"spec": "924248818871128843570750.388118986304"
}
}
]
]
2 changes: 1 addition & 1 deletion test/carbon/accuracy/data/ExtremeTrgTrade.json
Original file line number Diff line number Diff line change
Expand Up @@ -9253,4 +9253,4 @@
"spec": "189999993267.999957109613"
}
}
]
]
Loading