Skip to content

Commit

Permalink
Merge pull request bancorprotocol#121 from bancorprotocol/pol-eth-to-bnt
Browse files Browse the repository at this point in the history
carbon pol eth conversion - minor fixes
  • Loading branch information
ivanzhelyazkov authored Nov 22, 2023
2 parents d004195 + 1d28795 commit 45830c3
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 17 deletions.
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

0 comments on commit 45830c3

Please sign in to comment.