Skip to content

Commit

Permalink
Merge pull request #9 from neutral-protocol/chore/deposits-into-sd
Browse files Browse the repository at this point in the history
chore: migrate deposits into sd59x60
  • Loading branch information
kosecki123 authored Nov 21, 2023
2 parents 6acf9e4 + 4475e81 commit abf0c6d
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 64 deletions.
4 changes: 4 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 35 additions & 63 deletions src/FeeCalculator.sol
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
pragma solidity ^0.8.13;

import "forge-std/console.sol";
import "./interfaces/IDepositFeeCalculator.sol";
import "./interfaces/IRedemptionFeeCalculator.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { UD60x18, ud, intoUint256 } from "@prb/math/src/UD60x18.sol";
import { SD59x18, sd, intoUint256, convert } from "@prb/math/src/SD59x18.sol";
import {SD59x18, sd, intoUint256} from "@prb/math/src/SD59x18.sol";

contract FeeCalculator is IDepositFeeCalculator, IRedemptionFeeCalculator {
SD59x18 private zero = sd(0);
SD59x18 private one = sd(1e18);

UD60x18 private zero = ud(0);
UD60x18 private one = ud(1e18);

SD59x18 private zero_signed = sd(0);
SD59x18 private one_signed = sd(1e18);

UD60x18 private depositFeeScale = ud(0.18 * 1e18);
UD60x18 private depositFeeRatioScale = ud(0.99 * 1e18);
SD59x18 private depositFeeScale = sd(0.18 * 1e18);
SD59x18 private depositFeeRatioScale = sd(0.99 * 1e18);

SD59x18 private redemptionFeeScale = sd(0.3 * 1e18);
SD59x18 private redemptionFeeShift = sd(0.1 * 1e18);//-log10(0+0.1)=1 -> 10^-1
SD59x18 private redemptionFeeConstant = redemptionFeeScale.mul((one_signed+redemptionFeeShift).log10()); //0.0413926851582251=log10(1+0.1)

uint256 private constant tokenDenominator = 1e18;
uint256 private constant ratioDenominator = 1e12;
uint256 private constant relativeFeeDenominator = ratioDenominator**3;
SD59x18 private redemptionFeeConstant = redemptionFeeScale * (one + redemptionFeeShift).log10(); //0.0413926851582251=log10(1+0.1)

address[] private _recipients;
uint256[] private _shares;
Expand All @@ -44,27 +34,30 @@ contract FeeCalculator is IDepositFeeCalculator, IRedemptionFeeCalculator {
}

function calculateDepositFees(address tco2, address pool, uint256 depositAmount) external override returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens) {
require(depositAmount > 0, "depositAmount must be > 0");

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

function distributeFeeAmongShares(uint256 totalFee) private view returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens) {
recipients = new address[](_recipients.length);
feesDenominatedInPoolTokens = new uint256[](_recipients.length);

uint256 restFee = totalFee;

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

require(restFee >=0);
require(restFee >= 0);
recipients = _recipients;
feesDenominatedInPoolTokens[0] += restFee;//we give rest of the fee (if any) to the first recipient
}

function calculateRedemptionFee(address tco2, address pool, uint256 depositAmount) external override returns (address[] memory recipients, uint256[] memory feesDenominatedInPoolTokens) {
require(depositAmount > 0, "depositAmount must be > 0");

uint256 totalFee = getRedemptionFee(depositAmount, getTokenBalance(pool, tco2), getTotalSupply(pool));
return distributeFeeAmongShares(totalFee);
}
Expand All @@ -79,56 +72,41 @@ contract FeeCalculator is IDepositFeeCalculator, IRedemptionFeeCalculator {
return totalSupply;
}

function getRatiosDeposit(UD60x18 amount, UD60x18 current, UD60x18 total) private view returns (UD60x18, UD60x18)
function getRatiosDeposit(SD59x18 amount, SD59x18 current, SD59x18 total) private view returns (SD59x18, SD59x18)
{
UD60x18 a = total == zero ? zero : current / total;
UD60x18 b = (total + amount) == zero ? zero : (current + amount) / (total + amount);
SD59x18 a = total == zero ? zero : current / total;
SD59x18 b = (current + amount) / (total + amount);

return (a, b);
}

function getRatiosRedemption(SD59x18 amount, SD59x18 current, SD59x18 total) private view returns (SD59x18, SD59x18)
{
SD59x18 a = total == zero_signed ? zero_signed : current / total;
SD59x18 b = (total - amount) == zero_signed ? zero_signed : (current - amount) / (total - amount);
SD59x18 a = total == zero ? zero : current / total;
SD59x18 b = (total - amount) == zero ? zero : (current - amount) / (total - amount);

return (a, b);
}

