diff --git a/contracts/goodDollar/GoodDollarExpansionController.sol b/contracts/goodDollar/GoodDollarExpansionController.sol index 402e334..6790b99 100644 --- a/contracts/goodDollar/GoodDollarExpansionController.sol +++ b/contracts/goodDollar/GoodDollarExpansionController.sol @@ -173,11 +173,10 @@ contract GoodDollarExpansionController is IGoodDollarExpansionController, Ownabl .getPoolExchange(exchangeId); ExchangeExpansionConfig memory config = getExpansionConfig(exchangeId); - bool shouldExpand = block.timestamp > config.lastExpansion + config.expansionFrequency; + bool shouldExpand = block.timestamp >= config.lastExpansion + config.expansionFrequency; if (shouldExpand || config.lastExpansion == 0) { - uint256 reserveRatioScalar = _getReserveRatioScalar(config); + uint256 reserveRatioScalar = _getReserveRatioScalar(exchangeId); - exchangeExpansionConfigs[exchangeId].lastExpansion = uint32(block.timestamp); amountMinted = goodDollarExchangeProvider.mintFromExpansion(exchangeId, reserveRatioScalar); IGoodDollar(exchange.tokenAddress).mint(address(distributionHelper), amountMinted); @@ -220,17 +219,23 @@ contract GoodDollarExpansionController is IGoodDollarExpansionController, Ownabl /** * @notice Calculates the reserve ratio scalar for the given expansion config. - * @param config The expansion config. + * @param exchangeId The ID of the exchange. * @return reserveRatioScalar The reserve ratio scalar. */ - function _getReserveRatioScalar(ExchangeExpansionConfig memory config) internal view returns (uint256) { + function _getReserveRatioScalar(bytes32 exchangeId) internal returns (uint256) { + ExchangeExpansionConfig memory config = getExpansionConfig(exchangeId); uint256 numberOfExpansions; // If there was no previous expansion, we expand once. if (config.lastExpansion == 0) { numberOfExpansions = 1; + exchangeExpansionConfigs[exchangeId].lastExpansion = uint32(block.timestamp); } else { numberOfExpansions = (block.timestamp - config.lastExpansion) / config.expansionFrequency; + // slither-disable-next-line divide-before-multiply + exchangeExpansionConfigs[exchangeId].lastExpansion = uint32( + config.lastExpansion + numberOfExpansions * config.expansionFrequency + ); } uint256 stepReserveRatioScalar = MAX_WEIGHT - config.expansionRate; diff --git a/test/unit/goodDollar/GoodDollarExpansionController.t.sol b/test/unit/goodDollar/GoodDollarExpansionController.t.sol index 974843a..65d3cb8 100644 --- a/test/unit/goodDollar/GoodDollarExpansionController.t.sol +++ b/test/unit/goodDollar/GoodDollarExpansionController.t.sol @@ -402,7 +402,7 @@ contract GoodDollarExpansionControllerTest_mintUBIFromExpansion is GoodDollarExp expansionController.mintUBIFromExpansion("NotSetExchangeId"); } - function test_mintUBIFromExpansion_whenShouldNotExpand_shouldNotExpand() public { + function test_mintUBIFromExpansion_whenLessThanExpansionFrequencyPassed_shouldNotExpand() public { // doing one initial expansion to not be first expansion // since on first expansion the expansion is always applied once. expansionController.mintUBIFromExpansion(exchangeId); @@ -410,8 +410,7 @@ contract GoodDollarExpansionControllerTest_mintUBIFromExpansion is GoodDollarExp IGoodDollarExpansionController.ExchangeExpansionConfig memory config = expansionController.getExpansionConfig( exchangeId ); - uint256 lastExpansion = config.lastExpansion; - skip(lastExpansion + config.expansionFrequency - 1); + skip(config.expansionFrequency - 1); assertEq(expansionController.mintUBIFromExpansion(exchangeId), 0); } @@ -497,14 +496,11 @@ contract GoodDollarExpansionControllerTest_mintUBIFromExpansion is GoodDollarExp // 1 day has passed since last expansion and expansion rate is 1% so the rate passed to the exchangeProvider // should be 0.99^1 = 0.99 uint256 reserveRatioScalar = 1e18 * 0.99; - skip(expansionFrequency + 1); + skip(expansionFrequency); uint256 amountToMint = 1000e18; uint256 distributionHelperBalanceBefore = token.balanceOf(distributionHelper); - vm.expectEmit(true, true, true, true); - emit ExpansionUBIMinted(exchangeId, amountToMint); - vm.expectCall( exchangeProvider, abi.encodeWithSelector( @@ -518,6 +514,9 @@ contract GoodDollarExpansionControllerTest_mintUBIFromExpansion is GoodDollarExp abi.encodeWithSelector(IDistributionHelper(distributionHelper).onDistribution.selector, amountToMint) ); + vm.expectEmit(true, true, true, true); + emit ExpansionUBIMinted(exchangeId, amountToMint); + uint256 amountMinted = expansionController.mintUBIFromExpansion(exchangeId); IGoodDollarExpansionController.ExchangeExpansionConfig memory config = expansionController.getExpansionConfig( exchangeId @@ -528,7 +527,7 @@ contract GoodDollarExpansionControllerTest_mintUBIFromExpansion is GoodDollarExp assertEq(config.lastExpansion, block.timestamp); } - function test_mintUBIFromExpansion_whenMultipleDaysPassed_shouldCalculateCorrectRateAndExpand() public { + function test_mintUBIFromExpansion_whenThreeAndAHalfDaysPassed_shouldMintCorrectAmountAndSetLastExpansion() public { // doing one initial expansion to not be first expansion // since on first expansion the expansion is always applied once. expansionController.mintUBIFromExpansion(exchangeId); @@ -537,7 +536,12 @@ contract GoodDollarExpansionControllerTest_mintUBIFromExpansion is GoodDollarExp // should be 0.99^3 = 0.970299 uint256 reserveRatioScalar = 1e18 * 0.970299; - skip(3 * expansionFrequency + 1); + IGoodDollarExpansionController.ExchangeExpansionConfig memory stateBefore = expansionController.getExpansionConfig( + exchangeId + ); + + // 3.5 days have passed since last expansion + skip((7 * expansionFrequency) / 2); uint256 amountToMint = 1000e18; uint256 distributionHelperBalanceBefore = token.balanceOf(distributionHelper); @@ -565,7 +569,7 @@ contract GoodDollarExpansionControllerTest_mintUBIFromExpansion is GoodDollarExp assertEq(amountMinted, amountToMint); assertEq(token.balanceOf(distributionHelper), distributionHelperBalanceBefore + amountToMint); - assertEq(config.lastExpansion, block.timestamp); + assertEq(config.lastExpansion, stateBefore.lastExpansion + expansionFrequency * 3); } } @@ -575,18 +579,14 @@ contract GoodDollarExpansionControllerTest_getExpansionScalar is GoodDollarExpan function setUp() public override { super.setUp(); expansionController = new GoodDollarExpansionControllerHarness(false); + expansionController.initialize(exchangeProvider, distributionHelper, reserveAddress, avatarAddress); } - function test_getExpansionScaler_whenExpansionRateIs0_shouldReturn1e18() public { - IGoodDollarExpansionController.ExchangeExpansionConfig memory config = IGoodDollarExpansionController - .ExchangeExpansionConfig(0, 1, 0); - assertEq(expansionController.exposed_getReserveRatioScalar(config), 1e18); - } - - function test_getExpansionScaler_whenExpansionRateIs1_shouldReturn1() public { - IGoodDollarExpansionController.ExchangeExpansionConfig memory config = IGoodDollarExpansionController - .ExchangeExpansionConfig(1e18 - 1, 1, 0); - assertEq(expansionController.exposed_getReserveRatioScalar(config), 1); + function test_getExpansionScaler_whenStepReserveRatioScalerIs1_shouldReturn1() public { + vm.prank(avatarAddress); + expansionController.setExpansionConfig(exchangeId, 1e18 - 1, 1); + // stepReserveRatioScalar is 1e18 - expansionRate = 1e18 - (1e18 - 1) = 1 + assertEq(expansionController.exposed_getReserveRatioScalar(exchangeId), 1); } function testFuzz_getExpansionScaler( @@ -602,9 +602,11 @@ contract GoodDollarExpansionControllerTest_getExpansionScalar is GoodDollarExpan skip(lastExpansion + timeDelta); - IGoodDollarExpansionController.ExchangeExpansionConfig memory config = IGoodDollarExpansionController - .ExchangeExpansionConfig(expansionRate, expansionFrequency, lastExpansion); - uint256 scaler = expansionController.exposed_getReserveRatioScalar(config); + vm.prank(avatarAddress); + expansionController.setExpansionConfig(exchangeId, expansionRate, expansionFrequency); + expansionController.setLastExpansion(exchangeId, lastExpansion); + uint256 scaler = expansionController.exposed_getReserveRatioScalar(exchangeId); + assert(scaler >= 0 && scaler <= 1e18); } } diff --git a/test/utils/harnesses/GoodDollarExpansionControllerHarness.sol b/test/utils/harnesses/GoodDollarExpansionControllerHarness.sol index ad6f080..aef0837 100644 --- a/test/utils/harnesses/GoodDollarExpansionControllerHarness.sol +++ b/test/utils/harnesses/GoodDollarExpansionControllerHarness.sol @@ -7,7 +7,11 @@ import { GoodDollarExpansionController } from "contracts/goodDollar/GoodDollarEx contract GoodDollarExpansionControllerHarness is GoodDollarExpansionController { constructor(bool disabled) GoodDollarExpansionController(disabled) {} - function exposed_getReserveRatioScalar(ExchangeExpansionConfig calldata config) external returns (uint256) { - return _getReserveRatioScalar(config); + function exposed_getReserveRatioScalar(bytes32 exchangeId) external returns (uint256) { + return _getReserveRatioScalar(exchangeId); + } + + function setLastExpansion(bytes32 exchangeId, uint32 lastExpansion) external { + exchangeExpansionConfigs[exchangeId].lastExpansion = lastExpansion; } }