Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

carbon pol eth conversion - minor fixes #121

Merged
merged 5 commits into from
Nov 22, 2023
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
49 changes: 45 additions & 4 deletions contracts/pol/CarbonPOL.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ contract CarbonPOL is ICarbonPOL, Upgradeable, ReentrancyGuardUpgradeable, Utils
// initial and current eth sale amount - for ETH->BNT trades
EthSaleAmount private _ethSaleAmount;

// min eth sale amount - resets the current eth sale amount if below this amount after a trade
uint128 private _minEthSaleAmount;

// upgrade forward-compatibility storage gap
uint256[MAX_GAP - 4] private __gap;
uint256[MAX_GAP - 5] private __gap;

/**
* @dev used to initialize the implementation
Expand Down Expand Up @@ -81,6 +84,8 @@ contract CarbonPOL is ICarbonPOL, Upgradeable, ReentrancyGuardUpgradeable, Utils
_setPriceDecayHalfLife(10 days);
// set initial eth sale amount to 100 eth
_setEthSaleAmount(100 ether);
// set min eth sale amount to 10 eth
_setMinEthSaleAmount(10 ether);
}

/**
Expand Down Expand Up @@ -148,6 +153,17 @@ contract CarbonPOL is ICarbonPOL, Upgradeable, ReentrancyGuardUpgradeable, Utils
_setEthSaleAmount(newEthSaleAmount);
}

/**
* @notice sets the min eth sale amount
*
* requirements:
*
* - the caller must be the admin of the contract
*/
function setMinEthSaleAmount(uint128 newMinEthSaleAmount) external onlyAdmin greaterThanZero(newMinEthSaleAmount) {
_setMinEthSaleAmount(newMinEthSaleAmount);
}

/**
* @notice enable trading for TKN->ETH and set the initial price
*
Expand Down Expand Up @@ -200,6 +216,13 @@ contract CarbonPOL is ICarbonPOL, Upgradeable, ReentrancyGuardUpgradeable, Utils
return _ethSaleAmount;
}

/**
* @inheritdoc ICarbonPOL
*/
function minEthSaleAmount() external view returns (uint128) {
return _minEthSaleAmount;
}

/**
* @inheritdoc ICarbonPOL
*/
Expand Down Expand Up @@ -323,10 +346,12 @@ contract CarbonPOL is ICarbonPOL, Upgradeable, ReentrancyGuardUpgradeable, Utils
// update the available eth sale amount
_ethSaleAmount.current -= amount;

