Skip to content

Commit

Permalink
Break down fee calculator interfaces
Browse files Browse the repository at this point in the history
The pool contract needs to estimate the fee
to charge for a deposit or redemption separately
from executing the distribution of the total fee
to the fee recipients because in between the two
the pool contract needs to either burn the tokens
from the user in case of a redemption or mint tokens
to the user in case of a deposit.

In order to best facilitate this requirement the
current fee calculator interface needs to separate
estimating the fee of a deposit/redemption from
estimating the shares for each fee recipient.
  • Loading branch information
0xmichalis committed Dec 20, 2023
1 parent fbcbc73 commit d4cf262
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 188 deletions.
44 changes: 20 additions & 24 deletions src/FeeCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
// If you encounter a vulnerability or an issue, please contact <info@neutralx.com>
pragma solidity ^0.8.13;

import "./interfaces/IDepositFeeCalculator.sol";
import "./interfaces/IRedemptionFeeCalculator.sol";
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";

/// @title FeeCalculator
/// @author Neutral Labs Inc.
/// @notice This contract calculates deposit and redemption fees for a given pool.
/// @dev It implements IDepositFeeCalculator and IRedemptionFeeCalculator interfaces.
contract FeeCalculator is IDepositFeeCalculator, IRedemptionFeeCalculator, Ownable {
/// @dev It implements the IFeeCalculator interface.
contract FeeCalculator is IFeeCalculator, Ownable {
SD59x18 private zero = sd(0);
SD59x18 private one = sd(1e18);

Expand Down Expand Up @@ -131,34 +131,32 @@ contract FeeCalculator is IDepositFeeCalculator, IRedemptionFeeCalculator, Ownab
_shares = shares;
}

/// @notice Calculates the deposit fees for a given amount.
/// @notice Calculates the deposit fee for a given amount.
/// @param tco2 The address of the TCO2 token.
/// @param pool The address of the pool.
/// @param depositAmount The amount to be deposited.
/// @return recipients The addresses of the fee recipients.
/// @return feesDenominatedInPoolTokens The amount of fees each recipient should receive.
/// @return feeAmount The fee to be charged in pool
/// tokens for this deposit.
function calculateDepositFees(address tco2, address pool, uint256 depositAmount)
external
view
override
returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens)
returns (uint256 feeAmount)
{
require(depositAmount > 0, "depositAmount must be > 0");

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

require(totalFee <= depositAmount, "Fee must be lower or equal to deposit amount");
require(totalFee > 0, "Fee must be greater than 0");
feeAmount = getDepositFee(depositAmount, getTokenBalance(pool, tco2), getTotalSupply(pool));

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

/// @notice Distributes the total fee among the recipients according to their shares.
/// @notice Calculates the total fee among the recipients according to their shares.
/// @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 distributeFeeAmongShares(uint256 totalFee)
private
function calculateFeeAmongShares(uint256 totalFee)
external
view
returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens)
{
Expand All @@ -179,22 +177,20 @@ contract FeeCalculator is IDepositFeeCalculator, IRedemptionFeeCalculator, Ownab
/// @param tco2 The address of the TCO2 token.
/// @param pool The address of the pool.
/// @param redemptionAmount The amount to be redeemed.
/// @return recipients The addresses of the fee recipients.
/// @return feesDenominatedInPoolTokens The amount of fees each recipient should receive.
/// @return feeAmount The fee to be charged in pool
/// tokens for this redemption.
function calculateRedemptionFees(address tco2, address pool, uint256 redemptionAmount)
external
view
override
returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens)
returns (uint256 feeAmount)
{
require(redemptionAmount > 0, "redemptionAmount must be > 0");

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

require(totalFee <= redemptionAmount, "Fee must be lower or equal to redemption amount");
require(totalFee > 0, "Fee must be greater than 0");
feeAmount = getRedemptionFee(redemptionAmount, getTokenBalance(pool, tco2), getTotalSupply(pool));

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

/// @notice Gets the balance of the TCO2 token in a given pool.
Expand Down
21 changes: 0 additions & 21 deletions src/interfaces/IDepositFeeCalculator.sol

This file was deleted.

40 changes: 40 additions & 0 deletions src/interfaces/IFeeCalculator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: 2023 Neutral Labs Inc.
//
// SPDX-License-Identifier: UNLICENSED

// If you encounter a vulnerability or an issue, please contact <info@neutralx.com>
pragma solidity ^0.8.13;

/// @title IFeeCalculator
/// @author Neutral Labs Inc.
/// @notice This interface defines methods for calculating fees.
interface IFeeCalculator {
/// @notice Calculates the deposit fee for a given amount.
/// @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.
function calculateDepositFees(address tco2, address pool, uint256 depositAmount)
external
returns (uint256 feeAmount);

/// @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.
function calculateRedemptionFees(address tco2, address pool, uint256 redemptionAmount)
external
returns (uint256 feeAmount);

/// @notice Calculates the total fee among the recipients according to their shares.
/// @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 calculateFeeAmongShares(uint256 totalFee)
external
view
returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens);
}
21 changes: 0 additions & 21 deletions src/interfaces/IRedemptionFeeCalculator.sol

