Skip to content

Commit

Permalink
chore: refactor redemption interface
Browse files Browse the repository at this point in the history
Refactor redemption interface to support
multi-TCO2 inputs and restrict to a single
TCO2 at the same time. This should help
make the interface future-proof to support
multi-TCO2 redemptions in the future.
  • Loading branch information
0xmichalis committed Jan 25, 2024
1 parent 97a18fb commit 18bbf83
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 31 deletions.
11 changes: 8 additions & 3 deletions src/FeeCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,21 @@ contract FeeCalculator is IFeeCalculator, Ownable {

/// @notice Calculates the redemption fees for a given amount.
/// @param pool The address of the pool.
/// @param tco2 The address of the TCO2 token.
/// @param redemptionAmount The amount to be redeemed.
/// @param tco2s The addresses of the TCO2 token.
/// @param redemptionAmounts The amounts to be redeemed.
/// @return feeDistribution How the fee is meant to be
/// distributed among the fee recipients.
function calculateRedemptionFees(address pool, address tco2, uint256 redemptionAmount)
function calculateRedemptionFees(address pool, address[] calldata tco2s, uint256[] calldata redemptionAmounts)
external
view
override
returns (FeeDistribution memory feeDistribution)
{
require(tco2s.length == redemptionAmounts.length, "length mismatch");
require(tco2s.length == 1, "only one");
address tco2 = tco2s[0];
uint256 redemptionAmount = redemptionAmounts[0];

require(redemptionAmount > 0, "redemptionAmount must be > 0");

uint256 feeAmount = getRedemptionFee(redemptionAmount, getTokenBalance(pool, tco2), getTotalSupply(pool));
Expand Down
6 changes: 3 additions & 3 deletions src/interfaces/IFeeCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ interface IFeeCalculator {

/// @notice Calculates the redemption fees for a given amount.
/// @param pool The address of the pool.
/// @param tco2 The address of the TCO2 token.
/// @param redemptionAmount The amount to be redeemed.
/// @param tco2s The addresses of the TCO2 token.
/// @param redemptionAmounts The amounts to be redeemed.
/// @return feeDistribution How the fee is meant to be
/// distributed among the fee recipients.
function calculateRedemptionFees(address pool, address tco2, uint256 redemptionAmount)
function calculateRedemptionFees(address pool, address[] calldata tco2s, uint256[] calldata redemptionAmounts)
external
view
returns (FeeDistribution memory feeDistribution);
Expand Down
10 changes: 8 additions & 2 deletions test/FeeCalculator.fuzzy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,13 @@ contract FeeCalculatorTestFuzzy is Test {
bool oneTimeRedemptionFailed = false;
uint256 multipleTimesRedemptionFailedCount = 0;

address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Act
try feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), redemptionAmount) returns (
try feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts) returns (
FeeDistribution memory feeDistribution
) {
oneTimeFee = feeDistribution.shares.sumOf();
Expand All @@ -145,7 +150,8 @@ contract FeeCalculatorTestFuzzy is Test {

for (uint256 i = 0; i < numberOfRedemptions; i++) {
uint256 redemption = equalRedemption + (i == 0 ? restRedemption : 0);
try feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), redemption) returns (
redemptionAmounts[0] = redemption;
try feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts) returns (
FeeDistribution memory feeDistribution
) {
feeFromDividedRedemptions += feeDistribution.shares.sumOf();
Expand Down
105 changes: 82 additions & 23 deletions test/FeeCalculator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,18 @@ contract FeeCalculatorTest is Test {
// Arrange
// Set up your test data
uint256 redemptionAmount = 100 * 1e18;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(1000 * 1e18);
mockToken.setTokenBalance(address(mockPool), 500 * 1e18);

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), redemptionAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
address[] memory recipients = feeDistribution.recipients;
uint256[] memory fees = feeDistribution.shares;

Expand All @@ -109,14 +113,18 @@ contract FeeCalculatorTest is Test {
// Arrange
// Set up your test data
uint256 redemptionAmount = 1 * 1e18;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(1e6 * 1e18);
mockToken.setTokenBalance(address(mockPool), 1 * 1e18);

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), redemptionAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
address[] memory recipients = feeDistribution.recipients;
uint256[] memory fees = feeDistribution.shares;

Expand All @@ -131,14 +139,18 @@ contract FeeCalculatorTest is Test {
// Arrange
// Set up your test data
uint256 redemptionAmount = 1 * 1e18;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(1e6 * 1e18);
mockToken.setTokenBalance(address(mockPool), 1e6 * 1e18);

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), redemptionAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
address[] memory recipients = feeDistribution.recipients;
uint256[] memory fees = feeDistribution.shares;

Expand Down Expand Up @@ -453,7 +465,11 @@ contract FeeCalculatorTest is Test {
function testCalculateRedemptionFees_CurrentGreaterThanTotal_ExceptionShouldBeThrown() public {
// Arrange
// Set up your test data
uint256 depositAmount = 1;
uint256 redemptionAmount = 1;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(1000 * 1e18);
Expand All @@ -463,35 +479,43 @@ contract FeeCalculatorTest is Test {
vm.expectRevert(
"The total volume in the pool must be greater than or equal to the volume for an individual asset"
);
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), depositAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
}

function testCalculateRedemptionFees_AmountGreaterThanCurrent_ExceptionShouldBeThrown() public {
// Arrange
// Set up your test data
uint256 depositAmount = 600 * 1e18;
uint256 redemptionAmount = 600 * 1e18;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(1000 * 1e18);
mockToken.setTokenBalance(address(mockPool), 500 * 1e18);

// Act
vm.expectRevert("The amount to be redeemed cannot exceed the current balance of the pool");
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), depositAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
}

function testCalculateRedemptionFees_ZeroRedemption_ExceptionShouldBeThrown() public {
// Arrange
// Set up your test data
uint256 depositAmount = 0;
uint256 redemptionAmount = 0;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(1000 * 1e18);
mockToken.setTokenBalance(address(mockPool), 500 * 1e18);

// Act & Assert
vm.expectRevert("redemptionAmount must be > 0");
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), depositAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
}

function testCalculateDepositFees_EmptyPool_FeeCappedAt10Percent() public {
Expand Down Expand Up @@ -538,28 +562,36 @@ contract FeeCalculatorTest is Test {
// Arrange
// Set up your test data
uint256 redemptionAmount = 0;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(1000 * 1e18);
mockToken.setTokenBalance(address(mockPool), 500 * 1e18);

// Act
vm.expectRevert("redemptionAmount must be > 0");
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), redemptionAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
}

function testCalculateRedemptionFees_TotalEqualCurrent_FeeCappedAt10Percent() public {
// Arrange
// Set up your test data
uint256 redemptionAmount = 100;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(1000);
mockToken.setTokenBalance(address(mockPool), 1000);

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), redemptionAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
address[] memory recipients = feeDistribution.recipients;
uint256[] memory fees = feeDistribution.shares;

Expand Down Expand Up @@ -631,7 +663,11 @@ contract FeeCalculatorTest is Test {
function testCalculateRedemptionFees_HugeTotalLargeCurrentSmallDeposit_FeeCappedAt30Percent() public {
// Arrange
// Set up your test data
uint256 depositAmount = 10000 * 1e18;
uint256 redemptionAmount = 10000 * 1e18;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
uint256 supply = 100000 * 1e18;
Expand All @@ -640,33 +676,37 @@ contract FeeCalculatorTest is Test {

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), depositAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
address[] memory recipients = feeDistribution.recipients;
uint256[] memory fees = feeDistribution.shares;

// Assert
assertEq(recipients[0], feeRecipient);
assertEq(fees[0], depositAmount * 30 / 100);
assertEq(fees[0], redemptionAmount * 30 / 100);
}

function testCalculateRedemptionFees_NegativeFeeValue_FeeCappedAt30Percent() public {
// Arrange
// Set up your test data
uint256 depositAmount = 2323662174650;
uint256 redemptionAmount = 2323662174650;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(56636794628913227180683983236);
mockToken.setTokenBalance(address(mockPool), 55661911070827884041095553095);

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), depositAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
address[] memory recipients = feeDistribution.recipients;
uint256[] memory fees = feeDistribution.shares;

// Assert
assertEq(recipients[0], feeRecipient);
assertEq(fees[0], depositAmount * 30 / 100);
assertEq(fees[0], redemptionAmount * 30 / 100);
}

function testFeeSetup_RecipientsEmpty_ShouldThrowError() public {
Expand Down Expand Up @@ -846,14 +886,19 @@ contract FeeCalculatorTest is Test {

function testSetRedemptionFeeScale() public {
// Arrange
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = 100e18;

// Set up mock pool
mockPool.setTotalSupply(1000 * 1e18);
mockToken.setTokenBalance(address(mockPool), 500 * 1e18);
feeCalculator.setRedemptionFeeScale(0.4 * 1e18);

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), 100e18);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
uint256[] memory fees = feeDistribution.shares;

// Assert
Expand All @@ -862,14 +907,19 @@ contract FeeCalculatorTest is Test {

function testSetRedemptionFeeShift() public {
// Arrange
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = 100e18;

// Set up mock pool
mockPool.setTotalSupply(1000 * 1e18);
mockToken.setTokenBalance(address(mockPool), 500 * 1e18);
feeCalculator.setRedemptionFeeShift(0.5 * 1e18);

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), 100e18);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
uint256[] memory fees = feeDistribution.shares;

// Assert
Expand All @@ -878,14 +928,19 @@ contract FeeCalculatorTest is Test {

function testSetSingleAssetRedemptionRelativeFee() public {
// Arrange
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = 100 * 1e18;

// Set up mock pool
mockPool.setTotalSupply(1000 * 1e18);
mockToken.setTokenBalance(address(mockPool), 1000 * 1e18);
feeCalculator.setSingleAssetRedemptionRelativeFee(0.83 * 1e18);

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), 100 * 1e18);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
uint256[] memory fees = feeDistribution.shares;

assertEq(fees[0], 83 * 1e18);
Expand All @@ -894,7 +949,11 @@ contract FeeCalculatorTest is Test {
function testSetDustAssetRedemptionRelativeFee() public {
// Arrange
// Set up your test data
uint256 depositAmount = 2323662174650;
uint256 redemptionAmount = 2323662174650;
address[] memory tco2s = new address[](1);
tco2s[0] = address(mockToken);
uint256[] memory redemptionAmounts = new uint256[](1);
redemptionAmounts[0] = redemptionAmount;

// Set up mock pool
mockPool.setTotalSupply(56636794628913227180683983236);
Expand All @@ -903,10 +962,10 @@ contract FeeCalculatorTest is Test {

// Act
FeeDistribution memory feeDistribution =
feeCalculator.calculateRedemptionFees(address(mockPool), address(mockToken), depositAmount);
feeCalculator.calculateRedemptionFees(address(mockPool), tco2s, redemptionAmounts);
uint256[] memory fees = feeDistribution.shares;

// Assert
assertEq(fees[0], depositAmount * 91 / 100);
assertEq(fees[0], redemptionAmount * 91 / 100);
}
}

0 comments on commit 18bbf83

Please sign in to comment.