Skip to content

Commit

Permalink
Consolidate interface functions
Browse files Browse the repository at this point in the history
Consolidate the fee module interface functions
into a single function per action as originally
developed.

This consolidation should:
* make it easier for pools to integrate with the
fee module since there is only a single function
per action to use
* make the interface future-proof in case fee
modules in the future are determining fee recipients
based on the TCO2 that is deposited to or redeemed
from the pool
* make the fee calculation more performant since
there is only a single CALL to the fee module
instead of two per action

This change reverts 99f19a4
  • Loading branch information
0xmichalis committed Jan 17, 2024
1 parent 5ded2d4 commit 149ebf0
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 161 deletions.
69 changes: 24 additions & 45 deletions src/FeeCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import {SD59x18, sd, intoUint256} from "@prb/math/src/SD59x18.sol";

import "./interfaces/IFeeCalculator.sol";
import {IFeeCalculator, FeeDistribution} from "./interfaces/IFeeCalculator.sol";
import "./interfaces/IPool.sol";

/// @title FeeCalculator
Expand Down Expand Up @@ -136,86 +136,65 @@ contract FeeCalculator is IFeeCalculator, Ownable {
/// @param tco2 The address of the TCO2 token.
/// @param pool The address of the pool.
/// @param depositAmount The amount to be deposited.
/// @return feeAmount The fee to be charged in pool
/// tokens for this deposit.
/// @return feeDistribution How the fee is meant to be
/// distributed among the fee recipients.
function calculateDepositFees(address tco2, address pool, uint256 depositAmount)
external
view
override
returns (uint256 feeAmount)
returns (FeeDistribution memory feeDistribution)
{
require(depositAmount > 0, "depositAmount must be > 0");

feeAmount = getDepositFee(depositAmount, getTokenBalance(pool, tco2), getTotalSupply(pool));
uint256 feeAmount = getDepositFee(depositAmount, getTokenBalance(pool, tco2), getTotalSupply(pool));

require(feeAmount <= depositAmount, "Fee must be lower or equal to deposit amount");
require(feeAmount > 0, "Fee must be greater than 0");
feeDistribution = calculateFeeShares(feeAmount);
}

/// @notice Calculates the fee shares and recipients based on the total fee.
/// @param totalFee The total fee to be distributed.
/// @return recipients The addresses of the fee recipients.
/// @return feesDenominatedInPoolTokens The amount of fees each recipient should receive.
function calculateFeeShares(uint256 totalFee)
internal
view
returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens)
{
feesDenominatedInPoolTokens = new uint256[](_recipients.length);
/// @return feeDistribution The recipients and the amount of fees each
/// recipient should receive.
function calculateFeeShares(uint256 totalFee) internal view returns (FeeDistribution memory feeDistribution) {
uint256[] memory shares = new uint256[](_recipients.length);

uint256 restFee = totalFee;

for (uint256 i = 0; i < _recipients.length; i++) {
feesDenominatedInPoolTokens[i] = (totalFee * _shares[i]) / 100;
restFee -= feesDenominatedInPoolTokens[i];
shares[i] = (totalFee * _shares[i]) / 100;
restFee -= shares[i];
}

recipients = _recipients;
feesDenominatedInPoolTokens[0] += restFee; //we give rest of the fee (if any) to the first recipient
// If any fee is left, it is distributed to the first recipient.
// This may happen if any of the shares of the fee to be distributed
// has leftover from the division by 100 above.
shares[0] += restFee;

feeDistribution.recipients = _recipients;
feeDistribution.shares = shares;
}

/// @notice Calculates the redemption fees for a given amount.
/// @param tco2 The address of the TCO2 token.
/// @param pool The address of the pool.
/// @param redemptionAmount The amount to be redeemed.
/// @return feeAmount The fee to be charged in pool
/// tokens for this redemption.
/// @return feeDistribution How the fee is meant to be
/// distributed among the fee recipients.
function calculateRedemptionFees(address tco2, address pool, uint256 redemptionAmount)
external
view
override
returns (uint256 feeAmount)
returns (FeeDistribution memory feeDistribution)
{
require(redemptionAmount > 0, "redemptionAmount must be > 0");

feeAmount = getRedemptionFee(redemptionAmount, getTokenBalance(pool, tco2), getTotalSupply(pool));
uint256 feeAmount = getRedemptionFee(redemptionAmount, getTokenBalance(pool, tco2), getTotalSupply(pool));

require(feeAmount <= redemptionAmount, "Fee must be lower or equal to redemption amount");
require(feeAmount > 0, "Fee must be greater than 0");
}

/// @notice Calculates the fee shares and recipients for a deposit based on the total fee.
/// @param totalFee The total fee to be shared.
/// @return recipients The addresses of the fee recipients.
/// @return feesDenominatedInPoolTokens The amount of fees each recipient should receive.
function calculateDepositFeeShares(uint256 totalFee)
external
view
returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens)
{
return calculateFeeShares(totalFee);
}

/// @notice Calculates the fee shares and recipients for a redemption based on the total fee.
/// @param totalFee The total fee to be shared.
/// @return recipients The addresses of the fee recipients.
/// @return feesDenominatedInPoolTokens The amount of fees each recipient should receive.
function calculateRedemptionFeeShares(uint256 totalFee)
external
view
returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens)
{
return calculateFeeShares(totalFee);
feeDistribution = calculateFeeShares(feeAmount);
}

/// @notice Gets the balance of the TCO2 token in a given pool.
Expand Down
35 changes: 11 additions & 24 deletions src/interfaces/IFeeCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
// If you encounter a vulnerability or an issue, please contact <[email protected]>
pragma solidity ^0.8.13;

