diff --git a/contracts/ContinuousFundraising.sol b/contracts/ContinuousFundraising.sol index 5eaeceb0..f412f730 100644 --- a/contracts/ContinuousFundraising.sol +++ b/contracts/ContinuousFundraising.sol @@ -141,12 +141,12 @@ contract ContinuousFundraising is uint256 currencyAmount = (_amount * tokenPrice) / (10 ** token.decimals()); uint256 fee; - if (token.feeSettings().investmentFeeDenominator() == 0) { + if (token.feeSettings().continuousFundraisungFeeDenominator() == 0) { fee = 0; } else { fee = currencyAmount / - token.feeSettings().investmentFeeDenominator(); + token.feeSettings().continuousFundraisungFeeDenominator(); currency.safeTransferFrom( _msgSender(), token.feeSettings().feeCollector(), diff --git a/contracts/FeeSettings.sol b/contracts/FeeSettings.sol index 2c4949d1..df746332 100644 --- a/contracts/FeeSettings.sol +++ b/contracts/FeeSettings.sol @@ -3,10 +3,11 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/access/Ownable.sol"; -struct Change { - uint tokenFeeDenominator; - uint investmentFeeDenominator; - uint time; +struct Fees { + uint256 tokenFeeDenominator; + uint256 continuousFundraisungFeeDenominator; + uint256 personalInviteFeeDenominator; + uint256 time; } /* @@ -16,65 +17,54 @@ contract FeeSettings is Ownable { /// @notice Denominator to calculate fees paid Token.sol uint256 public tokenFeeDenominator; /// @notice Denominator to calculate fees paid in all investment contracts - uint256 public investmentFeeDenominator; + uint256 public continuousFundraisungFeeDenominator; + /// @notice Denominator to calculate fees paid in all investment contracts + uint256 public personalInviteFeeDenominator; /// @notice address used to pay platform fees to. address public feeCollector; - Change public change; + Fees public proposedFees; event SetTokenFeeDenominator(uint256 value); - event SetInvestmentFeeDenominator(uint256 value); + event SetContinuousFundraisungFeeDenominator(uint256 value); + event SetPersonalInviteFeeDenominator(uint256 value); event FeeCollectorChanged(address indexed newFeeCollector); - event ChangeProposed(Change proposal); + event ChangeProposed(Fees proposal); constructor( - uint256 _tokenFeeDenominator, - uint256 _investmentFeeDenominator, + Fees memory _fees, address _feeCollector ) { - require( - _tokenFeeDenominator >= 20 || _tokenFeeDenominator == 0, - "Fee must be below 5% or 0" - ); - tokenFeeDenominator = _tokenFeeDenominator; - require( - _investmentFeeDenominator >= 20 || _investmentFeeDenominator == 0, - "Fee must be below 5% or 0" - ); - investmentFeeDenominator = _investmentFeeDenominator; + checkFeeLimits(_fees); + tokenFeeDenominator = _fees.tokenFeeDenominator; + continuousFundraisungFeeDenominator = _fees.continuousFundraisungFeeDenominator; + personalInviteFeeDenominator = _fees.personalInviteFeeDenominator; require(_feeCollector != address(0), "Fee collector cannot be 0x0"); feeCollector = _feeCollector; } - function planFeeChange(Change memory _change) public onlyOwner { - require( - _change.tokenFeeDenominator >= 20 || - _change.tokenFeeDenominator == 0, - "Fee must be below 5% or 0" - ); - require( - _change.investmentFeeDenominator >= 20 || - _change.investmentFeeDenominator == 0, - "Fee must be below 5% or 0" - ); + function planFeeChange(Fees memory _fees) public onlyOwner { + checkFeeLimits(_fees); require( - _change.time > block.timestamp + 12 weeks, + _fees.time > block.timestamp + 12 weeks, "Fee change must be at least 12 weeks in the future" ); // can only be executed in 3 months - change = _change; - emit ChangeProposed(_change); + proposedFees = _fees; + emit ChangeProposed(_fees); } function executeFeeChange() public onlyOwner { require( - block.timestamp >= change.time, + block.timestamp >= proposedFees.time, "Fee change must be executed after the change time" ); - tokenFeeDenominator = change.tokenFeeDenominator; - investmentFeeDenominator = change.investmentFeeDenominator; - emit SetTokenFeeDenominator(change.tokenFeeDenominator); - emit SetInvestmentFeeDenominator(change.investmentFeeDenominator); - delete change; + tokenFeeDenominator = proposedFees.tokenFeeDenominator; + continuousFundraisungFeeDenominator = proposedFees.continuousFundraisungFeeDenominator; + personalInviteFeeDenominator = proposedFees.personalInviteFeeDenominator; + emit SetTokenFeeDenominator(proposedFees.tokenFeeDenominator); + emit SetContinuousFundraisungFeeDenominator(proposedFees.continuousFundraisungFeeDenominator); + emit SetPersonalInviteFeeDenominator(proposedFees.personalInviteFeeDenominator); + delete proposedFees; } function setFeeCollector(address _feeCollector) public onlyOwner { @@ -82,4 +72,19 @@ contract FeeSettings is Ownable { feeCollector = _feeCollector; emit FeeCollectorChanged(_feeCollector); } + + function checkFeeLimits(Fees memory _fees) internal pure { + require( + _fees.tokenFeeDenominator >= 20 || _fees.tokenFeeDenominator == 0, + "Fee must be below 5% or 0" + ); + require( + _fees.continuousFundraisungFeeDenominator >= 20 || _fees.continuousFundraisungFeeDenominator == 0, + "Fee must be below 5% or 0" + ); + require( + _fees.personalInviteFeeDenominator >= 20 || _fees.personalInviteFeeDenominator == 0, + "Fee must be below 5% or 0" + ); + } } diff --git a/contracts/PersonalInvite.sol b/contracts/PersonalInvite.sol index 0281ffe6..6a5a8a68 100644 --- a/contracts/PersonalInvite.sol +++ b/contracts/PersonalInvite.sol @@ -67,12 +67,12 @@ contract PersonalInvite { (10 ** _token.decimals()); uint256 fee; - if (_token.feeSettings().investmentFeeDenominator() == 0) { + if (_token.feeSettings().personalInviteFeeDenominator() == 0) { fee = 0; } else { fee = currencyAmount / - _token.feeSettings().investmentFeeDenominator(); + _token.feeSettings().personalInviteFeeDenominator(); _currency.safeTransferFrom( _buyer, _token.feeSettings().feeCollector(), diff --git a/test/ContinuousFundraising.t.sol b/test/ContinuousFundraising.t.sol index 987d99c9..91bcd69d 100644 --- a/test/ContinuousFundraising.t.sol +++ b/test/ContinuousFundraising.t.sol @@ -40,7 +40,8 @@ contract ContinuousFundraisingTest is Test { function setUp() public { list = new AllowList(); - feeSettings = new FeeSettings(100, 100, admin); + Fees memory fees = Fees(100,100,100,100); + feeSettings = new FeeSettings(fees, admin); token = new Token( trustedForwarder, @@ -199,7 +200,7 @@ contract ContinuousFundraisingTest is Test { // receiver should have the 990 FPT that were paid, minus the fee uint currencyAmount = 990 * 10 ** _paymentTokenDecimals; uint256 currencyFee = currencyAmount / - token.feeSettings().investmentFeeDenominator(); + token.feeSettings().continuousFundraisungFeeDenominator(); assertTrue( _paymentToken.balanceOf(receiver) == currencyAmount - currencyFee, @@ -337,13 +338,13 @@ contract ContinuousFundraisingTest is Test { paymentToken.balanceOf(receiver) == costInPaymentToken - costInPaymentToken / - token.feeSettings().investmentFeeDenominator(), + token.feeSettings().continuousFundraisungFeeDenominator(), "receiver has payment tokens" ); assertTrue( paymentToken.balanceOf(token.feeSettings().feeCollector()) == costInPaymentToken / - token.feeSettings().investmentFeeDenominator(), + token.feeSettings().continuousFundraisungFeeDenominator(), "fee collector has collected fee in payment tokens" ); assertTrue( @@ -470,7 +471,7 @@ contract ContinuousFundraisingTest is Test { token.feeSettings().tokenFeeDenominator(); uint256 paymentTokenFee = (costInPaymentTokenForMinAmount * 3) / 2 / - token.feeSettings().investmentFeeDenominator(); + token.feeSettings().continuousFundraisungFeeDenominator(); assertTrue( paymentToken.balanceOf(receiver) == (costInPaymentTokenForMinAmount * 3) / 2 - paymentTokenFee, diff --git a/test/ContinuousFundraisingERC2771.t.sol b/test/ContinuousFundraisingERC2771.t.sol index 85499f3d..eba3c0cb 100644 --- a/test/ContinuousFundraisingERC2771.t.sol +++ b/test/ContinuousFundraisingERC2771.t.sol @@ -67,11 +67,9 @@ contract ContinuousFundraisingTest is Test { function setUp() public { list = new AllowList(); - feeSettings = new FeeSettings( - tokenFeeDenominator, - paymentTokenFeeDenominator, - admin - ); + Fees memory fees = Fees(tokenFeeDenominator,paymentTokenFeeDenominator,paymentTokenFeeDenominator,0); + feeSettings = new FeeSettings(fees, admin); + token = new Token( trustedForwarder, address(feeSettings), diff --git a/test/FeeSettings.t.sol b/test/FeeSettings.t.sol index 619d3808..9e762cd9 100644 --- a/test/FeeSettings.t.sol +++ b/test/FeeSettings.t.sol @@ -37,8 +37,9 @@ contract FeeSettingsTest is Test { ) public { vm.assume(!feeInValidRange(fee)); FeeSettings _feeSettings; + Fees memory fees = Fees(fee,100,100,0); vm.expectRevert("Fee must be below 5% or 0"); - _feeSettings = new FeeSettings(fee, 100, admin); + _feeSettings = new FeeSettings(fees, admin); } function testEnforceInvestmentFeeDenominatorRangeinConstructor( @@ -46,17 +47,20 @@ contract FeeSettingsTest is Test { ) public { vm.assume(!feeInValidRange(fee)); FeeSettings _feeSettings; + Fees memory fees = Fees(fee,100,100,0); vm.expectRevert("Fee must be below 5% or 0"); - _feeSettings = new FeeSettings(100, fee, admin); + _feeSettings = new FeeSettings(fees, admin); } function testEnforceTokenFeeDenominatorRangeinFeeChanger(uint8 fee) public { vm.assume(!feeInValidRange(fee)); - FeeSettings _feeSettings = new FeeSettings(100, 100, admin); + Fees memory fees = Fees(100,100,100,0); + FeeSettings _feeSettings = new FeeSettings(fees, admin); - Change memory feeChange = Change({ + Fees memory feeChange = Fees({ tokenFeeDenominator: fee, - investmentFeeDenominator: 100, + continuousFundraisungFeeDenominator: 100, + personalInviteFeeDenominator: 100, time: block.timestamp + 7884001 }); vm.expectRevert("Fee must be below 5% or 0"); @@ -67,11 +71,13 @@ contract FeeSettingsTest is Test { uint8 fee ) public { vm.assume(!feeInValidRange(fee)); - FeeSettings _feeSettings = new FeeSettings(100, 100, admin); + Fees memory fees = Fees(100,100,100,0); + FeeSettings _feeSettings = new FeeSettings(fees, admin); - Change memory feeChange = Change({ + Fees memory feeChange = Fees({ tokenFeeDenominator: 100, - investmentFeeDenominator: fee, + continuousFundraisungFeeDenominator: fee, + personalInviteFeeDenominator: 100, time: block.timestamp + 7884001 }); vm.expectRevert("Fee must be below 5% or 0"); @@ -80,12 +86,14 @@ contract FeeSettingsTest is Test { function testEnforceFeeChangeDelay(uint delay) public { vm.assume(delay <= 12 weeks); + Fees memory fees = Fees(50,50,50,0); vm.prank(admin); - FeeSettings _feeSettings = new FeeSettings(50, 50, admin); + FeeSettings _feeSettings = new FeeSettings(fees, admin); - Change memory feeChange = Change({ + Fees memory feeChange = Fees({ tokenFeeDenominator: 100, - investmentFeeDenominator: 100, + continuousFundraisungFeeDenominator: 100, + personalInviteFeeDenominator: 100, time: block.timestamp + delay }); vm.prank(admin); @@ -104,12 +112,14 @@ contract FeeSettingsTest is Test { vm.assume(delayAnnounced > 12 weeks && delayAnnounced < 1000000000000); vm.assume(feeInValidRange(tokenFee)); vm.assume(feeInValidRange(investmentFee)); + Fees memory fees = Fees(50,50,50,0); vm.prank(admin); - FeeSettings _feeSettings = new FeeSettings(50, 50, admin); + FeeSettings _feeSettings = new FeeSettings(fees, admin); - Change memory feeChange = Change({ + Fees memory feeChange = Fees({ tokenFeeDenominator: tokenFee, - investmentFeeDenominator: investmentFee, + continuousFundraisungFeeDenominator: investmentFee, + personalInviteFeeDenominator: 100, time: block.timestamp + delayAnnounced }); vm.prank(admin); @@ -129,12 +139,14 @@ contract FeeSettingsTest is Test { vm.assume(delayAnnounced > 12 weeks && delayAnnounced < 100000000000); vm.assume(feeInValidRange(tokenFee)); vm.assume(feeInValidRange(investmentFee)); + Fees memory fees = Fees(50,50,50,0); vm.prank(admin); - FeeSettings _feeSettings = new FeeSettings(50, 50, admin); + FeeSettings _feeSettings = new FeeSettings(fees, admin); - Change memory feeChange = Change({ + Fees memory feeChange = Fees({ tokenFeeDenominator: tokenFee, - investmentFeeDenominator: investmentFee, + continuousFundraisungFeeDenominator: investmentFee, + personalInviteFeeDenominator: 100, time: block.timestamp + delayAnnounced }); vm.prank(admin); @@ -145,7 +157,7 @@ contract FeeSettingsTest is Test { _feeSettings.executeFeeChange(); assertEq(_feeSettings.tokenFeeDenominator(), tokenFee); - assertEq(_feeSettings.investmentFeeDenominator(), investmentFee); + assertEq(_feeSettings.continuousFundraisungFeeDenominator(), investmentFee); //assertEq(_feeSettings.change, 0); } @@ -156,14 +168,15 @@ contract FeeSettingsTest is Test { vm.assume(feeInValidRange(tokenFee)); vm.assume(feeInValidRange(investmentFee)); FeeSettings _feeSettings; - _feeSettings = new FeeSettings(tokenFee, investmentFee, admin); + Fees memory fees = Fees(tokenFee,investmentFee,investmentFee,0); + _feeSettings = new FeeSettings(fees, admin); assertEq( _feeSettings.tokenFeeDenominator(), tokenFee, "Token fee mismatch" ); assertEq( - _feeSettings.investmentFeeDenominator(), + _feeSettings.continuousFundraisungFeeDenominator(), investmentFee, "Investment fee mismatch" ); @@ -172,13 +185,15 @@ contract FeeSettingsTest is Test { function testFeeCollector0FailsInConstructor() public { vm.expectRevert("Fee collector cannot be 0x0"); FeeSettings _feeSettings; - _feeSettings = new FeeSettings(100, 100, address(0)); + Fees memory fees = Fees(100,100,100,0); + _feeSettings = new FeeSettings(fees, address(0)); } function testFeeCollector0FailsInSetter() public { FeeSettings _feeSettings; + Fees memory fees = Fees(100,100,100,0); vm.prank(admin); - _feeSettings = new FeeSettings(100, 100, admin); + _feeSettings = new FeeSettings(fees, admin); vm.expectRevert("Fee collector cannot be 0x0"); vm.prank(admin); _feeSettings.setFeeCollector(address(0)); @@ -187,8 +202,9 @@ contract FeeSettingsTest is Test { function testUpdateFeeCollector(address newCollector) public { vm.assume(newCollector != address(0)); FeeSettings _feeSettings; + Fees memory fees = Fees(100,100,100,0); vm.prank(admin); - _feeSettings = new FeeSettings(100, 100, admin); + _feeSettings = new FeeSettings(fees, admin); vm.prank(admin); _feeSettings.setFeeCollector(newCollector); assertEq(_feeSettings.feeCollector(), newCollector); diff --git a/test/MainnetCurrencies.t.sol b/test/MainnetCurrencies.t.sol index 0f327d18..c2892334 100644 --- a/test/MainnetCurrencies.t.sol +++ b/test/MainnetCurrencies.t.sol @@ -51,7 +51,8 @@ contract MainnetCurrencies is Test { function setUp() public { list = new AllowList(); - feeSettings = new FeeSettings(100, 100, admin); + Fees memory fees = Fees(100,100,100,0); + feeSettings = new FeeSettings(fees, admin); token = new Token( trustedForwarder, @@ -147,17 +148,17 @@ contract MainnetCurrencies is Test { _currency.balanceOf(receiver), _currencyCost - _currencyCost / - token.feeSettings().investmentFeeDenominator(), + token.feeSettings().continuousFundraisungFeeDenominator(), "receiver should have received currency" ); assertEq( _currency.balanceOf(token.feeSettings().feeCollector()), - _currencyCost / token.feeSettings().investmentFeeDenominator(), + _currencyCost / token.feeSettings().continuousFundraisungFeeDenominator(), "fee receiver should have received currency" ); assertEq( token.balanceOf(token.feeSettings().feeCollector()), - amountOfTokenToBuy / token.feeSettings().investmentFeeDenominator(), + amountOfTokenToBuy / token.feeSettings().continuousFundraisungFeeDenominator(), "fee receiver should have received tokens" ); assertEq( @@ -253,17 +254,17 @@ contract MainnetCurrencies is Test { _currency.balanceOf(receiver), _currencyCost - _currencyCost / - token.feeSettings().investmentFeeDenominator(), + token.feeSettings().continuousFundraisungFeeDenominator(), "receiver should have received currency" ); assertEq( _currency.balanceOf(token.feeSettings().feeCollector()), - _currencyCost / token.feeSettings().investmentFeeDenominator(), + _currencyCost / token.feeSettings().continuousFundraisungFeeDenominator(), "fee receiver should have received currency" ); assertEq( token.balanceOf(token.feeSettings().feeCollector()), - amountOfTokenToBuy / token.feeSettings().investmentFeeDenominator(), + amountOfTokenToBuy / token.feeSettings().continuousFundraisungFeeDenominator(), "fee receiver should have received tokens" ); assertEq( diff --git a/test/PersonalInvite.t.sol b/test/PersonalInvite.t.sol index 567a496a..5c75eb31 100644 --- a/test/PersonalInvite.t.sol +++ b/test/PersonalInvite.t.sol @@ -35,7 +35,9 @@ contract PersonalInviteTest is Test { function setUp() public { factory = new PersonalInviteFactory(); list = new AllowList(); - feeSettings = new FeeSettings(100, 100, admin); + + Fees memory fees = Fees(100,100,100,0); + feeSettings = new FeeSettings(fees, admin); token = new Token( trustedForwarder, @@ -154,7 +156,7 @@ contract PersonalInviteTest is Test { currency.balanceOf(receiver), currencyAmount - currencyAmount / - token.feeSettings().investmentFeeDenominator() + token.feeSettings().personalInviteFeeDenominator() ); console.log( @@ -166,7 +168,7 @@ contract PersonalInviteTest is Test { currency.balanceOf(token.feeSettings().feeCollector()), feeCollectorCurrencyBalanceBefore + currencyAmount / - token.feeSettings().investmentFeeDenominator(), + token.feeSettings().personalInviteFeeDenominator(), "feeCollector currency balance is not correct" ); diff --git a/test/PersonalInviteFactory.t.sol b/test/PersonalInviteFactory.t.sol index b95b92bc..5ce71cf7 100644 --- a/test/PersonalInviteFactory.t.sol +++ b/test/PersonalInviteFactory.t.sol @@ -62,7 +62,8 @@ contract PersonalInviteFactoryTest is Test { function setUp() public { factory = new PersonalInviteFactory(); list = new AllowList(); - feeSettings = new FeeSettings(100, 100, admin); + Fees memory fees = Fees(100,100,100,0); + feeSettings = new FeeSettings(fees, admin); token = new Token( trustedForwarder, diff --git a/test/Token.t.sol b/test/Token.t.sol index 10ffb1e3..c866347e 100644 --- a/test/Token.t.sol +++ b/test/Token.t.sol @@ -31,7 +31,8 @@ contract tokenTest is Test { vm.prank(admin); allowList = new AllowList(); vm.prank(feeSettingsOwner); - feeSettings = new FeeSettings(100, 100, admin); + Fees memory fees = Fees(100,100,100,0); + feeSettings = new FeeSettings(fees, admin); token = new Token( trustedForwarder, address(feeSettings), @@ -902,7 +903,8 @@ contract tokenTest is Test { function testSuggestNewFeeSettingsWrongCaller(address wrongUpdater) public { vm.assume(wrongUpdater != feeSettings.owner()); - FeeSettings newFeeSettings = new FeeSettings(0, 0, pauser); + Fees memory fees = Fees(0,0,0,0); + FeeSettings newFeeSettings = new FeeSettings(fees, pauser); vm.prank(wrongUpdater); vm.expectRevert( "Only fee settings owner can suggest fee settings update" @@ -911,7 +913,8 @@ contract tokenTest is Test { } function testSuggestNewFeeSettingsFeeCollector() public { - FeeSettings newFeeSettings = new FeeSettings(0, 0, pauser); + Fees memory fees = Fees(0,0,0,0); + FeeSettings newFeeSettings = new FeeSettings(fees, pauser); vm.prank(feeSettings.feeCollector()); vm.expectRevert( "Only fee settings owner can suggest fee settings update" @@ -921,10 +924,11 @@ contract tokenTest is Test { function testSuggestNewFeeSettings(address newCollector) public { vm.assume(newCollector != address(0)); - FeeSettings newFeeSettings = new FeeSettings(0, 0, newCollector); + Fees memory fees = Fees(0,0,0,0); + FeeSettings newFeeSettings = new FeeSettings(fees, newCollector); FeeSettings oldFeeSettings = token.feeSettings(); uint oldInvestmentFeeDenominator = oldFeeSettings - .investmentFeeDenominator(); + .continuousFundraisungFeeDenominator(); uint oldTokenFeeDenominator = oldFeeSettings.tokenFeeDenominator(); vm.prank(feeSettings.owner()); token.suggestNewFeeSettings(newFeeSettings); @@ -939,7 +943,7 @@ contract tokenTest is Test { "suggested fee settings not set!" ); assertTrue( - token.feeSettings().investmentFeeDenominator() == + token.feeSettings().continuousFundraisungFeeDenominator() == oldInvestmentFeeDenominator, "investment fee denominator changed!" ); @@ -951,10 +955,11 @@ contract tokenTest is Test { function testAcceptNewFeeSettings(address newCollector) public { vm.assume(newCollector != address(0)); - FeeSettings newFeeSettings = new FeeSettings(0, 0, newCollector); + Fees memory fees = Fees(0,0,0,0); + FeeSettings newFeeSettings = new FeeSettings(fees, newCollector); FeeSettings oldFeeSettings = token.feeSettings(); uint oldInvestmentFeeDenominator = oldFeeSettings - .investmentFeeDenominator(); + .continuousFundraisungFeeDenominator(); uint oldTokenFeeDenominator = oldFeeSettings.tokenFeeDenominator(); vm.prank(feeSettings.owner()); token.suggestNewFeeSettings(newFeeSettings); @@ -976,7 +981,7 @@ contract tokenTest is Test { "suggested fee settings not reset!" ); assertTrue( - token.feeSettings().investmentFeeDenominator() != + token.feeSettings().continuousFundraisungFeeDenominator() != oldInvestmentFeeDenominator, "investment fee denominator changed!" );