Skip to content

Commit

Permalink
support empty fee recipients table (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbrent authored Feb 3, 2025
1 parent 3c35a06 commit db744c1
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 13 deletions.
14 changes: 11 additions & 3 deletions contracts/Folio.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -815,19 +816,27 @@ 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++) {
feeRecipients.pop();
}

// 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());
Expand All @@ -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%
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/IFolio.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 19 additions & 9 deletions test/Folio.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit db744c1

Please sign in to comment.