// check if below 10% of the initial eth sale amount
if (_ethSaleAmount.current < _ethSaleAmount.initial / 10) {
// check if remaining eth sale amount is below the min eth sale amount
if (_ethSaleAmount.current < _minEthSaleAmount) {
// top up the eth sale amount
_ethSaleAmount.current = _ethSaleAmount.initial;
_ethSaleAmount.current = address(this).balance > _ethSaleAmount.initial
? _ethSaleAmount.initial
: uint128(address(this).balance);
// reset the price to double the current one
Price memory price = tokenPrice(NATIVE_TOKEN);
price.sourceAmount *= _marketPriceMultiply;
Expand Down Expand Up @@ -391,6 +416,22 @@ contract CarbonPOL is ICarbonPOL, Upgradeable, ReentrancyGuardUpgradeable, Utils
emit EthSaleAmountUpdated(prevEthSaleAmount, newEthSaleAmount);
}

/**
* @dev set min eth sale amount helper
*/
function _setMinEthSaleAmount(uint128 newMinEthSaleAmount) private {
uint128 prevMinEthSaleAmount = _minEthSaleAmount;

// return if the min eth sale amount is the same
if (prevMinEthSaleAmount == newMinEthSaleAmount) {
return;
}

_minEthSaleAmount = newMinEthSaleAmount;

emit MinEthSaleAmountUpdated(prevMinEthSaleAmount, newMinEthSaleAmount);
}

/**
* @dev returns the token amount available for trading
*/
Expand Down
10 changes: 10 additions & 0 deletions contracts/pol/interfaces/ICarbonPOL.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ interface ICarbonPOL is IUpgradeable {
*/
event EthSaleAmountUpdated(uint128 prevEthSaleAmount, uint128 newEthSaleAmount);

/**
* @notice triggered when the min eth sale amount is updated
*/
event MinEthSaleAmountUpdated(uint128 prevMinEthSaleAmount, uint128 newMinEthSaleAmount);

/**
* @notice returns the market price multiplier
*/
Expand All @@ -70,6 +75,11 @@ interface ICarbonPOL is IUpgradeable {
*/
function ethSaleAmount() external view returns (EthSaleAmount memory);

/**
* @notice returns the min eth sale amount - resets the current eth sale amount if below this amount after a trade
*/
function minEthSaleAmount() external view returns (uint128);

/**
* @notice returns true if trading is enabled for token
*/
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"glob": "^10.2.7",
"hardhat-contract-sizer": "^2.10.0",
"hardhat-dependency-compiler": "^1.1.3",
"hardhat-deploy": "^0.11.34",
"hardhat-deploy": "0.11.34",
"hardhat-deploy-tenderly": "^0.2.0",
"hardhat-storage-layout": "^0.1.7",
"hardhat-watcher": "^2.5.0",
Expand Down
16 changes: 8 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 99 additions & 4 deletions test/forge/CarbonPOL.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ contract CarbonPOLTest is TestFixture {
uint128 private constant ETH_SALE_AMOUNT_DEFAULT = 100 ether;
uint128 private constant ETH_SALE_AMOUNT_UPDATED = 150 ether;

uint128 private constant MIN_ETH_SALE_AMOUNT_DEFAULT = 10 ether;
uint128 private constant MIN_ETH_SALE_AMOUNT_UPDATED = 15 ether;

// Events

/**
Expand Down Expand Up @@ -60,6 +63,11 @@ contract CarbonPOLTest is TestFixture {
*/
event EthSaleAmountUpdated(uint128 prevEthSaleAmount, uint128 newEthSaleAmount);

/**
* @notice triggered when the min eth sale amount is updated
*/
event MinEthSaleAmountUpdated(uint128 prevMinEthSaleAmount, uint128 newMinEthSaleAmount);

/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
Expand Down Expand Up @@ -221,6 +229,47 @@ contract CarbonPOLTest is TestFixture {
vm.stopPrank();
}

/**
* @dev min eth sale amount tests
*/

/// @dev test that setMinEthSaleAmount should revert when a non-admin calls it
function testShouldRevertWhenNonAdminAttemptsToSetTheMinEthSaleAmount() public {
vm.prank(user1);
vm.expectRevert(AccessDenied.selector);
carbonPOL.setMinEthSaleAmount(MIN_ETH_SALE_AMOUNT_UPDATED);
}

/// @dev test that setMinEthSaleAmount should revert when setting to an invalid value
function testShouldRevertSettingTheMinEthSaleAmountWithAnInvalidValue() public {
vm.prank(admin);
vm.expectRevert(ZeroValue.selector);
carbonPOL.setMinEthSaleAmount(0);
}

/// @dev test that setMinEthSaleAmount with the same value should be ignored
function testFailShouldIgnoreSettingTheSameMinEthSaleAmount() public {
vm.prank(admin);
vm.expectEmit(false, false, false, false);
emit MinEthSaleAmountUpdated(MIN_ETH_SALE_AMOUNT_DEFAULT, MIN_ETH_SALE_AMOUNT_DEFAULT);
carbonPOL.setMinEthSaleAmount(MIN_ETH_SALE_AMOUNT_DEFAULT);
}

/// @dev test that admin should be able to update the min eth sale amount
function testShouldBeAbleToSetAndUpdateTheMinEthSaleAmount() public {
vm.startPrank(admin);
uint128 minEthSaleAmount = carbonPOL.minEthSaleAmount();
assertEq(minEthSaleAmount, MIN_ETH_SALE_AMOUNT_DEFAULT);

vm.expectEmit();
emit MinEthSaleAmountUpdated(MIN_ETH_SALE_AMOUNT_DEFAULT, MIN_ETH_SALE_AMOUNT_UPDATED);
carbonPOL.setMinEthSaleAmount(MIN_ETH_SALE_AMOUNT_UPDATED);

minEthSaleAmount = carbonPOL.minEthSaleAmount();
assertEq(minEthSaleAmount, MIN_ETH_SALE_AMOUNT_UPDATED);
vm.stopPrank();
}

/// @dev test that setting the eth sale amount to an amount below the current eth sale amount reset the current amount
function testCurrentEthSaleAmountIsUpdatedWhenAboveTheNewEthSaleAmount() public {
vm.startPrank(admin);
Expand Down Expand Up @@ -788,7 +837,7 @@ contract CarbonPOLTest is TestFixture {
vm.stopPrank();
}

/// @dev test trading eth below the 10% * sale amount threshold should reset the price and current eth amount
/// @dev test trading eth below the 10 ether sale amount threshold should reset the price and current eth amount
function testTradingETHBelowTheSaleThreshholdShouldResetThePriceAndCurrentEthAmount() public {
// enable trading and set price for the native token
vm.prank(admin);
Expand Down Expand Up @@ -825,7 +874,7 @@ contract CarbonPOLTest is TestFixture {
// get the price before the threshold trade
ICarbonPOL.Price memory prevPrice = carbonPOL.tokenPrice(NATIVE_TOKEN);

// trade 10% more (so that we go below 10% of the max sale amount)
// trade 10% more (so that we go below 10 ether current eth sale amount)
amountToSell = uint128((initialSaleAmount * 10) / 100);
carbonPOL.trade(token, amountToSell);

Expand All @@ -847,7 +896,7 @@ contract CarbonPOLTest is TestFixture {
vm.stopPrank();
}

/// @dev test trading eth below the 10% * sale amount threshold should emit price updated event
/// @dev test trading eth below the 10 ether sale amount threshold should emit price updated event
function testTradingETHBelowTheSaleThreshholdShouldEmitEvent() public {
// enable trading and set price for the native token
vm.prank(admin);
Expand All @@ -868,7 +917,7 @@ contract CarbonPOLTest is TestFixture {
// assert current and initial eth sale amount are equal
assertEq(initialSaleAmount, currentSaleAmount);

// trade 95% of the sale amount
// trade 95% of the sale amount (leaving 5 eth as current amount)
uint128 amountToSell = uint128((initialSaleAmount * 95) / 100);

// approve bnt for eth -> bnt trades
Expand All @@ -891,6 +940,52 @@ contract CarbonPOLTest is TestFixture {
vm.stopPrank();
}

/// @dev test trading eth below the 10 ether sale amount threshold should reset the current sale amount
/// @dev to contract balance or the initial amount, depending on which is less
function testTradingETHBelowTheSaleThreshholdShouldResetSaleAmountToContractBalanceIfItsLessThanInitialAmount()
public
{
// enable trading and set price for the native token
vm.startPrank(admin);
Token token = NATIVE_TOKEN;

// set 1 eth = 2000 bnt as initial price
ICarbonPOL.Price memory initialPrice = ICarbonPOL.Price({ sourceAmount: 2000 * 1e18, targetAmount: 1e18 });
carbonPOL.enableTradingETH(initialPrice);

// set up current eth sale amount
carbonPOL.setEthSaleAmount(uint128(address(carbonPOL).balance * 2));

vm.stopPrank();

vm.startPrank(user1);

// set timestamp to 10 days
vm.warp(10 days);

uint128 initialSaleAmount = carbonPOL.ethSaleAmount().initial;
uint128 currentSaleAmount = carbonPOL.ethSaleAmount().current;

// check the initial sale amount is greater than the contract balance
assertGt(initialSaleAmount, address(carbonPOL).balance);

// trade 95% of the sale amount (leaving 5 eth as current amount)
uint128 amountToSell = uint128((currentSaleAmount * 95) / 100);

// approve bnt for eth -> bnt trades
bnt.safeApprove(address(carbonPOL), MAX_SOURCE_AMOUNT);

// trade
carbonPOL.trade(token, amountToSell);

uint128 currentSaleAmountPostTrade = carbonPOL.ethSaleAmount().current;

// check that the current sale amount has been reset to the contract's balance
assertEq(currentSaleAmountPostTrade, address(carbonPOL).balance);

vm.stopPrank();
}

/// @dev test should revert getting price for tokens for which trading is disabled
function testShouldRevertTokenPriceIfTradingIsDisabled(bool isNativeToken) public {
Token token = isNativeToken ? NATIVE_TOKEN : token1;
Expand Down
Loading