diff --git a/contracts/Folio.sol b/contracts/Folio.sol index 9e1d214..0ff1167 100644 --- a/contracts/Folio.sol +++ b/contracts/Folio.sol @@ -213,6 +213,7 @@ contract Folio is /// @dev Non-reentrant via distributeFees() /// @dev Fee recipients must be unique and sorted by address, and sum to 1e18 + /// @dev Warning: An empty fee recipients table will result in all fees being sent to DAO function setFeeRecipients(FeeRecipient[] memory _newRecipients) external onlyRole(DEFAULT_ADMIN_ROLE) { distributeFees(); @@ -372,7 +373,7 @@ contract Folio is } /// Distribute all pending fee shares - /// @dev Recipients: DAO and fee recipients + /// @dev Recipients: DAO and fee recipients; if feeRecipients are empty, the DAO gets all the fees /// @dev Pending fee shares are already reflected in the total supply, this function only concretizes balances function distributeFees() public nonReentrant { _poke(); @@ -815,7 +816,10 @@ contract Folio is emit MintFeeSet(_newFee); } + /// @dev Warning: An empty fee recipients table will result in all fees being sent to DAO function _setFeeRecipients(FeeRecipient[] memory _feeRecipients) internal { + emit FeeRecipientsSet(_feeRecipients); + // Clear existing fee table uint256 len = feeRecipients.length; for (uint256 i; i < len; i++) { @@ -823,11 +827,16 @@ contract Folio is } // Add new items to the fee table - uint256 total; len = _feeRecipients.length; + + if (len == 0) { + return; + } + require(len <= MAX_FEE_RECIPIENTS, Folio__TooManyFeeRecipients()); address previousRecipient; + uint256 total; for (uint256 i; i < len; i++) { require(_feeRecipients[i].recipient > previousRecipient, Folio__FeeRecipientInvalidAddress()); @@ -836,7 +845,6 @@ contract Folio is total += _feeRecipients[i].portion; previousRecipient = _feeRecipients[i].recipient; feeRecipients.push(_feeRecipients[i]); - emit FeeRecipientSet(_feeRecipients[i].recipient, _feeRecipients[i].portion); } // ensure table adds up to 100% diff --git a/contracts/interfaces/IFolio.sol b/contracts/interfaces/IFolio.sol index b442e29..c50aeec 100644 --- a/contracts/interfaces/IFolio.sol +++ b/contracts/interfaces/IFolio.sol @@ -18,7 +18,7 @@ interface IFolio { event BasketTokenRemoved(address indexed token); event TVLFeeSet(uint256 newFee, uint256 feeAnnually); event MintFeeSet(uint256 newFee); - event FeeRecipientSet(address indexed recipient, uint96 portion); + event FeeRecipientsSet(FeeRecipient[] recipients); event AuctionDelaySet(uint256 newAuctionDelay); event AuctionLengthSet(uint256 newAuctionLength); event MandateSet(string newMandate); diff --git a/test/Folio.t.sol b/test/Folio.t.sol index c1c7576..0696a71 100644 --- a/test/Folio.t.sol +++ b/test/Folio.t.sol @@ -628,9 +628,7 @@ contract FolioTest is BaseTest { recipients[1] = IFolio.FeeRecipient(feeReceiver, 0.05e18); recipients[2] = IFolio.FeeRecipient(user1, 0.15e18); vm.expectEmit(true, true, false, true); - emit IFolio.FeeRecipientSet(owner, 0.8e18); - emit IFolio.FeeRecipientSet(feeReceiver, 0.05e18); - emit IFolio.FeeRecipientSet(user1, 0.15e18); + emit IFolio.FeeRecipientsSet(recipients); folio.setFeeRecipients(recipients); (address r1, uint256 bps1) = folio.feeRecipients(0); @@ -675,9 +673,7 @@ contract FolioTest is BaseTest { recipients[1] = IFolio.FeeRecipient(feeReceiver, 0.05e18); recipients[2] = IFolio.FeeRecipient(user1, 0.15e18); vm.expectEmit(true, true, false, true); - emit IFolio.FeeRecipientSet(owner, 0.8e18); - emit IFolio.FeeRecipientSet(feeReceiver, 0.05e18); - emit IFolio.FeeRecipientSet(user1, 0.15e18); + emit IFolio.FeeRecipientsSet(recipients); folio.setFeeRecipients(recipients); assertEq(folio.daoPendingFeeShares(), 0, "wrong dao pending fee shares"); @@ -880,9 +876,23 @@ contract FolioTest is BaseTest { function test_setFeeRecipients_EmptyList() public { vm.startPrank(owner); - IFolio.FeeRecipient[] memory recipients = new IFolio.FeeRecipient[](0); - vm.expectRevert(IFolio.Folio__BadFeeTotal.selector); - folio.setFeeRecipients(recipients); + vm.expectEmit(true, true, false, true); + emit IFolio.FeeRecipientsSet(new IFolio.FeeRecipient[](0)); + folio.setFeeRecipients(new IFolio.FeeRecipient[](0)); + vm.stopPrank(); + + vm.expectRevert(); + folio.feeRecipients(0); + + // distributeFees should give all fees to DAO + + vm.warp(block.timestamp + YEAR_IN_SECONDS); + vm.roll(block.number + 1); + + folio.distributeFees(); + assertEq(folio.getPendingFeeShares(), 0); + assertApproxEqRel(folio.balanceOf(dao), (INITIAL_SUPPLY * 0.1111e18) / 1e18, 0.001e18); + assertEq(folio.balanceOf(feeReceiver), 0); } function test_setFeeRecipients_TooManyRecipients() public {