Skip to content

Commit

Permalink
refactor: move protocol fee to fee collector (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
cucupac authored Mar 20, 2024
1 parent 9d2a56b commit d9cd361
Show file tree
Hide file tree
Showing 19 changed files with 98 additions and 26 deletions.
13 changes: 11 additions & 2 deletions src/FeeCollector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,19 @@ contract FeeCollector is Ownable, IFeeCollector {
/// @inheritdoc IFeeCollector
mapping(address => mapping(address => uint256)) public balances;

/// @inheritdoc IFeeCollector
uint256 public feeRate;

// Errors
error Unauthorized();
error OutOfRange();

/// @notice This function is called when the FeeCollector is deployed.
/// @param _owner The account address of the FeeCollector contract's owner.
constructor(address _owner) Ownable(_owner) {
/// @param _feeRate The protocol fee rate.
constructor(address _owner, uint256 _feeRate) Ownable(_owner) {
if (msg.sender != CONTRACT_DEPLOYER) revert Unauthorized();
feeRate = _feeRate;
}

/// @inheritdoc IFeeCollector
Expand Down Expand Up @@ -92,10 +97,14 @@ contract FeeCollector is Ownable, IFeeCollector {
**
******************************************************************************/

function setFeeRate(uint256 _feeRate) public payable onlyOwner {
if (_feeRate > 1000) revert OutOfRange();
feeRate = _feeRate;
}

/// @inheritdoc IFeeCollector
function setClientRate(uint256 _clientRate) public payable onlyOwner {
if (_clientRate < 30 || _clientRate > 100) revert OutOfRange();

clientRate = _clientRate;
}

Expand Down
3 changes: 3 additions & 0 deletions src/interfaces/IFeeCollector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ interface IFeeCollector {
/// @return balance The balance for the specified token for the specified client operator.
function balances(address _client, address _token) external view returns (uint256);

/// @notice Returns the current protocol fee rate.
function feeRate() external view returns (uint256);

/// @notice Collects fees from Position contracts.
/// @param _client The address where a client operator will receive protocols fees.
/// @param _token The token to collect fees in (collateral token or debt token of the calling Position contract).
Expand Down
6 changes: 1 addition & 5 deletions src/libraries/FeeLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ import { IFeeCollector } from "src/interfaces/IFeeCollector.sol";
/// @notice Manages all protocol-fee-related interactions
library FeeLib {
// Constants: no SLOAD to save gas

/// @notice The protocol fee rate.
uint256 public constant PROTOCOL_FEE_RATE = 3;

/// @notice The address of the fee collector contract.
address public constant FEE_COLLECTOR = 0x7A7AbDb9E12F3a9845E2433958Eef8FB9C8489Ee;

Expand All @@ -22,7 +18,7 @@ library FeeLib {
/// @param _client The address where a client operator will receive protocols fees.
/// @return cAmtNet The resulting amount of collateral to be supplied after fees are taken.
function takeProtocolFee(address _token, uint256 _amt, address _client) internal returns (uint256 cAmtNet) {
uint256 maxFee = (_amt * PROTOCOL_FEE_RATE) / 1000;
uint256 maxFee = (_amt * IFeeCollector(FEE_COLLECTOR).feeRate()) / 1000;
(uint256 userSavings, uint256 clientFee) = IFeeCollector(FEE_COLLECTOR).getClientAllocations(_client, maxFee);
uint256 totalFee = maxFee - userSavings;
cAmtNet = _amt - totalFee;
Expand Down
61 changes: 59 additions & 2 deletions test/FeeCollector.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ import { Test } from "forge-std/Test.sol";
// Local Imports
import { PositionFactory } from "src/PositionFactory.sol";
import { FeeCollector } from "src/FeeCollector.sol";
import { Assets, CONTRACT_DEPLOYER, TEST_CLIENT, CLIENT_RATE, USDC, WETH, WBTC } from "test/common/Constants.t.sol";
import {
Assets,
CONTRACT_DEPLOYER,
CLIENT_RATE,
PROTOCOL_FEE_RATE,
TEST_CLIENT,
USDC,
WETH,
WBTC
} from "test/common/Constants.t.sol";
import { TokenUtils } from "test/common/utils/TokenUtils.t.sol";
import { FeeUtils } from "test/common/utils/FeeUtils.t.sol";
import { IERC20 } from "src/interfaces/token/IERC20.sol";
Expand Down Expand Up @@ -42,7 +51,7 @@ contract FeeCollectorTest is Test, TokenUtils, FeeUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
feeCollector = new FeeCollector(CONTRACT_DEPLOYER);
feeCollector = new FeeCollector(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE);
feeCollectorAddr = address(feeCollector);

// Deploy PositionFactory
Expand Down Expand Up @@ -241,6 +250,54 @@ contract FeeCollectorTest is Test, TokenUtils, FeeUtils {
feeCollector.setClientRate(_clientRate);
}

/// @dev
// - The current client rate should be updated to new client rate
function testFuzz_SetFeeRate(uint256 _feeRate) external payable {
// Bound fuzzed variables
_feeRate = bound(_feeRate, 0, 1000);

// Pre-act data
uint256 preFeeRate = feeCollector.feeRate();

// Assertions
assertEq(preFeeRate, PROTOCOL_FEE_RATE);

// Act
vm.prank(CONTRACT_DEPLOYER);
feeCollector.setFeeRate(_feeRate);

// Post-act data
uint256 postFeeRate = feeCollector.feeRate();

// Assertions
assertEq(postFeeRate, _feeRate);
}

/// @dev
// - The clientRate in FeeCollector contract cannot be > 1000
function testFuzz_CannotSetFeeRateOutOfRange(uint256 _feeRate) external payable {
// Assumptions
vm.assume(_feeRate > 1000);

// Act
vm.prank(CONTRACT_DEPLOYER);
vm.expectRevert(FeeCollector.OutOfRange.selector);
feeCollector.setFeeRate(_feeRate);
}

/// @dev
// - It should revert with Unauthorized() error when called by an unauthorized sender.
function testFuzz_CannotSetFeeRateUnauthorized(uint256 _feeRate, address _sender) external payable {
// Assumptions
_feeRate = bound(_feeRate, 0, 1000);
vm.assume(_sender != CONTRACT_DEPLOYER);

// Act
vm.prank(_sender);
vm.expectRevert(abi.encodeWithSelector(OwnableUnauthorizedAccount.selector, _sender));
feeCollector.setFeeRate(_feeRate);
}

/// @dev
// - The current client take rate should be updated to new client take rate
function testFuzz_SetClientTakeRate(uint256 _clientTakeRate) public {
Expand Down
3 changes: 2 additions & 1 deletion test/Position.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
CONTRACT_DEPLOYER,
DAI,
FEE_COLLECTOR,
PROTOCOL_FEE_RATE,
TEST_CLIENT,
TEST_LTV,
TEST_POOL_FEE,
Expand Down Expand Up @@ -56,7 +57,7 @@ contract PositionTest is Test, TokenUtils, DebtUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Deploy PositionFactory
vm.prank(CONTRACT_DEPLOYER);
Expand Down
2 changes: 1 addition & 1 deletion test/common/Constants.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ uint256 constant PROFIT_PERCENT = 25;
uint256 constant REPAY_PERCENT = 75;
uint256 constant WITHDRAW_BUFFER = 100_000;
uint256 constant REPAY_BUFFER = 2;
uint256 constant PROTOCOL_FEE_RATE = 3;
uint256 constant CLIENT_RATE = 30;
uint256 constant PROTOCOL_FEE_RATE = 3;
uint256 constant CLIENT_TAKE_RATE = 50;
uint256 constant SUCCESSIVE_ITERATIONS = 3;
uint256 constant TEST_LTV = 50;
Expand Down
2 changes: 1 addition & 1 deletion test/integration/FeeCollector.add.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ contract FeeCollectorAddTest is Test, TokenUtils, FeeUtils, DebtUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Deploy PositionFactory
vm.prank(CONTRACT_DEPLOYER);
Expand Down
2 changes: 1 addition & 1 deletion test/integration/FeeCollector.addLeverage.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ contract FeeCollectorAddLeverageTest is Test, TokenUtils, DebtUtils, FeeUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Deploy PositionFactory
vm.prank(CONTRACT_DEPLOYER);
Expand Down
4 changes: 2 additions & 2 deletions test/integration/FeeCollector.clientFees.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Test } from "forge-std/Test.sol";

// Local Imports
import { FeeCollector } from "src/FeeCollector.sol";
import { Assets, CONTRACT_DEPLOYER } from "test/common/Constants.t.sol";
import { Assets, CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE } from "test/common/Constants.t.sol";
import { TokenUtils } from "test/common/utils/TokenUtils.t.sol";
import { IERC20 } from "src/interfaces/token/IERC20.sol";

Expand All @@ -29,7 +29,7 @@ contract FeeCollectorClientFeesTest is Test, TokenUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
feeCollector = new FeeCollector(CONTRACT_DEPLOYER);
feeCollector = new FeeCollector(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE);
feeCollectorAddr = address(feeCollector);
}

Expand Down
3 changes: 2 additions & 1 deletion test/integration/Position.add.successive.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
CONTRACT_DEPLOYER,
DAI,
FEE_COLLECTOR,
PROTOCOL_FEE_RATE,
SUCCESSIVE_ITERATIONS,
TEST_CLIENT,
TEST_POOL_FEE,
Expand Down Expand Up @@ -94,7 +95,7 @@ contract PositionAddSuccessiveTest is Test, TokenUtils, DebtUtils, FeeUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Set client rate
vm.prank(CONTRACT_DEPLOYER);
Expand Down
2 changes: 1 addition & 1 deletion test/integration/Position.add.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ contract PositionAddTest is Test, TokenUtils, DebtUtils, FeeUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Set client rate
vm.prank(CONTRACT_DEPLOYER);
Expand Down
3 changes: 2 additions & 1 deletion test/integration/Position.addLeverage.successive.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
CONTRACT_DEPLOYER,
DAI,
FEE_COLLECTOR,
PROTOCOL_FEE_RATE,
SUCCESSIVE_ITERATIONS,
TEST_CLIENT,
TEST_POOL_FEE,
Expand Down Expand Up @@ -85,7 +86,7 @@ contract PositionAddLeverageSuccessiveTest is Test, TokenUtils, DebtUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Set client rate
vm.prank(CONTRACT_DEPLOYER);
Expand Down
3 changes: 2 additions & 1 deletion test/integration/Position.addLeverage.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
CONTRACT_DEPLOYER,
DAI,
FEE_COLLECTOR,
PROTOCOL_FEE_RATE,
TEST_CLIENT,
TEST_LTV,
TEST_POOL_FEE,
Expand Down Expand Up @@ -84,7 +85,7 @@ contract PositionAddLeverageTest is Test, TokenUtils, DebtUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Set client rate
vm.prank(CONTRACT_DEPLOYER);
Expand Down
2 changes: 1 addition & 1 deletion test/integration/Position.addWithPermit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ contract PositionAddPermitTest is Test, TokenUtils, DebtUtils, FeeUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Set client rate
vm.prank(CONTRACT_DEPLOYER);
Expand Down
3 changes: 2 additions & 1 deletion test/integration/Position.close.gains.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
DAI,
FEE_COLLECTOR,
PROFIT_PERCENT,
PROTOCOL_FEE_RATE,
SWAP_ROUTER,
TEST_CLIENT,
TEST_LTV,
Expand Down Expand Up @@ -73,7 +74,7 @@ contract PositionCloseGainsTest is Test, TokenUtils, DebtUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Deploy PositionFactory
vm.prank(CONTRACT_DEPLOYER);
Expand Down
3 changes: 2 additions & 1 deletion test/integration/Position.close.losses.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
CONTRACT_DEPLOYER,
DAI,
FEE_COLLECTOR,
PROTOCOL_FEE_RATE,
REPAY_PERCENT,
SWAP_ROUTER,
TEST_CLIENT,
Expand Down Expand Up @@ -78,7 +79,7 @@ contract PositionCloseLossesTest is Test, TokenUtils, DebtUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Deploy PositionFactory
vm.prank(CONTRACT_DEPLOYER);
Expand Down
3 changes: 2 additions & 1 deletion test/integration/Position.close.partial.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
CONTRACT_DEPLOYER,
DAI,
FEE_COLLECTOR,
PROTOCOL_FEE_RATE,
SWAP_ROUTER,
TEST_CLIENT,
TEST_LTV,
Expand Down Expand Up @@ -81,7 +82,7 @@ contract PositionCloseGainsTest is Test, TokenUtils, DebtUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Deploy PositionFactory
vm.prank(CONTRACT_DEPLOYER);
Expand Down
4 changes: 2 additions & 2 deletions test/invariant/FeeCollector.netProtocolFees.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Test } from "forge-std/Test.sol";

// Local Imports
import { FeeCollector } from "src/FeeCollector.sol";
import { Assets, CONTRACT_DEPLOYER } from "test/common/Constants.t.sol";
import { Assets, CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE } from "test/common/Constants.t.sol";
import { TokenUtils } from "test/common/utils/TokenUtils.t.sol";
import { IERC20 } from "src/interfaces/token/IERC20.sol";

Expand All @@ -29,7 +29,7 @@ contract FeeCollectorNetProtocolFeesTest is Test, TokenUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
feeCollector = new FeeCollector(CONTRACT_DEPLOYER);
feeCollector = new FeeCollector(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE);
feeCollectorAddr = address(feeCollector);
}

Expand Down
2 changes: 1 addition & 1 deletion test/libraries/FeeLib.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ contract FeeLibTest is Test, TokenUtils, FeeUtils {

// Deploy FeeCollector
vm.prank(CONTRACT_DEPLOYER);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER), FEE_COLLECTOR);
deployCodeTo("FeeCollector.sol", abi.encode(CONTRACT_DEPLOYER, PROTOCOL_FEE_RATE), FEE_COLLECTOR);

// Set client rate
vm.prank(CONTRACT_DEPLOYER);
Expand Down

0 comments on commit d9cd361

Please sign in to comment.