function getDepositFee(uint256 amount, uint256 current, uint256 total) private view returns (uint256) {
require(total >= current);

UD60x18 amount_float = ud(amount);
UD60x18 ta = ud(current);
UD60x18 tb = ta + amount_float;

(UD60x18 da, UD60x18 db) = getRatiosDeposit(amount_float, ta, ud(total));

//(log10(1 - a * N)*ta - log10(1 - b * N)*tb) * M
//used this property: `log_b(a) = -log_b(1/a)` to not use negative values

UD60x18 one_minus_a = one - da.mul(depositFeeRatioScale);
UD60x18 one_minus_b = one - db.mul(depositFeeRatioScale);
SD59x18 amount_float = sd(int256(amount));
SD59x18 ta = sd(int256(current));
SD59x18 tb = ta + amount_float;

UD60x18 ta_log_a = ta.mul(one_minus_a.inv().log10());
UD60x18 tb_log_b = tb.mul(one_minus_b.inv().log10());
(SD59x18 da, SD59x18 db) = getRatiosDeposit(amount_float, ta, sd(int256(total)));

UD60x18 fee_float;
SD59x18 ta_log_a = ta * (one - da * depositFeeRatioScale).log10();
SD59x18 tb_log_b = tb * (one - db * depositFeeRatioScale).log10();

if(tb_log_b > ta_log_a)
fee_float = depositFeeScale.mul(tb_log_b - ta_log_a);
else
fee_float = depositFeeScale.mul(ta_log_a - tb_log_b);
SD59x18 fee_float = depositFeeScale * (ta_log_a - tb_log_b);

uint256 fee = intoUint256(fee_float);

if(fee > amount)
{
console.log("Fee > amount:\n%d\n>\n%d", fee, amount);
require(fee <= amount, "Fee must be lower or equal to deposit amount");
}

require(fee <= amount, "Fee must be lower or equal to deposit amount");
require(fee > 0, "Fee must be greater than 0");

return fee;
}

Expand All @@ -143,28 +121,22 @@ contract FeeCalculator is IDepositFeeCalculator, IRedemptionFeeCalculator {
(SD59x18 da, SD59x18 db) = getRatiosRedemption(amount_float, ta, sd(int256(total)));

//redemption_fee = scale * (tb * log10(b+shift) - ta * log10(a+shift)) + constant*amount;
SD59x18 i_a = ta * (da + redemptionFeeShift).log10();
SD59x18 i_b = tb * (db + redemptionFeeShift).log10();
SD59x18 fee_float = redemptionFeeScale * (i_b - i_a) + redemptionFeeConstant * amount_float;


SD59x18 i_a = ta.mul(da.add(redemptionFeeShift).log10());
SD59x18 i_b = tb.mul(db.add(redemptionFeeShift).log10());
SD59x18 fee_float = redemptionFeeScale.mul(i_b.sub(i_a)).add(redemptionFeeConstant*amount_float);

if(fee_float < zero_signed)
if (fee_float < zero)
{
if(fee_float / amount_float < sd(1e-6 * 1e18))
if (fee_float / amount_float < sd(1e-6 * 1e18))
//fee_float=zero_signed;//if the fee is negative but is less than 0.0001% of amount than it's basically 0
require(fee_float > zero_signed, "Fee must be greater than 0");
require(fee_float > zero, "Fee must be greater than 0");
else
require(fee_float > zero_signed, "Total failure. Fee must be greater than 0 or at least close to it.");
require(fee_float > zero, "Total failure. Fee must be greater than 0 or at least close to it.");
}

uint256 fee = intoUint256(fee_float);

if(fee > amount)
{
console.log("Fee > amount:\n%d\n>\n%d", fee, amount);
require(fee <= amount, "Fee must be lower or equal to redemption amount");
}
require(fee <= amount, "Fee must be lower or equal to redemption amount");

return fee;
}
Expand Down
3 changes: 2 additions & 1 deletion test/FeeCalculator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ contract FeeCalculatorTest is Test {
fail("Exception should be thrown");
}
catch Error(string memory reason) {
assertEq("Fee must be greater than 0", reason);
assertEq("depositAmount must be > 0", reason);
}
}

Expand Down Expand Up @@ -556,6 +556,7 @@ contract FeeCalculatorTest is Test {
//vm.assume(current > 0);
vm.assume(total >= current);
vm.assume(depositAmount < 1e20 * 1e18);
vm.assume(depositAmount > 0);
vm.assume(total < 1e20 * 1e18);

// Arrange
Expand Down

0 comments on commit abf0c6d

Please sign in to comment.