This file was deleted.

33 changes: 12 additions & 21 deletions test/FeeCalculator.fuzzy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,8 @@ contract FeeCalculatorTestFuzzy is Test {

// Act
try feeCalculator.calculateDepositFees(address(mockToken), address(mockPool), depositAmount) returns (
address[] memory recipients, uint256[] memory fees
) {
// Assert
assertEq(recipients[0], feeRecipient);
} catch Error(string memory reason) {
uint256 feeAmount
) {} catch Error(string memory reason) {
assertTrue(
keccak256(bytes("Fee must be greater than 0")) == keccak256(bytes(reason))
|| keccak256(bytes("Fee must be lower or equal to deposit amount")) == keccak256(bytes(reason)),
Expand Down Expand Up @@ -124,12 +121,9 @@ contract FeeCalculatorTestFuzzy is Test {

// Act
try feeCalculator.calculateRedemptionFees(address(mockToken), address(mockPool), redemptionAmount) returns (
address[] memory recipients, uint256[] memory fees
uint256 feeAmount
) {
oneTimeFee = fees[0];

// Assert
assertEq(recipients[0], feeRecipient);
oneTimeFee = feeAmount;
} catch Error(string memory reason) {
oneTimeRedemptionFailed = true;
assertTrue(
Expand All @@ -152,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 (
address[] memory recipients, uint256[] memory fees
uint256 feeAmount
) {
feeFromDividedRedemptions += fees[0];
feeFromDividedRedemptions += feeAmount;
total -= redemption;
current -= redemption;
mockPool.setTotalSupply(total);
Expand Down Expand Up @@ -210,12 +204,9 @@ contract FeeCalculatorTestFuzzy is Test {

// Act
try feeCalculator.calculateDepositFees(address(mockToken), address(mockPool), depositAmount) returns (
address[] memory recipients, uint256[] memory fees
uint256 feeAmount
) {
oneTimeFee = fees[0];

// Assert
assertEq(recipients[0], feeRecipient);
oneTimeFee = feeAmount;
} catch Error(string memory reason) {
oneTimeDepositFailed = true;
assertTrue(
Expand All @@ -233,9 +224,9 @@ contract FeeCalculatorTestFuzzy is Test {
uint256 deposit = equalDeposit + (i == 0 ? restDeposit : 0);

try feeCalculator.calculateDepositFees(address(mockToken), address(mockPool), deposit) returns (
address[] memory recipients, uint256[] memory fees
uint256 feeAmount
) {
feeFromDividedDeposits += fees[0];
feeFromDividedDeposits += feeAmount;
total += deposit;
current += deposit;
mockPool.setTotalSupply(total);
Expand Down Expand Up @@ -286,8 +277,8 @@ contract FeeCalculatorTestFuzzy is Test {
mockToken.setTokenBalance(address(mockPool), 100 * 1e18);

// Act
(address[] memory recipients, uint256[] memory fees) =
feeCalculator.calculateDepositFees(address(mockToken), address(mockPool), depositAmount);
uint256 feeAmount = feeCalculator.calculateDepositFees(address(mockToken), address(mockPool), depositAmount);
(address[] memory recipients, uint256[] memory fees) = feeCalculator.calculateFeeAmongShares(feeAmount);

// Assert
for (uint256 i = 0; i < recipients.length; i++) {
Expand Down
Loading

0 comments on commit d4cf262

Please sign in to comment.