From 8d9c0abe736194b664af8fcc5b6ca71c22f1d5b5 Mon Sep 17 00:00:00 2001 From: Michalis Kargakis Date: Wed, 20 Dec 2023 12:51:52 +0100 Subject: [PATCH] feat: calculate fees for multiple redemptions at once This is going to be useful for both `redeemMany` and eventually `redeemAuto` in the pool contract as these functions already support redemption of multiple TCO2s within a single transaction. We can even get rid of `calculateRedemptionFees` altogether since I don't forsee it being used in the Toucan pools but this can be done in a follow-up if needed. --- src/FeeCalculator.sol | 36 +++++++++++++++++++++ src/interfaces/IRedemptionFeeCalculator.sol | 10 ++++++ 2 files changed, 46 insertions(+) diff --git a/src/FeeCalculator.sol b/src/FeeCalculator.sol index 5f279df..5069596 100644 --- a/src/FeeCalculator.sol +++ b/src/FeeCalculator.sol @@ -197,6 +197,42 @@ contract FeeCalculator is IDepositFeeCalculator, IRedemptionFeeCalculator, Ownab return distributeFeeAmongShares(totalFee); } + /// @notice Calculates the redemption fees across multiple TCO2s for a given amount. + /// @param tco2s The addresses of the TCO2 tokens. + /// @param pool The address of the pool. + /// @param amounts The amounts to be redeemed per TCO2. + /// @return recipients The addresses of the fee recipients. + /// @return feesDenominatedInPoolTokens The amount of fees each recipient should receive. + function calculateRedemptionFeesMany(address[] calldata tco2s, address pool, uint256[] calldata amounts) + external + view + override + returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens) + { + uint256 tco2Len = tco2s.length; + require(tco2Len == amounts.length, "Length mismatch"); + + uint256 totalFee = 0; + uint256 totalPoolSupply = getTotalSupply(pool); + + for (uint256 i = 0; i < tco2Len; ++i) { + uint256 redemptionAmount = amounts[i]; + require(redemptionAmount > 0, "amount must be > 0"); + uint256 tco2Balance = getTokenBalance(pool, tco2s[i]); + uint256 feeAmount = getRedemptionFee(redemptionAmount, tco2Balance, totalPoolSupply); + require(feeAmount <= redemptionAmount, "Fee must be lower or equal to redemption amount"); + totalFee += feeAmount; + // Update total pool supply to account for the tokens to be burnt + // so the next iteration charges fees using the intermediate + // pool supply. + totalPoolSupply = totalPoolSupply - redemptionAmount + feeAmount; + } + + require(totalFee > 0, "Fee must be greater than 0"); + + return distributeFeeAmongShares(totalFee); + } + /// @notice Gets the balance of the TCO2 token in a given pool. /// @param pool The address of the pool. /// @param tco2 The address of the TCO2 token. diff --git a/src/interfaces/IRedemptionFeeCalculator.sol b/src/interfaces/IRedemptionFeeCalculator.sol index a0185f2..5280f76 100644 --- a/src/interfaces/IRedemptionFeeCalculator.sol +++ b/src/interfaces/IRedemptionFeeCalculator.sol @@ -18,4 +18,14 @@ interface IRedemptionFeeCalculator { function calculateRedemptionFees(address tco2, address pool, uint256 depositAmount) external returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens); + + /// @notice Calculates the redemption fees across multiple TCO2s for a given amount. + /// @param tco2s The addresses of the TCO2 tokens. + /// @param pool The address of the pool. + /// @param amounts The amounts to be redeemed per TCO2. + /// @return recipients The addresses of the fee recipients. + /// @return feesDenominatedInPoolTokens The amount of fees each recipient should receive. + function calculateRedemptionFeesMany(address[] calldata tco2s, address pool, uint256[] calldata amounts) + external + returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens); }