Skip to content

Commit

Permalink
refactor: swtich fee calculation functions with 18 decimals instead o…
Browse files Browse the repository at this point in the history
…f 5 to reduce precision loss
  • Loading branch information
0xChin committed Nov 19, 2024
1 parent 8692000 commit 1a3b739
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 45 deletions.
15 changes: 10 additions & 5 deletions script/Deploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ contract Deploy is Script {
address[] tokens;
IPool aavePool;
uint256 initialFee;
uint256 initialPerformanceFee;
VaultDeploymentParams[] vaults;
}

Expand Down Expand Up @@ -61,7 +62,8 @@ contract Deploy is Script {
params = DeploymentParams({
tokens: _tokens,
aavePool: IPool(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2),
initialFee: 100,
initialFee: 0.01 ether, // 1%
initialPerformanceFee: 0.05 ether, // 5%
vaults: _vaults
});
} else if (chainId == 10) {
Expand All @@ -75,7 +77,8 @@ contract Deploy is Script {
params = DeploymentParams({
tokens: _tokens,
aavePool: IPool(0x794a61358D6845594F94dc1DB02A252b5b4814aD),
initialFee: 100,
initialFee: 0.01 ether, // 1%
initialPerformanceFee: 0.05 ether, // 5%
vaults: _vaults
});
} else if (chainId == 11_155_420) {
Expand All @@ -93,7 +96,8 @@ contract Deploy is Script {
params = DeploymentParams({
tokens: _tokens,
aavePool: IPool(0xb50201558B00496A145fE76f7424749556E326D8),
initialFee: 100,
initialFee: 0.01 ether, // 1%
initialPerformanceFee: 0.05 ether, // 5%
vaults: _vaults
});
} else if (chainId == 421_614) {
Expand All @@ -111,7 +115,8 @@ contract Deploy is Script {
params = DeploymentParams({
tokens: _tokens,
aavePool: IPool(0xBfC91D59fdAA134A4ED45f7B584cAf96D7792Eff),
initialFee: 100,
initialFee: 0.01 ether, // 1%
initialPerformanceFee: 0.05 ether, // 5%
vaults: _vaults
});
} else {
Expand All @@ -126,7 +131,7 @@ contract Deploy is Script {
vm.startBroadcast();
}
// Deploy Grateful contract
grateful = new Grateful(_params.tokens, _params.aavePool, _params.initialFee);
grateful = new Grateful(_params.tokens, _params.aavePool, _params.initialFee, _params.initialPerformanceFee);

// Deploy vaults and add them to Grateful
uint256 vaultsLength = _params.vaults.length;
Expand Down
32 changes: 19 additions & 13 deletions src/contracts/Grateful.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ contract Grateful is IGrateful, Ownable2Step, ReentrancyGuard {
//////////////////////////////////////////////////////////////*/

/// @inheritdoc IGrateful
uint256 public constant MAX_FEE = 10_000; // Max 100% fee (10000 basis points)
uint256 public constant MAX_FEE = 1 ether; // Max 100% fee (1 ether)

/// @inheritdoc IGrateful
uint256 public constant MAX_PERFORMANCE_FEE = 5000; // Max 50% performance fee (5000 basis points)
uint256 public constant MAX_PERFORMANCE_FEE = 0.5 ether; // Max 50% performance fee (0.5 ether)

/*//////////////////////////////////////////////////////////////
STATE VARIABLES
Expand Down Expand Up @@ -68,7 +68,7 @@ contract Grateful is IGrateful, Ownable2Step, ReentrancyGuard {
mapping(uint256 => bool) public paymentIds;

/// @inheritdoc IGrateful
uint256 public performanceFeeRate = 500; // 5% fee
uint256 public performanceFee;

/*//////////////////////////////////////////////////////////////
MODIFIERS
Expand Down Expand Up @@ -102,14 +102,20 @@ contract Grateful is IGrateful, Ownable2Step, ReentrancyGuard {
* @notice Initializes the Grateful contract.
* @param _tokens Array of token addresses to whitelist.
* @param _aavePool Address of the Aave V3 pool.
* @param _initialFee Initial fee in basis points (10000 = 100%).
* @param _initialFee Initial fee in fixed-point (1 ether = 100%).
*/
constructor(address[] memory _tokens, IPool _aavePool, uint256 _initialFee) Ownable(msg.sender) {
constructor(
address[] memory _tokens,
IPool _aavePool,
uint256 _initialFee,
uint256 _initialPerformanceFee
) Ownable(msg.sender) {
if (address(_aavePool) == address(0)) {
revert Grateful_InvalidAddress();
}
aavePool = _aavePool;
fee = _initialFee;
performanceFee = _initialPerformanceFee;
uint256 tokensLength = _tokens.length;
for (uint256 i = 0; i < tokensLength;) {
tokensWhitelisted[_tokens[i]] = true;
Expand Down Expand Up @@ -148,7 +154,7 @@ contract Grateful is IGrateful, Ownable2Step, ReentrancyGuard {
if (customFees[_merchant].isSet) {
feePercentage = customFees[_merchant].fee;
}
uint256 feeAmount = (_amount * feePercentage) / 10_000;
uint256 feeAmount = (_amount * feePercentage) / 1e18;
return _amount - feeAmount;
}

Expand All @@ -174,7 +180,7 @@ contract Grateful is IGrateful, Ownable2Step, ReentrancyGuard {
function calculatePerformanceFee(
uint256 _profit
) public view returns (uint256 feeAmount) {
feeAmount = (_profit * performanceFeeRate) / 10_000;
feeAmount = (_profit * performanceFee) / 1e18;
}

/*//////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -355,14 +361,14 @@ contract Grateful is IGrateful, Ownable2Step, ReentrancyGuard {
}

/// @inheritdoc IGrateful
function setPerformanceFeeRate(
uint256 _newPerformanceFeeRate
function setPerformanceFee(
uint256 _newPerformanceFee
) external onlyOwner {
if (_newPerformanceFeeRate > MAX_PERFORMANCE_FEE) {
if (_newPerformanceFee > MAX_PERFORMANCE_FEE) {
revert Grateful_FeeRateTooHigh();
}
performanceFeeRate = _newPerformanceFeeRate;
emit PerformanceFeeRateUpdated(_newPerformanceFeeRate);
performanceFee = _newPerformanceFee;
emit PerformanceFeeUpdated(_newPerformanceFee);
}

/// @inheritdoc IGrateful
Expand Down Expand Up @@ -512,7 +518,7 @@ contract Grateful is IGrateful, Ownable2Step, ReentrancyGuard {
if (assetsToWithdraw > initialDepositToWithdraw) {
profit = assetsToWithdraw - initialDepositToWithdraw;
performanceFeeAmount = calculatePerformanceFee(profit);
assetsToWithdraw -= performanceFeeAmount; // Deduct fee from assets
assetsToWithdraw -= performanceFeeAmount;
}

// Update user's shares and deposits before external calls
Expand Down
18 changes: 5 additions & 13 deletions src/interfaces/IGrateful.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,6 @@ interface IGrateful {
*/
event OneTimePaymentCreated(address indexed merchant, address[] tokens, uint256 amount);

/**
* @notice Emitted when funds are withdrawn.
* @param user Address of the user withdrawing funds.
* @param token Address of the token.
* @param amount Amount withdrawn.
*/
event Withdraw(address indexed user, address indexed token, uint256 amount);

/**
* @notice Emitted when the default fee is updated.
* @param newFee The new fee in basis points.
Expand All @@ -69,7 +61,7 @@ interface IGrateful {
* @notice Emitted when the performance fee rate is updated.
* @param newRate The new performance fee rate in basis points.
*/
event PerformanceFeeRateUpdated(uint256 newRate);
event PerformanceFeeUpdated(uint256 newRate);

/**
* @notice Emitted when a custom fee is set for a merchant.
Expand Down Expand Up @@ -206,7 +198,7 @@ interface IGrateful {

/// @notice Returns the performance fee rate.
/// @return Performance fee rate in basis points.
function performanceFeeRate() external view returns (uint256);
function performanceFee() external view returns (uint256);

/// @notice Returns the custom fee applied to the payments for a merchant.
/// @param _merchant Address of the merchant.
Expand Down Expand Up @@ -412,10 +404,10 @@ interface IGrateful {

/**
* @notice Sets the performance fee rate.
* @param _newPerformanceFeeRate The new performance fee rate in basis points.
* @param _newPerformanceFee The new performance fee rate in basis points.
*/
function setPerformanceFeeRate(
uint256 _newPerformanceFeeRate
function setPerformanceFee(
uint256 _newPerformanceFee
) external;

/**
Expand Down
25 changes: 13 additions & 12 deletions test/integration/Grateful.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ contract IntegrationGrateful is IntegrationBase {
// Calculate profit before withdrawal
uint256 profit = _grateful.calculateProfit(_merchant, tokenAddr);

uint256 assetsToWithdraw = grateful.calculateAssets(_merchant, tokenAddr);
uint256 assetsToWithdraw = _grateful.calculateAssets(_merchant, tokenAddr);

// Merchant withdraws funds
vm.prank(_merchant);
Expand Down Expand Up @@ -135,15 +135,15 @@ contract IntegrationGrateful is IntegrationBase {
assertApproxEqAbs(
IERC20(tokenAddr).balanceOf(_merchant),
withdrawalAmount - performanceFee,
(withdrawalAmount - performanceFee) / 1000, // Precission loss tolerance of 0.1%
(withdrawalAmount - performanceFee) / 1000, // Precision loss tolerance of 0.1%
string(abi.encodePacked(_tokenSymbols[tokenAddr], ": Merchant balance mismatch after partial withdrawal"))
);

// Verify that the merchant still has some assets in the vault
assertApproxEqAbs(
_grateful.calculateAssets(_merchant, tokenAddr),
expectedRemainingAssets,
expectedRemainingAssets / 1000, // Precission loss tolerance of 0.1%
expectedRemainingAssets / 1000, // Precision loss tolerance of 0.1%
string(abi.encodePacked(_tokenSymbols[tokenAddr], ": Remaining assets mismatch after partial withdrawal"))
);

Expand All @@ -155,7 +155,7 @@ contract IntegrationGrateful is IntegrationBase {
assertApproxEqAbs(
ownerFinalBalance - ownerInitialBalance,
ownerExpectedBalanceIncrease,
ownerExpectedBalanceIncrease / 1000, // Precission loss tolerance of 0.1%
ownerExpectedBalanceIncrease / 1000, // Precision loss tolerance of 0.1%
string(
abi.encodePacked(
_tokenSymbols[tokenAddr], ": Owner did not receive correct performance fee after partial withdrawal"
Expand Down Expand Up @@ -310,9 +310,9 @@ contract IntegrationGrateful is IntegrationBase {
vm.assume(amountMultiplier < 1000);

uint256[] memory customFees = new uint256[](3);
customFees[0] = 200; // 2%
customFees[0] = 0.02 ether; // 2%
customFees[1] = 0; // 0%
customFees[2] = _FEE; // Default fee after unsetting custom fee
customFees[2] = _FEE; // Default fee after unsetting custom fee (1%)

for (uint256 i = 0; i < _tokens.length; i++) {
address tokenAddr = _tokens[i];
Expand All @@ -323,7 +323,7 @@ contract IntegrationGrateful is IntegrationBase {
uint256 expectedMerchantBalance = 0;

for (uint256 j = 0; j < customFees.length; j++) {
// Set custom fee
// Set or unset custom fee
vm.prank(_owner);
if (j < 2) {
_grateful.setCustomFee(customFees[j], _merchant);
Expand All @@ -337,12 +337,13 @@ contract IntegrationGrateful is IntegrationBase {
// Process payment
_approveAndPay(_user, _merchant, tokenAddr, amount, _NOT_YIELDING_FUNDS);

// Calculate expected amounts
uint256 feeAmount = (amount * customFees[j]) / 10_000;
uint256 merchantAmount = amount - feeAmount;
// Calculate expected amounts using fixed-point arithmetic
uint256 feeAmount = _grateful.applyFee(_merchant, amount);
uint256 initialFee = amount - feeAmount;

expectedOwnerBalance += feeAmount;
expectedMerchantBalance += merchantAmount;
// Update expected balances
expectedOwnerBalance += initialFee;
expectedMerchantBalance += feeAmount;

// Verify balances
assertEq(
Expand Down
4 changes: 2 additions & 2 deletions test/integration/IntegrationBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract IntegrationBase is Test, Deploy {
CONSTANTS
//////////////////////////////////////////////////////////////*/

uint256 internal constant _FEE = 100; // 1% fee
uint256 internal constant _FEE = 0.01 ether; // 1% fee
uint256 internal constant _PAYMENT_SALT = 4; // Salt for computing payment addresses
bool internal constant _YIELDING_FUNDS = true;
bool internal constant _NOT_YIELDING_FUNDS = false;
Expand Down Expand Up @@ -55,7 +55,7 @@ contract IntegrationBase is Test, Deploy {
IGrateful internal _grateful;

/*//////////////////////////////////////////////////////////////
SETUP FUNCTION
SETUP FUNCTION
//////////////////////////////////////////////////////////////*/

function setUp() public {
Expand Down

0 comments on commit 1a3b739

Please sign in to comment.