struct FeeDistribution {
address[] recipients;
uint256[] shares;
}

/// @title IFeeCalculator
/// @author Neutral Labs Inc.
/// @notice This interface defines methods for calculating fees.
Expand All @@ -13,39 +18,21 @@ interface IFeeCalculator {
/// @param tco2 The address of the TCO2 token.
/// @param pool The address of the pool.
/// @param depositAmount The amount to be deposited.
/// @return feeAmount The fee to be charged in pool
/// tokens for this deposit.
/// @return feeDistribution How the fee is meant to be
/// distributed among the fee recipients.
function calculateDepositFees(address tco2, address pool, uint256 depositAmount)
external
view
returns (uint256 feeAmount);
returns (FeeDistribution memory feeDistribution);

/// @notice Calculates the redemption fees for a given amount.
/// @param tco2 The address of the TCO2 token.
/// @param pool The address of the pool.
/// @param redemptionAmount The amount to be redeemed.
/// @return feeAmount The fee to be charged in pool
/// tokens for this redemption.
/// @return feeDistribution How the fee is meant to be
/// distributed among the fee recipients.
function calculateRedemptionFees(address tco2, address pool, uint256 redemptionAmount)
external
view
returns (uint256 feeAmount);

/// @notice Calculates the fee shares and recipients for a deposit based on the total fee.
/// @param totalFee The total fee to be shared.
/// @return recipients The addresses of the fee recipients.
/// @return feesDenominatedInPoolTokens The amount of fees each recipient should receive.
function calculateDepositFeeShares(uint256 totalFee)
external
view
returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens);

/// @notice Calculates the fee shares and recipients for a redemption based on the total fee.
/// @param totalFee The total fee to be shared.
/// @return recipients The addresses of the fee recipients.
/// @return feesDenominatedInPoolTokens The amount of fees each recipient should receive.
function calculateRedemptionFeeShares(uint256 totalFee)
external
view
returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens);
returns (FeeDistribution memory feeDistribution);
}
23 changes: 13 additions & 10 deletions test/FeeCalculator.fuzzy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pragma solidity ^0.8.13;

import {Test, console2} from "forge-std/Test.sol";
import {FeeCalculator} from "../src/FeeCalculator.sol";
import {FeeDistribution} from "../src/interfaces/IFeeCalculator.sol";
import {SD59x18, sd, intoUint256 as sdIntoUint256} from "@prb/math/src/SD59x18.sol";
import {UD60x18, ud, intoUint256} from "@prb/math/src/UD60x18.sol";
import "./TestUtilities.sol";
Expand Down Expand Up @@ -120,9 +121,9 @@ contract FeeCalculatorTestFuzzy is Test {

// Act
try feeCalculator.calculateRedemptionFees(address(mockToken), address(mockPool), redemptionAmount) returns (
uint256 feeAmount
FeeDistribution memory feeDistribution
) {
oneTimeFee = feeAmount;
oneTimeFee = feeDistribution.shares.sumOf();
} catch Error(string memory reason) {
oneTimeRedemptionFailed = true;
assertTrue(
Expand All @@ -145,9 +146,9 @@ contract FeeCalculatorTestFuzzy is Test {
for (uint256 i = 0; i < numberOfRedemptions; i++) {
uint256 redemption = equalRedemption + (i == 0 ? restRedemption : 0);
try feeCalculator.calculateRedemptionFees(address(mockToken), address(mockPool), redemption) returns (
uint256 feeAmount
FeeDistribution memory feeDistribution
) {
feeFromDividedRedemptions += feeAmount;
feeFromDividedRedemptions += feeDistribution.shares.sumOf();
total -= redemption;
current -= redemption;
mockPool.setTotalSupply(total);
Expand Down Expand Up @@ -203,9 +204,9 @@ contract FeeCalculatorTestFuzzy is Test {

// Act
try feeCalculator.calculateDepositFees(address(mockToken), address(mockPool), depositAmount) returns (
uint256 feeAmount
FeeDistribution memory feeDistribution
) {
oneTimeFee = feeAmount;
oneTimeFee = feeDistribution.shares.sumOf();
} catch Error(string memory reason) {
oneTimeDepositFailed = true;
assertTrue(
Expand All @@ -223,9 +224,9 @@ contract FeeCalculatorTestFuzzy is Test {
uint256 deposit = equalDeposit + (i == 0 ? restDeposit : 0);

try feeCalculator.calculateDepositFees(address(mockToken), address(mockPool), deposit) returns (
uint256 feeAmount
FeeDistribution memory feeDistribution
) {
feeFromDividedDeposits += feeAmount;
feeFromDividedDeposits += feeDistribution.shares.sumOf();
total += deposit;
current += deposit;
mockPool.setTotalSupply(total);
Expand Down Expand Up @@ -276,8 +277,10 @@ contract FeeCalculatorTestFuzzy is Test {
mockToken.setTokenBalance(address(mockPool), 100 * 1e18);

// Act
uint256 feeAmount = feeCalculator.calculateDepositFees(address(mockToken), address(mockPool), depositAmount);
(address[] memory gotRecipients, uint256[] memory fees) = feeCalculator.calculateDepositFeeShares(feeAmount);
FeeDistribution memory feeDistribution =
feeCalculator.calculateDepositFees(address(mockToken), address(mockPool), depositAmount);
address[] memory gotRecipients = feeDistribution.recipients;
uint256[] memory fees = feeDistribution.shares;

// Assert
assertEq(gotRecipients.length, recipients.length);
Expand Down
Loading

0 comments on commit 149ebf0

Please sign in to comment.