diff --git a/contracts/pol/CarbonPOL.sol b/contracts/pol/CarbonPOL.sol index 604bfbc3..c014a21b 100644 --- a/contracts/pol/CarbonPOL.sol +++ b/contracts/pol/CarbonPOL.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.19; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.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"; import { IVersioned } from "../utility/interfaces/IVersioned.sol"; import { ICarbonPOL } from "./interfaces/ICarbonPOL.sol"; @@ -191,7 +192,7 @@ contract CarbonPOL is ICarbonPOL, Upgradeable, ReentrancyGuardUpgradeable, Utils function enableTradingETH(Price memory price) external onlyAdmin validPrice(price) { _tradingStartTimes[NATIVE_TOKEN] = uint32(block.timestamp); _initialPrice[NATIVE_TOKEN] = price; - _ethSaleAmount.current = _ethSaleAmount.initial; + _ethSaleAmount.current = Math.min(address(this).balance, _ethSaleAmount.initial).toUint128(); emit TradingEnabled(NATIVE_TOKEN, price); } @@ -349,13 +350,11 @@ contract CarbonPOL is ICarbonPOL, Upgradeable, ReentrancyGuardUpgradeable, Utils // 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 = address(this).balance > _ethSaleAmount.initial - ? _ethSaleAmount.initial - : uint128(address(this).balance); + _ethSaleAmount.current = Math.min(address(this).balance, _ethSaleAmount.initial).toUint128(); // reset the price to double the current one Price memory price = tokenPrice(NATIVE_TOKEN); - price.sourceAmount *= _marketPriceMultiply; _initialPrice[NATIVE_TOKEN] = price; + _tradingStartTimes[NATIVE_TOKEN] = uint32(block.timestamp); // emit price updated event emit PriceUpdated(NATIVE_TOKEN, price); } @@ -410,7 +409,7 @@ contract CarbonPOL is ICarbonPOL, Upgradeable, ReentrancyGuardUpgradeable, Utils // check if the new sale amount is below the current available eth sale amount if (newEthSaleAmount < _ethSaleAmount.current) { - _ethSaleAmount.current = newEthSaleAmount; + _ethSaleAmount.current = Math.min(address(this).balance, _ethSaleAmount.initial).toUint128(); } emit EthSaleAmountUpdated(prevEthSaleAmount, newEthSaleAmount); diff --git a/deploy/scripts/0009-CarbonPOL-upgrade.ts b/deploy/scripts/0009-CarbonPOL-upgrade.ts index 27fbee08..58f30ce4 100644 --- a/deploy/scripts/0009-CarbonPOL-upgrade.ts +++ b/deploy/scripts/0009-CarbonPOL-upgrade.ts @@ -1,6 +1,7 @@ -import { upgradeProxy, InstanceName, setDeploymentMetadata } from '../../utils/Deploy'; import { DeployFunction } from 'hardhat-deploy/types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { upgradeProxy, InstanceName, setDeploymentMetadata, execute } from '../../utils/Deploy'; +import { toWei } from '../../utils/Types'; const func: DeployFunction = async ({ getNamedAccounts }: HardhatRuntimeEnvironment) => { const { deployer, bnt } = await getNamedAccounts(); @@ -11,6 +12,22 @@ const func: DeployFunction = async ({ getNamedAccounts }: HardhatRuntimeEnvironm args: [bnt] }); + // Set ETH sale amount to 100 ether + await execute({ + name: InstanceName.CarbonPOL, + methodName: 'setEthSaleAmount', + args: [toWei(100)], + from: deployer + }); + + // Set min ETH sale amount to 10 ether + await execute({ + name: InstanceName.CarbonPOL, + methodName: 'setMinEthSaleAmount', + args: [toWei(10)], + from: deployer + }); + return true; }; diff --git a/deploy/tests/0009-carbon-pol.ts b/deploy/tests/0009-carbon-pol.ts index 0924b727..d5cdf556 100644 --- a/deploy/tests/0009-carbon-pol.ts +++ b/deploy/tests/0009-carbon-pol.ts @@ -1,3 +1,4 @@ +import { toWei } from '../../utils/Types'; import { CarbonPOL, ProxyAdmin } from '../../components/Contracts'; import { DeployedContracts } from '../../utils/Deploy'; import { describeDeployment } from '../../utils/helpers/Deploy'; @@ -21,6 +22,10 @@ describeDeployment(__filename, () => { expect(await carbonPOL.marketPriceMultiply()).to.equal(2); // check price decay half-life is configured correctly expect(await carbonPOL.priceDecayHalfLife()).to.equal(864000); + // check eth sale amount is configured correctly + expect((await carbonPOL.ethSaleAmount()).initial).to.equal(toWei(100)); + // check min eth sale amount is configured correctly + expect(await carbonPOL.minEthSaleAmount()).to.equal(toWei(10)); }); it('carbon pol implementation should be initialized', async () => { diff --git a/test/forge/CarbonPOL.t.sol b/test/forge/CarbonPOL.t.sol index 230ce739..ca6c8c82 100644 --- a/test/forge/CarbonPOL.t.sol +++ b/test/forge/CarbonPOL.t.sol @@ -379,6 +379,18 @@ contract CarbonPOLTest is TestFixture { carbonPOL.enableTradingETH(price); } + /// @dev test enabling trading for eth should set the current sale amount properly + function testEnablingTradingForETHShouldSetTheCurrentSaleAmountProperly() public { + vm.startPrank(admin); + // set the initial sale amount to a value higher than the pol balance + carbonPOL.setEthSaleAmount(uint128(address(carbonPOL).balance * 2)); + // enable trading for eth + carbonPOL.enableTradingETH(ICarbonPOL.Price({ sourceAmount: 100, targetAmount: 10000 })); + // check current eth sale amount is set to the contract balance + assertEq(carbonPOL.ethSaleAmount().current, address(carbonPOL).balance); + vm.stopPrank(); + } + /// @dev test should revert when setting invalid price for a token function testShouldRevertWhenSettingInvalidPriceForToken(uint256 i) public { // pick one of these tokens to test @@ -838,7 +850,9 @@ contract CarbonPOLTest is TestFixture { } /// @dev test trading eth below the 10 ether sale amount threshold should reset the price and current eth amount - function testTradingETHBelowTheSaleThreshholdShouldResetThePriceAndCurrentEthAmount() public { + function testTradingETHBelowTheSaleThreshholdShouldResetThePriceAndCurrentEthAmount(uint256 timestamp) public { + // test with different timestamps + timestamp = bound(timestamp, 1, 100 days); // enable trading and set price for the native token vm.prank(admin); Token token = NATIVE_TOKEN; @@ -849,8 +863,8 @@ contract CarbonPOLTest is TestFixture { vm.startPrank(user1); - // set timestamp to 10 days - vm.warp(10 days); + // set timestamp + vm.warp(timestamp); uint128 initialSaleAmount = carbonPOL.ethSaleAmount().initial; uint128 currentSaleAmount = carbonPOL.ethSaleAmount().current; @@ -885,7 +899,6 @@ contract CarbonPOLTest is TestFixture { currentSaleAmount = carbonPOL.ethSaleAmount().current; assertEq(currentSaleAmount, initialSaleAmount); - vm.warp(block.timestamp + 1); // get the price after the threshold trade ICarbonPOL.Price memory newPrice = carbonPOL.tokenPrice(NATIVE_TOKEN); @@ -897,7 +910,9 @@ contract CarbonPOLTest is TestFixture { } /// @dev test trading eth below the 10 ether sale amount threshold should emit price updated event - function testTradingETHBelowTheSaleThreshholdShouldEmitEvent() public { + function testTradingETHBelowTheSaleThreshholdShouldEmitEvent(uint256 timestamp) public { + // test with different timestamps + timestamp = bound(timestamp, 1, 100 days); // enable trading and set price for the native token vm.prank(admin); Token token = NATIVE_TOKEN; @@ -908,8 +923,8 @@ contract CarbonPOLTest is TestFixture { vm.startPrank(user1); - // set timestamp to 10 days - vm.warp(10 days); + // set timestamp + vm.warp(timestamp); uint128 initialSaleAmount = carbonPOL.ethSaleAmount().initial; uint128 currentSaleAmount = carbonPOL.ethSaleAmount().current; @@ -926,15 +941,14 @@ contract CarbonPOLTest is TestFixture { // get the price before the threshold trade ICarbonPOL.Price memory prevPrice = carbonPOL.tokenPrice(NATIVE_TOKEN); - uint128 newExpectedSourceAmount = prevPrice.sourceAmount * carbonPOL.marketPriceMultiply(); - ICarbonPOL.Price memory newExpectedPrice = ICarbonPOL.Price({ - sourceAmount: newExpectedSourceAmount, + ICarbonPOL.Price memory expectedPrice = ICarbonPOL.Price({ + sourceAmount: prevPrice.sourceAmount, targetAmount: prevPrice.targetAmount }); // trade vm.expectEmit(); - emit PriceUpdated(token, newExpectedPrice); + emit PriceUpdated(token, expectedPrice); carbonPOL.trade(token, amountToSell); vm.stopPrank();