From efbf474afe31748323bb73d60884be4d2b37ab31 Mon Sep 17 00:00:00 2001 From: Tranquil-Flow <66773372+Tranquil-Flow@users.noreply.github.com> Date: Sat, 9 Mar 2024 08:24:33 +1000 Subject: [PATCH 1/6] Remove Floating Pragmas Will likely be picked up in audits as SWC-103 https://swcregistry.io/docs/SWC-103/ --- src/Position.sol | 2 +- src/PositionFactory.sol | 2 +- src/dependencies/aave/DataTypes.sol | 2 +- src/dependencies/access/Context.sol | 2 +- src/dependencies/access/Ownable.sol | 2 +- src/dependencies/uniswap/TransferHelper.sol | 2 +- src/interfaces/IFeeCollector.sol | 2 +- src/interfaces/IPosition.sol | 2 +- src/interfaces/IPositionFactory.sol | 2 +- src/interfaces/aave/IAaveOracle.sol | 2 +- src/interfaces/aave/IPool.sol | 2 +- src/interfaces/aave/IPoolAddressesProvider.sol | 2 +- src/interfaces/aave/IPriceOracleGetter.sol | 2 +- src/interfaces/token/IERC20.sol | 2 +- src/interfaces/token/IERC20Metadata.sol | 2 +- src/interfaces/token/IERC20Permit.sol | 2 +- src/interfaces/uniswap/ISwapRouter.sol | 2 +- src/interfaces/uniswap/IUniswapV3SwapCallback.sol | 2 +- src/libraries/FeeLib.sol | 2 +- src/services/AdminService.sol | 2 +- src/services/DebtService.sol | 2 +- src/services/SwapService.sol | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Position.sol b/src/Position.sol index d0568659..6d7d0915 100644 --- a/src/Position.sol +++ b/src/Position.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; // Local Imports import { DebtService } from "src/services/DebtService.sol"; diff --git a/src/PositionFactory.sol b/src/PositionFactory.sol index 8a9293db..54a97f0b 100644 --- a/src/PositionFactory.sol +++ b/src/PositionFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; // Local import { Position } from "src/Position.sol"; diff --git a/src/dependencies/aave/DataTypes.sol b/src/dependencies/aave/DataTypes.sol index c49420a6..5897edc3 100644 --- a/src/dependencies/aave/DataTypes.sol +++ b/src/dependencies/aave/DataTypes.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.21; +pragma solidity 0.8.21; library DataTypes { struct ReserveData { diff --git a/src/dependencies/access/Context.sol b/src/dependencies/access/Context.sol index adaf25c2..8bdd745c 100644 --- a/src/dependencies/access/Context.sol +++ b/src/dependencies/access/Context.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) -pragma solidity ^0.8.21; +pragma solidity 0.8.21; /** * @dev Provides information about the current execution context, including the diff --git a/src/dependencies/access/Ownable.sol b/src/dependencies/access/Ownable.sol index 92d3347f..98e9319c 100644 --- a/src/dependencies/access/Ownable.sol +++ b/src/dependencies/access/Ownable.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) -pragma solidity ^0.8.21; +pragma solidity 0.8.21; import { Context } from "src/dependencies/access/Context.sol"; diff --git a/src/dependencies/uniswap/TransferHelper.sol b/src/dependencies/uniswap/TransferHelper.sol index b4202732..45bb6381 100644 --- a/src/dependencies/uniswap/TransferHelper.sol +++ b/src/dependencies/uniswap/TransferHelper.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.21; +pragma solidity 0.8.21; import { IERC20 } from "src/interfaces/token/IERC20.sol"; diff --git a/src/interfaces/IFeeCollector.sol b/src/interfaces/IFeeCollector.sol index e750a9e3..84e14ce7 100644 --- a/src/interfaces/IFeeCollector.sol +++ b/src/interfaces/IFeeCollector.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; interface IFeeCollector { /* solhint-disable func-name-mixedcase */ diff --git a/src/interfaces/IPosition.sol b/src/interfaces/IPosition.sol index da89b873..13739aaa 100644 --- a/src/interfaces/IPosition.sol +++ b/src/interfaces/IPosition.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; interface IPosition { /* solhint-disable func-name-mixedcase */ diff --git a/src/interfaces/IPositionFactory.sol b/src/interfaces/IPositionFactory.sol index 3cbf8bc6..1bb2433f 100644 --- a/src/interfaces/IPositionFactory.sol +++ b/src/interfaces/IPositionFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; interface IPositionFactory { /* solhint-disable func-name-mixedcase */ diff --git a/src/interfaces/aave/IAaveOracle.sol b/src/interfaces/aave/IAaveOracle.sol index 310ac305..651653de 100644 --- a/src/interfaces/aave/IAaveOracle.sol +++ b/src/interfaces/aave/IAaveOracle.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.21; +pragma solidity 0.8.21; import { IPriceOracleGetter } from "./IPriceOracleGetter.sol"; import { IPoolAddressesProvider } from "./IPoolAddressesProvider.sol"; diff --git a/src/interfaces/aave/IPool.sol b/src/interfaces/aave/IPool.sol index 485f2d75..38a33da2 100644 --- a/src/interfaces/aave/IPool.sol +++ b/src/interfaces/aave/IPool.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.21; +pragma solidity 0.8.21; import { IPoolAddressesProvider } from "src/interfaces/aave/IPoolAddressesProvider.sol"; import { DataTypes } from "src/dependencies/aave/DataTypes.sol"; diff --git a/src/interfaces/aave/IPoolAddressesProvider.sol b/src/interfaces/aave/IPoolAddressesProvider.sol index 7b975421..e73b9bc7 100644 --- a/src/interfaces/aave/IPoolAddressesProvider.sol +++ b/src/interfaces/aave/IPoolAddressesProvider.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.21; +pragma solidity 0.8.21; /** * @title IPoolAddressesProvider diff --git a/src/interfaces/aave/IPriceOracleGetter.sol b/src/interfaces/aave/IPriceOracleGetter.sol index 12385a7d..7f602892 100644 --- a/src/interfaces/aave/IPriceOracleGetter.sol +++ b/src/interfaces/aave/IPriceOracleGetter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.21; +pragma solidity 0.8.21; /** * @title IPriceOracleGetter diff --git a/src/interfaces/token/IERC20.sol b/src/interfaces/token/IERC20.sol index 97082008..8ec1bf73 100644 --- a/src/interfaces/token/IERC20.sol +++ b/src/interfaces/token/IERC20.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) -pragma solidity ^0.8.21; +pragma solidity 0.8.21; /** * @dev Interface of the ERC-20 standard as defined in the ERC. diff --git a/src/interfaces/token/IERC20Metadata.sol b/src/interfaces/token/IERC20Metadata.sol index 17574abd..308480a7 100644 --- a/src/interfaces/token/IERC20Metadata.sol +++ b/src/interfaces/token/IERC20Metadata.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; import { IERC20 } from "src/interfaces/token/IERC20.sol"; diff --git a/src/interfaces/token/IERC20Permit.sol b/src/interfaces/token/IERC20Permit.sol index 5d4fb8cb..86add439 100644 --- a/src/interfaces/token/IERC20Permit.sol +++ b/src/interfaces/token/IERC20Permit.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) -pragma solidity ^0.8.21; +pragma solidity 0.8.21; /** * @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in diff --git a/src/interfaces/uniswap/ISwapRouter.sol b/src/interfaces/uniswap/ISwapRouter.sol index 6a35dbcb..e1ab8d85 100644 --- a/src/interfaces/uniswap/ISwapRouter.sol +++ b/src/interfaces/uniswap/ISwapRouter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.21; +pragma solidity 0.8.21; pragma abicoder v2; import { IUniswapV3SwapCallback } from "src/interfaces/uniswap/IUniswapV3SwapCallback.sol"; diff --git a/src/interfaces/uniswap/IUniswapV3SwapCallback.sol b/src/interfaces/uniswap/IUniswapV3SwapCallback.sol index 5041e187..34d2d5fe 100644 --- a/src/interfaces/uniswap/IUniswapV3SwapCallback.sol +++ b/src/interfaces/uniswap/IUniswapV3SwapCallback.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.21; +pragma solidity 0.8.21; /// @title Callback for IUniswapV3PoolActions#swap /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface diff --git a/src/libraries/FeeLib.sol b/src/libraries/FeeLib.sol index b634e912..65f0859d 100644 --- a/src/libraries/FeeLib.sol +++ b/src/libraries/FeeLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; import { IFeeCollector } from "src/interfaces/IFeeCollector.sol"; diff --git a/src/services/AdminService.sol b/src/services/AdminService.sol index ad9a1e12..455f6b55 100644 --- a/src/services/AdminService.sol +++ b/src/services/AdminService.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; // Local Imports import { IERC20 } from "src/interfaces/token/IERC20.sol"; diff --git a/src/services/DebtService.sol b/src/services/DebtService.sol index 3467a232..c5f5017a 100644 --- a/src/services/DebtService.sol +++ b/src/services/DebtService.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; // Local Imports import { AdminService } from "src/services/AdminService.sol"; diff --git a/src/services/SwapService.sol b/src/services/SwapService.sol index 329cd925..dcabf21e 100644 --- a/src/services/SwapService.sol +++ b/src/services/SwapService.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity 0.8.21; pragma abicoder v2; From 61b7fac97c06dbf41bec8d6d7a15fec4bfedc28f Mon Sep 17 00:00:00 2001 From: Tranquil-Flow <66773372+Tranquil-Flow@users.noreply.github.com> Date: Sat, 9 Mar 2024 08:28:15 +1000 Subject: [PATCH 2/6] Remove unnecessary declaration of abicoder v2 As per Solidity 0.8.0, ABI coder v2 is activated by default and as such it does not need to be specified in the code. Disregard this commit if wanting to be explicit in using ABI coder v2 instead of experimental ABIEncoderV2 or abicoder V1. --- src/interfaces/uniswap/ISwapRouter.sol | 1 - src/services/SwapService.sol | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/interfaces/uniswap/ISwapRouter.sol b/src/interfaces/uniswap/ISwapRouter.sol index e1ab8d85..caa51e8e 100644 --- a/src/interfaces/uniswap/ISwapRouter.sol +++ b/src/interfaces/uniswap/ISwapRouter.sol @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.21; -pragma abicoder v2; import { IUniswapV3SwapCallback } from "src/interfaces/uniswap/IUniswapV3SwapCallback.sol"; diff --git a/src/services/SwapService.sol b/src/services/SwapService.sol index dcabf21e..6e2ca2e8 100644 --- a/src/services/SwapService.sol +++ b/src/services/SwapService.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.21; -pragma abicoder v2; - // Local imports import { TransferHelper } from "src/dependencies/uniswap/TransferHelper.sol"; import { ISwapRouter } from "src/interfaces/uniswap/ISwapRouter.sol"; From f79c7b55671c8204d3d03bb5efe0ec65e1f85a29 Mon Sep 17 00:00:00 2001 From: Tranquil-Flow <66773372+Tranquil-Flow@users.noreply.github.com> Date: Sat, 9 Mar 2024 08:34:20 +1000 Subject: [PATCH 3/6] Remove unnecessary fallback() functions In `FeeCollector.sol`, `PositionFactory.sol` and `AdminService.sol`, both a fallback() and receive() function are specified. No code is executed in these contracts upon receiving ether, therefore, fallback() can be omitted as it is redundant. --- src/FeeCollector.sol | 5 ----- src/PositionFactory.sol | 5 ----- src/services/AdminService.sol | 5 ----- 3 files changed, 15 deletions(-) diff --git a/src/FeeCollector.sol b/src/FeeCollector.sol index 27114631..0b26dc7c 100644 --- a/src/FeeCollector.sol +++ b/src/FeeCollector.sol @@ -132,11 +132,6 @@ contract FeeCollector is Ownable { SafeTransferLib.safeTransfer(ERC20(_token), msg.sender, withdrawAmt); } - /** - * @notice Executes when native is sent to this contract through a non-existent function. - */ - fallback() external payable { } // solhint-disable-line no-empty-blocks - /** * @notice Executes when native is sent to this contract with a plain transaction. */ diff --git a/src/PositionFactory.sol b/src/PositionFactory.sol index 54a97f0b..7bdb8cd0 100644 --- a/src/PositionFactory.sol +++ b/src/PositionFactory.sol @@ -81,11 +81,6 @@ contract PositionFactory is Ownable { SafeTransferLib.safeTransfer(ERC20(_token), msg.sender, balance); } - /** - * @notice Executes when native is sent to this contract through a non-existent function. - */ - fallback() external payable { } // solhint-disable-line no-empty-blocks - /** * @notice Executes when native is sent to this contract with a plain transaction. */ diff --git a/src/services/AdminService.sol b/src/services/AdminService.sol index 455f6b55..d9418699 100644 --- a/src/services/AdminService.sol +++ b/src/services/AdminService.sol @@ -36,11 +36,6 @@ contract AdminService { SafeTransferLib.safeTransfer(ERC20(_token), msg.sender, balance); } - /** - * @notice Executes when native is sent to this contract through a non-existent function. - */ - fallback() external payable { } // solhint-disable-line no-empty-blocks - /** * @notice Executes when native is sent to this contract with a plain transaction. */ From 9d2a56b34c3232e01540308d659fd3ab13df6e82 Mon Sep 17 00:00:00 2001 From: Adam Cuculich <46691282+cucupac@users.noreply.github.com> Date: Sun, 17 Mar 2024 19:28:22 -0500 Subject: [PATCH 4/6] docs: natspec compliance (#23) --- .husky/pre-commit | 2 +- README.md | 6 +- src/FeeCollector.sol | 66 +++------- src/Position.sol | 79 +++++------- src/PositionFactory.sol | 43 +++---- src/interfaces/IAdminService.sol | 22 ++++ src/interfaces/IDebtService.sol | 56 +++++++++ src/interfaces/IFeeCollector.sol | 103 ++++++---------- src/interfaces/IPosition.sol | 184 ++++++---------------------- src/interfaces/IPositionFactory.sol | 50 +++----- src/libraries/FeeLib.sol | 15 ++- src/services/AdminService.sol | 25 ++-- src/services/DebtService.sol | 123 +++++++------------ src/services/SwapService.sol | 40 +++--- test/FeeCollector.t.sol | 23 ---- test/PositionFactory.t.sol | 23 ---- test/common/utils/DebtUtils.t.sol | 4 +- test/services/AdminService.t.sol | 23 ---- 18 files changed, 329 insertions(+), 558 deletions(-) create mode 100644 src/interfaces/IAdminService.sol create mode 100644 src/interfaces/IDebtService.sol diff --git a/.husky/pre-commit b/.husky/pre-commit index 45d8278a..dc4f59d9 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -npm run format && npm run lint:check && npm run test +npm run format && npm run lint:check && forge test --fork-url "https://lb.drpc.org/ogrpc?network=arbitrum&dkey=AndBdVMW9kqLhpV95Kl_zsdLWrYu3PsR7qO5GtyyLTIM" diff --git a/README.md b/README.md index 4173280a..be8af7b6 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,13 @@ The following outlines principles for **core** protocol funcitonality. 1. Immutable. 2. No Governance on the core protocol. -3. No Admin Keys. +3. No Admin Keys on the core protocol. ## To-Do Logic: -- [ ] Add gas optimizations where possible. +- None at the moment🙂 Tests: @@ -26,7 +26,7 @@ Tests: Considerations: -- None at the moment🙂 +- [ ] Consider moving the protocol fee rate to the fee collector contract. Cleanup: diff --git a/src/FeeCollector.sol b/src/FeeCollector.sol index 27114631..446520ee 100644 --- a/src/FeeCollector.sol +++ b/src/FeeCollector.sol @@ -4,35 +4,40 @@ pragma solidity ^0.8.21; // Local Imports import { Ownable } from "src/dependencies/access/Ownable.sol"; import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; +import { IFeeCollector } from "src/interfaces/IFeeCollector.sol"; import { IERC20 } from "src/interfaces/token/IERC20.sol"; -/// @title FeeCollector +/// @title The fee collector contract /// @author Chain Rule, LLC -/// @notice Collects protocol fees -contract FeeCollector is Ownable { +/// @notice Collects all protocol fees +contract FeeCollector is Ownable, IFeeCollector { // Constants: no SLOAD to save gas address private constant CONTRACT_DEPLOYER = 0x0a5B347509621337cDDf44CBCf6B6E7C9C908CD2; // Storage + /// @inheritdoc IFeeCollector uint256 public clientRate; + + /// @inheritdoc IFeeCollector mapping(address => uint256) public clientTakeRates; + + /// @inheritdoc IFeeCollector mapping(address => uint256) public totalClientBalances; + + /// @inheritdoc IFeeCollector mapping(address => mapping(address => uint256)) public balances; // 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) { if (msg.sender != CONTRACT_DEPLOYER) revert Unauthorized(); } - /** - * @notice Collects fees from Position contracts when collateral is added. - * @param _client The address where a client operator will receive protocols fees. - * @param _token The token to collect fees in (the collateral token of the calling Position contract). - * @param _amt The total amount of fees to collect. - */ + /// @inheritdoc IFeeCollector function collectFees(address _client, address _token, uint256 _amt, uint256 _clientFee) external payable { // 1. Transfer tokens to this contract SafeTransferLib.safeTransferFrom(ERC20(_token), msg.sender, address(this), _amt); @@ -47,10 +52,7 @@ contract FeeCollector is Ownable { } } - /** - * @notice Withdraw collected fees from this contract. - * @param _token The token address to withdraw. - */ + /// @inheritdoc IFeeCollector function clientWithdraw(address _token) public payable { uint256 withdrawAmt = balances[msg.sender][_token]; @@ -62,28 +64,13 @@ contract FeeCollector is Ownable { SafeTransferLib.safeTransfer(ERC20(_token), msg.sender, withdrawAmt); } - /** - * @notice Allows clients to set the percentage of the clientRate they will receive each revenue-generating tx. - * Amounts less than 100 will give the calling client's users a protocol fee discount: - * clientPercentOfProtocolFee = clientRate * _clientTakeRate - * userPercentOfProtocolFee = clientRate * (1 - _clientTakeRate) - * clientFee = protocolFee * clientPercentOfProtocolFee - * userSavings = protocolFee * userPercentOfProtocolFee - * @param _clientTakeRate The percentage of the clientRate the client will receive each revenue-generating tx (100 = 100%). - */ + /// @inheritdoc IFeeCollector function setClientTakeRate(uint256 _clientTakeRate) public payable { if (_clientTakeRate > 100) revert OutOfRange(); clientTakeRates[msg.sender] = _clientTakeRate; } - /** - * @notice Returns the amount discounted from the protocol fee for using the provided client, - * and the amount of fees the client will receive. - * @param _client The address where a client operator will receive protocols fees. - * @param _maxFee The maximum amount of fees the protocol will collect. - * @return userSavings The amount of fees discounted from the protocol fee. - * @return clientFee The amount of fees the client will receive. - */ + /// @inheritdoc IFeeCollector function getClientAllocations(address _client, uint256 _maxFee) public view @@ -105,27 +92,19 @@ contract FeeCollector is Ownable { ** ******************************************************************************/ - /** - * @notice Allows owner to set client rate. - * @param _clientRate The percentage of total transaction-specific protocol fee, allocated to the utilized client. - */ + /// @inheritdoc IFeeCollector function setClientRate(uint256 _clientRate) public payable onlyOwner { if (_clientRate < 30 || _clientRate > 100) revert OutOfRange(); clientRate = _clientRate; } - /** - * @notice Allows OWNER to withdraw all of this contract's native token balance. - */ + /// @inheritdoc IFeeCollector function extractNative() public payable onlyOwner { payable(msg.sender).transfer(address(this).balance); } - /** - * @notice Allows owner to withdraw protocol fees from this contract. - * @param _token The address of token to remove. - */ + /// @inheritdoc IFeeCollector function extractERC20(address _token) public payable onlyOwner { uint256 withdrawAmt = IERC20(_token).balanceOf(address(this)) - totalClientBalances[_token]; @@ -136,9 +115,4 @@ contract FeeCollector is Ownable { * @notice Executes when native is sent to this contract through a non-existent function. */ fallback() external payable { } // solhint-disable-line no-empty-blocks - - /** - * @notice Executes when native is sent to this contract with a plain transaction. - */ - receive() external payable { } // solhint-disable-line no-empty-blocks } diff --git a/src/Position.sol b/src/Position.sol index 1c34d09c..4cc213cc 100644 --- a/src/Position.sol +++ b/src/Position.sol @@ -6,23 +6,44 @@ import { DebtService } from "src/services/DebtService.sol"; import { SwapService } from "src/services/SwapService.sol"; import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; import { FeeLib } from "src/libraries/FeeLib.sol"; +import { IPosition } from "src/interfaces/IPosition.sol"; import { IERC20 } from "src/interfaces/token/IERC20.sol"; import { IERC20Permit } from "src/interfaces/token/IERC20Permit.sol"; import { IERC20Metadata } from "src/interfaces/token/IERC20Metadata.sol"; -/// @title Position +/// @title The position contract /// @author Chain Rule, LLC -/// @notice Manages the owner's individual position -contract Position is DebtService, SwapService { +/// @notice Allows an owner account to manage its individual position +contract Position is DebtService, SwapService, IPosition { // Immutables: no SLOAD to save gas + + /// @inheritdoc IPosition uint8 public immutable B_DECIMALS; + + /// @inheritdoc IPosition address public immutable B_TOKEN; // Events + /// @notice An event emitted when a position is created or added to. + /// @param cAmt The amount of collateral token supplied (units: C_DECIMALS). + /// @param dAmt The amount of debt token borrowed (units: D_DECIMALS). + /// @param bAmt The amount of base token received and subsequently supplied as collateral (units: B_DECIMALS). event Add(uint256 cAmt, uint256 dAmt, uint256 bAmt); + + /// @notice An event emitted when leverage is added to a position. + /// @param dAmt The amount of debt token borrowed (units: D_DECIMALS). + /// @param bAmt The amount of base token received and subsequently supplied as collateral (units: B_DECIMALS). event AddLeverage(uint256 dAmt, uint256 bAmt); + + /// @notice An event emitted when a position is closed. + /// @param gains The amount of base token gained from the position (units: B_DECIMALS). event Close(uint256 gains); + /// @notice This function is called when a Position contract is deployed. + /// @param _owner The account address of the Position contract's owner. + /// @param _cToken The address of the token to be used as collateral. + /// @param _dToken The address of the token to be borrowed. + /// @param _bToken The address of the token to swap _dToken for. constructor(address _owner, address _cToken, address _dToken, address _bToken) DebtService(_owner, _cToken, _dToken) { @@ -30,14 +51,7 @@ contract Position is DebtService, SwapService { B_DECIMALS = IERC20Metadata(_bToken).decimals(); } - /** - * @notice Adds to this contract's position. - * @param _cAmt The amount of collateral token to be supplied for this transaction-specific loan (units: C_DECIMALS). - * @param _ltv The desired loan-to-value ratio for this transaction-specific loan (ex: 75 is 75%). - * @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through. - * @param _poolFee The fee of the Uniswap pool. - * @param _client The address of the client operator. Use address(0) if not using a client. - */ + /// @inheritdoc IPosition function add(uint256 _cAmt, uint256 _ltv, uint256 _swapAmtOutMin, uint24 _poolFee, address _client) public payable @@ -61,19 +75,7 @@ contract Position is DebtService, SwapService { emit Add(cAmtNet, dAmt, bAmt); } - /** - * @notice Adds to this contract's position with permit, obviating the need for a separate approve tx. - * This function can only be used for ERC-2612-compliant tokens. - * @param _cAmt The amount of collateral token to be supplied for this transaction-specific loan (units: C_DECIMALS). - * @param _ltv The desired loan-to-value ratio for this transaction-specific loan (ex: 75 is 75%). - * @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through. - * @param _poolFee The fee of the Uniswap pool. - * @param _client The address of the client operator. Use address(0) if not using a client. - * @param _deadline The expiration timestamp of the permit. - * @param _v The V parameter of ERC712 signature for the permit. - * @param _r The R parameter of ERC712 signature for the permit. - * @param _s The S parameter of ERC712 signature for the permit. - */ + /// @inheritdoc IPosition function addWithPermit( uint256 _cAmt, uint256 _ltv, @@ -85,21 +87,11 @@ contract Position is DebtService, SwapService { bytes32 _r, bytes32 _s ) public payable onlyOwner { - // 1. Approve with permit IERC20Permit(C_TOKEN).permit(msg.sender, address(this), _cAmt, _deadline, _v, _r, _s); - - // 2. Short add(_cAmt, _ltv, _swapAmtOutMin, _poolFee, _client); } - /** - * @notice Adds leverage to this contract's position. This function can only be used for positions where the - * collateral token is the same as the base token. - * @param _dAmt The amount of D_TOKEN to borrow; use position LTV to identify max amount. - * @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through. - * @param _poolFee The fee of the Uniswap pool. - * @param _client The address of the client operator. Use address(0) if not using a client. - */ + /// @inheritdoc IPosition function addLeverage(uint256 _dAmt, uint256 _swapAmtOutMin, uint24 _poolFee, address _client) public payable @@ -120,14 +112,7 @@ contract Position is DebtService, SwapService { emit AddLeverage(dAmtNet, bAmt); } - /** - * @notice Fully closes the position. - * @param _poolFee The fee of the Uniswap pool. - * @param _exactOutput Whether to swap exact output or exact input (true for exact output, false for exact input). - * @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through (only used if _exactOutput is false, supply 0 if true). - * @param _withdrawCAmt The amount of C_TOKEN to withdraw (units: C_DECIMALS). - * @param _withdrawBAmt The amount of B_TOKEN to withdraw (units: B_DECIMALS). - */ + /// @inheritdoc IPosition function close( uint24 _poolFee, bool _exactOutput, @@ -155,8 +140,12 @@ contract Position is DebtService, SwapService { withdraw(C_TOKEN, _withdrawCAmt, OWNER); } - // 5. pay gains if any: NOTE: can probably be unchecked as bAmtIn will never be greater than _withdrawBAmt - uint256 gains = _withdrawBAmt - bAmtIn; + // 5. pay gains if any + uint256 gains; + unchecked { + gains = _withdrawBAmt - bAmtIn; // unchecked because bAmtIn will never be greater than _withdrawBAmt + } + if (gains != 0) { SafeTransferLib.safeTransfer(ERC20(B_TOKEN), OWNER, gains); } diff --git a/src/PositionFactory.sol b/src/PositionFactory.sol index 8a9293db..c5e4cf9b 100644 --- a/src/PositionFactory.sol +++ b/src/PositionFactory.sol @@ -5,18 +5,21 @@ pragma solidity ^0.8.21; import { Position } from "src/Position.sol"; import { Ownable } from "src/dependencies/access/Ownable.sol"; import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; +import { IPositionFactory } from "src/interfaces/IPositionFactory.sol"; import { IERC20 } from "src/interfaces/token/IERC20.sol"; -/// @title Position Factory +/// @title The position factory contract /// @author Chain Rule, LLC -/// @notice Creates user position contracts and stores their addresses -contract PositionFactory is Ownable { +/// @notice Creates user position contracts and stores their addresses for all users +contract PositionFactory is Ownable, IPositionFactory { // Constants: no SLOAD to save gas address private constant CONTRACT_DEPLOYER = 0x0a5B347509621337cDDf44CBCf6B6E7C9C908CD2; // Factory Storage - /// @dev Mapping from owner to cToken to dToken to bToken to position + /// @inheritdoc IPositionFactory mapping(address => mapping(address => mapping(address => mapping(address => address)))) public positions; + + /// @notice A mapping of all positions owned by a user. mapping(address => address[]) public positionsLookup; // Errors @@ -24,18 +27,18 @@ contract PositionFactory is Ownable { error PositionExists(); // Events + /// @notice An event emitted when a position contract is created. + /// @param owner The owner of the created Position contract. + /// @param position The address of the created Position contract. event PositionCreated(address indexed owner, address indexed position); + /// @notice This function is called when the PositionFactory is deployed. + /// @param _owner The account address of the PositionFactory contract's owner. constructor(address _owner) Ownable(_owner) { if (msg.sender != CONTRACT_DEPLOYER) revert Unauthorized(); } - /** - * @notice Deploys a Position contract for msg.sender, given a _cToken, _dToken, and _bToken. - * @param _cToken The address of the token to be used as collateral. - * @param _dToken The address of the token to be borrowed. - * @param _bToken The address of the token to swap _dToken for. - */ + /// @inheritdoc IPositionFactory function createPosition(address _cToken, address _dToken, address _bToken) public payable @@ -51,10 +54,7 @@ contract PositionFactory is Ownable { emit PositionCreated(msg.sender, position); } - /** - * @notice Returns a list of contract addresses for the given _positionOwner. - * @param _positionOwner The owner of the Position contracts. - */ + /// @inheritdoc IPositionFactory function getPositions(address _positionOwner) public view returns (address[] memory) { return positionsLookup[_positionOwner]; } @@ -64,17 +64,13 @@ contract PositionFactory is Ownable { ** ADMIN FUNCTIONS ** ******************************************************************************/ - /** - * @notice Allows OWNER to withdraw all of this contract's native token balance. - */ + + /// @inheritdoc IPositionFactory function extractNative() public payable onlyOwner { payable(msg.sender).transfer(address(this).balance); } - /** - * @notice Allows OWNER to withdraw all of a specified ERC20 token's balance from this contract. - * @param _token The address of token to remove. - */ + /// @inheritdoc IPositionFactory function extractERC20(address _token) public payable onlyOwner { uint256 balance = IERC20(_token).balanceOf(address(this)); @@ -85,9 +81,4 @@ contract PositionFactory is Ownable { * @notice Executes when native is sent to this contract through a non-existent function. */ fallback() external payable { } // solhint-disable-line no-empty-blocks - - /** - * @notice Executes when native is sent to this contract with a plain transaction. - */ - receive() external payable { } // solhint-disable-line no-empty-blocks } diff --git a/src/interfaces/IAdminService.sol b/src/interfaces/IAdminService.sol new file mode 100644 index 00000000..65973d16 --- /dev/null +++ b/src/interfaces/IAdminService.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +interface IAdminService { + /* solhint-disable func-name-mixedcase */ + + /// @notice Returns the owner of this contract. + function OWNER() external view returns (address); + + /* **************************************************************************** + ** + ** ADMIN FUNCTIONS + ** + ******************************************************************************/ + + /// @notice Allows owner to withdraw all of this contract's native token balance. + function extractNative() external payable; + + /// @notice Allows owner to withdraw all of a specified ERC20 token's balance from this contract. + /// @param _token The address of token to remove. + function extractERC20(address _token) external payable; +} diff --git a/src/interfaces/IDebtService.sol b/src/interfaces/IDebtService.sol new file mode 100644 index 00000000..7afe6295 --- /dev/null +++ b/src/interfaces/IDebtService.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import { IAdminService } from "src/interfaces/IAdminService.sol"; + +interface IDebtService is IAdminService { + /* solhint-disable func-name-mixedcase */ + + /// @notice Returns the number of decimals this position's collateral token is denominated in. + function C_DECIMALS() external view returns (uint8); + + /// @notice Returns the number of decimals this position's debt token is denominated in. + function D_DECIMALS() external view returns (uint8); + + /// @notice Returns the address of this position's collateral token. + function C_TOKEN() external view returns (address); + + /// @notice Returns the address of this position's debt token. + function D_TOKEN() external view returns (address); + + /// @notice Increases the collateral amount backing this contract's loan. + /// @param _cAmt The amount of collateral to be supplied (units: C_DECIMALS). + function addCollateral(uint256 _cAmt) external payable; + + /// @notice Increases the collateral amount for this contract's loan with permit (no separate approve tx). + /// @dev This function can only be used for ERC-2612-compliant tokens. + /// @param _cAmt The amount of collateral to be supplied (units: C_DECIMALS). + /// @param _deadline The expiration timestamp of the permit. + /// @param _v The V parameter of ERC712 signature for the permit. + /// @param _r The R parameter of ERC712 signature for the permit. + /// @param _s The S parameter of ERC712 signature for the permit. + function addCollateralWithPermit(uint256 _cAmt, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) + external + payable; + + /// @notice Withdraws collateral from Aave to the specified recipient. + /// @param _token The address of the collateral token to be withdrawn (C_TOKEN or B_TOKEN). + /// @param _amt The amount of collateral to be withdrawn (units: C_DECIMALS or B_DECIMALS). + /// @param _recipient The recipient of the funds. + function withdraw(address _token, uint256 _amt, address _recipient) external payable; + + /// @notice Repays outstanding debt to Aave. + /// @dev To pay off entire debt, _dAmt = debtOwed + smallBuffer (to account for interest). + /// @param _dAmt The amount of debt token to repay to Aave (units: D_DECIMALS). + function repay(uint256 _dAmt) external payable; + + /// @notice Repays outstanding debt to Aave with permit (no separate approve tx). + /// @dev This function can only be used for ERC-2612-compliant tokens. + /// @dev To pay off entire debt, _dAmt = debtOwed + smallBuffer (to account for interest). + /// @param _dAmt The amount of debt token to repay to Aave (units: D_DECIMALS). + /// @param _deadline The expiration timestamp of the permit. + /// @param _v The V parameter of ERC712 signature for the permit. + /// @param _r The R parameter of ERC712 signature for the permit. + /// @param _s The S parameter of ERC712 signature for the permit. + function repayWithPermit(uint256 _dAmt, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external payable; +} diff --git a/src/interfaces/IFeeCollector.sol b/src/interfaces/IFeeCollector.sol index e750a9e3..8cbc2a25 100644 --- a/src/interfaces/IFeeCollector.sol +++ b/src/interfaces/IFeeCollector.sol @@ -8,70 +8,49 @@ interface IFeeCollector { ** CORE FUNCTIONS ** ******************************************************************************/ - /** - * @notice Returns the owner of this contract. - */ - function owner() external view returns (address); - /** - * @notice Returns the current client rate. - */ + /// @notice The maximum percentage of protocol fees allocated to clients. function clientRate() external view returns (uint256); - /** - * @notice Returns the total balance for the specified token across all client operators. - * @param _token The token address to check. - * @return balance The total balance for the specified token across all client operators. - */ + /// @notice Returns the take rate of the specified client operator (% of client rate that the operator keeps). + /// @dev Example: clientTakeRates[client] = clientTakeRate + /// @param _client A client operator address. + /// @return clientTakeRate The percentage of the client rate that the operator keeps. + function clientTakeRates(address _client) external view returns (uint256); + + /// @notice Returns the total balance for the specified token across all client operators. + /// @dev Example: totalClientBalances[token] = totalClientBalance + /// @param _token The token address to check. + /// @return balance The total balance for the specified token across all client operators. function totalClientBalances(address _token) external view returns (uint256); - /** - * @notice Returns the balance for the specified token for the specified client operator. - * @param _client A client operator address. - * @param _token The token address to check. - * @return balance The balance for the specified token for the specified client operator. - */ + /// @notice Returns the balance for the specified token for the specified client operator. + /// @dev Example: balances[client][token] = clientBalance + /// @param _client A client operator address. + /// @param _token The token address to check. + /// @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 take rate of the specified client operator; the percentage of the client rate that the operator keeps. - * @param _client A client operator address. - * @return clientTakeRate The percentage of the client rate that the operator keeps. - */ - function clientTakeRates(address _client) external view returns (uint256); - - /** - * @notice Collects fees from Position contracts when collateral is added. - * @param _client The address where a client operator will receive protocols fees. - * @param _token The token to collect fees in (the collateral token of the calling Position contract). - * @param _amt The total amount of fees to collect. - */ + /// @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). + /// @param _amt The total amount of fees to collect. function collectFees(address _client, address _token, uint256 _amt, uint256 _clientFee) external payable; - /** - * @notice Withdraw collected fees from this contract. - * @param _token The token address to withdraw. - */ + + /// @notice Withdraw collected fees from this contract. + /// @param _token The token address to withdraw. function clientWithdraw(address _token) external payable; - /** - * @notice Allows clients to set the percentage of the clientRate they will receive each revenue-generating tx. - * Amounts less than 100 will give the calling client's users a protocol fee discount: - * clientPercentOfProtocolFee = clientRate * _clientTakeRate - * userPercentOfProtocolFee = clientRate * (1 - _clientTakeRate) - * clientFee = protocolFee * clientPercentOfProtocolFee - * userSavings = protocolFee * userPercentOfProtocolFee - * @param _clientTakeRate The percentage of the clientRate the client will receive each revenue-generating tx (100 = 100%). - */ + /// @notice Allows clients to set the percentage of clientRate they receive each revenue-generating tx. + /// @dev Amounts less than 100 will give the calling client's users a protocol fee discount. + /// @param _clientTakeRate The percentage of clientRate the client receives (100 = 100%). function setClientTakeRate(uint256 _clientTakeRate) external payable; - /** - * @notice Returns the amount discounted from the protocol fee for using the provided client, - * and the amount of fees the client will receive. - * @param _client The address where a client operator will receive protocols fees. - * @param _maxFee The maximum amount of fees the protocol will collect. - * @return userSavings The amount of fees discounted from the protocol fee. - * @return clientFee The amount of fees the client will receive. - */ + /// @notice Returns discount amount and client fees when using the provided client. + /// @param _client The address where a client operator will receive protocols fees. + /// @param _maxFee The maximum amount of fees the protocol will collect. + /// @return userSavings The amount of fees discounted from the protocol fee. + /// @return clientFee The amount of fees the client will receive. function getClientAllocations(address _client, uint256 _maxFee) external view @@ -82,20 +61,18 @@ interface IFeeCollector { ** ADMIN FUNCTIONS ** ******************************************************************************/ - /** - * @notice Allows owner to set client rate. - * @param _clientRate The percentage of total transaction-specific protocol fee, allocated to the utilized client. - */ + + /// @notice Allows owner to set client rate. + /// @dev This function is only callable by the owner account. + /// @param _clientRate The percentage of total transaction-specific protocol fee, allocated to the utilized client. function setClientRate(uint256 _clientRate) external payable; - /** - * @notice Allows owner to withdraw all of this contract's native token balance. - */ + /// @notice Allows owner to withdraw all of this contract's native token balance. + /// @dev This function is only callable by the owner account. function extractNative() external payable; - /** - * @notice Allows owner to withdraw protocol fees from this contract. - * @param _token The address of token to remove. - */ + /// @notice Allows owner to withdraw protocol fees from this contract. + /// @dev This function is only callable by the owner account. + /// @param _token The address of token to remove. function extractERC20(address _token) external payable; } diff --git a/src/interfaces/IPosition.sol b/src/interfaces/IPosition.sol index b3adb8c5..543d8b1a 100644 --- a/src/interfaces/IPosition.sol +++ b/src/interfaces/IPosition.sol @@ -1,53 +1,20 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.21; -interface IPosition { +import { IDebtService } from "src/interfaces/IDebtService.sol"; + +interface IPosition is IDebtService { /* solhint-disable func-name-mixedcase */ /* **************************************************************************** ** ** METADATA ** ******************************************************************************/ - /** - * @notice Returns the owner of this contract. - */ - function OWNER() external view returns (address); - /** - * @notice Returns the address of this position's collateral token. - */ - function C_TOKEN() external view returns (address); - /** - * @notice Returns the address of this position's debt token. - */ - function D_TOKEN() external view returns (address); - /** - * @notice Returns the address of this position's base token (the token that the debt token is swapped for when shorting). - */ - function B_TOKEN() external view returns (address); - - /** - * @notice Returns the address of the FeeCollector contract, which is responsible for collecting and allocating protocol fees. - */ - function FEE_COLLECTOR() external view returns (address); - - /** - * @notice Returns the maximum percentage of the collateral token that the protocol charges each revenue-generating transaction. - */ - function PROTOCOL_FEE_RATE() external view returns (uint256); - /** - * @notice Returns the number of decimals for this position's collateral token. - */ - function C_DECIMALS() external view returns (uint8); - - /** - * @notice Returns the number of decimals for this position's debt token. - */ - function D_DECIMALS() external view returns (uint8); + /// @notice Returns the address of base token (the token that D_TOKEN is swapped for). + function B_TOKEN() external view returns (address); - /** - * @notice Returns the number of decimals for this position's base token. - */ + /// @notice Returns the number of decimals this position's base token is denominated in. function B_DECIMALS() external view returns (uint8); /* **************************************************************************** @@ -55,31 +22,28 @@ interface IPosition { ** CORE FUNCTIONS ** ******************************************************************************/ - /** - * @notice Adds to this contract's position. - * @param _cAmt The amount of collateral token to be supplied for this transaction-specific loan (units: C_DECIMALS). - * @param _ltv The desired loan-to-value ratio for this transaction-specific loan (ex: 75 is 75%). - * @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through. - * @param _poolFee The fee of the Uniswap pool. - * @param _client The address of the client operator. Use address(0) if not using a client. - */ + + /// @notice Adds to this contract's position. + /// @param _cAmt The amount of collateral token to be supplied for this transaction-specific loan (units: C_DECIMALS). + /// @param _ltv The desired loan-to-value ratio for this transaction-specific loan (ex: 75 is 75%). + /// @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through. + /// @param _poolFee The fee of the Uniswap pool (3000 = 0.30%). + /// @param _client The address of the client operator. Use address(0) if not using a client. function add(uint256 _cAmt, uint256 _ltv, uint256 _swapAmtOutMin, uint24 _poolFee, address _client) external payable; - /** - * @notice Adds to this contract's position with permit, obviating the need for a separate approve tx. - * This function can only be used for ERC-2612-compliant tokens. - * @param _cAmt The amount of collateral token to be supplied for this transaction-specific loan (units: C_DECIMALS). - * @param _ltv The desired loan-to-value ratio for this transaction-specific loan (ex: 75 is 75%). - * @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through. - * @param _poolFee The fee of the Uniswap pool. - * @param _client The address of the client operator. Use address(0) if not using a client. - * @param _deadline The expiration timestamp of the permit. - * @param _v The V parameter of ERC712 signature for the permit. - * @param _r The R parameter of ERC712 signature for the permit. - * @param _s The S parameter of ERC712 signature for the permit. - */ + /// @notice Adds to this contract's position with permit (no separate approve tx). + /// @dev This function can only be used for ERC-2612-compliant tokens. + /// @param _cAmt The amount of collateral token to be supplied for this transaction-specific loan (units: C_DECIMALS). + /// @param _ltv The desired loan-to-value ratio for this transaction-specific loan (ex: 75 is 75%). + /// @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through. + /// @param _poolFee The fee of the Uniswap pool (3000 = 0.30%). + /// @param _client The address of the client operator. Use address(0) if not using a client. + /// @param _deadline The expiration timestamp of the permit. + /// @param _v The V parameter of ERC712 signature for the permit. + /// @param _r The R parameter of ERC712 signature for the permit. + /// @param _s The S parameter of ERC712 signature for the permit. function addWithPermit( uint256 _cAmt, uint256 _ltv, @@ -92,24 +56,19 @@ interface IPosition { bytes32 _s ) external payable; - /** - * @notice Adds leverage to this contract's position. This function can only be used for positions where the - * collateral token is the same as the base token. - * @param _ltv The desired loan-to-value ratio for this transaction-specific loan (ex: 75 is 75%). - * @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through. - * @param _poolFee The fee of the Uniswap pool. - * @param _client The address of the client operator. Use address(0) if not using a client. - */ - function addLeverage(uint256 _ltv, uint256 _swapAmtOutMin, uint24 _poolFee, address _client) external payable; - - /** - * @notice Fully closes the position. - * @param _poolFee The fee of the Uniswap pool. - * @param _exactOutput Whether to swap exact output or exact input (true for exact output, false for exact input). - * @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through (only used if _exactOutput is false, supply 0 if true). - * @param _withdrawCAmt The amount of C_TOKEN to withdraw (units: C_DECIMALS). - * @param _withdrawBAmt The amount of B_TOKEN to withdraw (units: B_DECIMALS). - */ + /// @notice Adds leverage to this contract's position. + /// @param _dAmt The amount of D_TOKEN to borrow; use position LTV to identify max amount. + /// @param _swapAmtOutMin The minimum amount of output tokens from swap for the tx to go through. + /// @param _poolFee The fee of the Uniswap pool (3000 = 0.30%). + /// @param _client The address of the client operator. Use address(0) if not using a client. + function addLeverage(uint256 _dAmt, uint256 _swapAmtOutMin, uint24 _poolFee, address _client) external payable; + + /// @notice Reduces a position based on the amount of B_TOKEN and C_TOKEN withdrawn. + /// @param _poolFee The fee of the Uniswap pool (3000 = 0.30%). + /// @param _exactOutput Whether to swap exact output or exact input (true for exact output, false for exact input). + /// @param _swapAmtOutMin The min amount of output tokens from swap (supply 0 if _exactOutput = true). + /// @param _withdrawCAmt The amount of C_TOKEN to withdraw (units: C_DECIMALS). + /// @param _withdrawBAmt The amount of B_TOKEN to withdraw (units: B_DECIMALS). function close( uint24 _poolFee, bool _exactOutput, @@ -117,73 +76,4 @@ interface IPosition { uint256 _withdrawCAmt, uint256 _withdrawBAmt ) external payable; - - /** - * @notice Increases the collateral amount backing this contract's loan. - * @param _cAmt The amount of collateral to be supplied (units: C_DECIMALS). - */ - function addCollateral(uint256 _cAmt) external payable; - - /** - * @notice Increases the collateral amount for this contract's loan with permit, - * obviating the need for a separate approve tx. This function can only be used for ERC-2612-compliant tokens. - * @param _cAmt The amount of collateral to be supplied (units: C_DECIMALS). - * @param _deadline The expiration timestamp of the permit. - * @param _v The V parameter of ERC712 signature for the permit. - * @param _r The R parameter of ERC712 signature for the permit. - * @param _s The S parameter of ERC712 signature for the permit. - */ - function addCollateralWithPermit(uint256 _cAmt, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) - external - payable; - - /** - * @notice Withdraws collateral token from Aave to specified recipient. - * @param _token The address of the collateral token to be withdrawn (C_TOKEN or B_TOKEN). - * @param _amt The amount of collateral to be withdrawn (units: C_DECIMALS or B_DECIMALS). - * @param _recipient The recipient of the funds. - */ - function withdraw(address _token, uint256 _amt, address _recipient) external payable; - - /** - * @notice Repays any outstanding debt to Aave and transfers remaining collateral from Aave to owner. - * @param _dAmt The amount of debt token to repay to Aave (units: D_DECIMALS). - * To pay off entire debt, _dAmt = debtOwed + smallBuffer (to account for interest). - */ - function repay(uint256 _dAmt) external payable; - - /** - * @notice Repays any outstanding debt to Aave and transfers remaining collateral from Aave to owner, - * with permit, obviating the need for a separate approve tx. This function can only be used for ERC-2612-compliant tokens. - * @param _dAmt The amount of debt token to repay to Aave (units: D_DECIMALS). - * To pay off entire debt, _dAmt = debtOwed + smallBuffer (to account for interest). - * @param _deadline The expiration timestamp of the permit. - * @param _v The V parameter of ERC712 signature for the permit. - * @param _r The R parameter of ERC712 signature for the permit. - * @param _s The S parameter of ERC712 signature for the permit. - */ - function repayWithPermit( - uint256 _dAmt, - uint256 _withdrawBuffer, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s - ) external payable; - - /* **************************************************************************** - ** - ** ADMIN FUNCTIONS - ** - ******************************************************************************/ - /** - * @notice Allows owner to withdraw all of this contract's native token balance. - */ - function extractNative() external payable; - - /** - * @notice Allows owner to withdraw all of a specified ERC20 token's balance from this contract. - * @param _token The address of token to remove. - */ - function extractERC20(address _token) external payable; } diff --git a/src/interfaces/IPositionFactory.sol b/src/interfaces/IPositionFactory.sol index 3cbf8bc6..72c087d6 100644 --- a/src/interfaces/IPositionFactory.sol +++ b/src/interfaces/IPositionFactory.sol @@ -9,46 +9,33 @@ interface IPositionFactory { ** ******************************************************************************/ - /** - * @notice Returns the owner of this contract. - */ - function owner() external view returns (address); - - /** - * @notice Returns the address of an owner's specified Position contract. - */ + /// @notice The address of the Position contract for the given owner and token permutation. + /// @dev Example: positions[owner][cToken][dToken][bToken] = position + /// @return position The address of the corresponding Position contract. function positions(address _owner, address _cToken, address _dToken, address _bToken) external view returns (address); - /** - * @notice Returns an indexed contract addresses from the list of contracts for the given owner. - * Direct external calls to this mapping require an index to retrieve - * a specific address. To get the full array, call getPositions(). - */ - function positionsLookup(address _owner) external view returns (address[] memory); - /* **************************************************************************** ** ** CORE FUNCTIONS ** ******************************************************************************/ - /** - * @notice Deploys a Position contract for msg.sender, given a _cToken, _dToken, and _bToken. - * @param _cToken The address of the token to be used as collateral. - * @param _dToken The address of the token to be borrowed. - * @param _bToken The address of the token to swap _dToken for. - */ + + /// @notice Deploys a Position contract for msg.sender, given a _cToken, _dToken, and _bToken. + /// @param _cToken The address of the token to be used as collateral. + /// @param _dToken The address of the token to be borrowed. + /// @param _bToken The address of the token to swap _dToken for. + /// @return position The address of the newly created Position contract. function createPosition(address _cToken, address _dToken, address _bToken) external payable returns (address position); - /** - * @notice Returns a list of contract addresses for the given _positionOwner. - * @param _positionOwner The owner of the Position contracts. - */ + /// @notice Returns a list of Position contract addresses owned by the supplied _positionOwner. + /// @param _positionOwner The owner of the Position contracts. + /// @return positions The list of the supplied _positionOwner's Position contract addresses. function getPositions(address _positionOwner) external view returns (address[] memory); /* **************************************************************************** @@ -56,14 +43,13 @@ interface IPositionFactory { ** ADMIN FUNCTIONS ** ******************************************************************************/ - /** - * @notice Allows owner to withdraw all of this contract's native token balance. - */ + + /// @notice Allows OWNER to withdraw all of this contract's native token balance. + /// @dev This function is only callable by the owner account. function extractNative() external payable; - /** - * @notice Allows owner to withdraw all of a specified ERC20 token's balance from this contract. - * @param _token The address of token to remove. - */ + /// @notice Allows OWNER to withdraw all of a specified ERC20 token's balance from this contract. + /// @dev This function is only callable by the owner account. + /// @param _token The address of token to remove. function extractERC20(address _token) external payable; } diff --git a/src/libraries/FeeLib.sol b/src/libraries/FeeLib.sol index e3a59a91..429b73a2 100644 --- a/src/libraries/FeeLib.sol +++ b/src/libraries/FeeLib.sol @@ -9,15 +9,18 @@ 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; - /** - * @notice Takes protocol fee from the amount of collateral supplied. - * @param _amt The amount that's subjected to the protocol fee. - * @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. - */ + /// @notice Takes the protocol fee. + /// @param _token The token to collect fees in. + /// @param _amt The amount that's subjected to the protocol fee. + /// @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 userSavings, uint256 clientFee) = IFeeCollector(FEE_COLLECTOR).getClientAllocations(_client, maxFee); diff --git a/src/services/AdminService.sol b/src/services/AdminService.sol index ad9a1e12..8603a3f5 100644 --- a/src/services/AdminService.sol +++ b/src/services/AdminService.sol @@ -3,33 +3,33 @@ pragma solidity ^0.8.21; // Local Imports import { IERC20 } from "src/interfaces/token/IERC20.sol"; +import { IAdminService } from "src/interfaces/IAdminService.sol"; import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; -/// @title Position Admin +/// @title The Position admin service /// @author Chain Rule, LLC -/// @notice Defines logic that Position and DebtService both need access to. -contract AdminService { +/// @notice Defines admin logic that Position and DebtService both need access to +contract AdminService is IAdminService { // Immutables: no SLOAD to save gas + + /// @notice The account address of the contract owner. address public immutable OWNER; // Errors error Unauthorized(); + /// @notice This function is called when the AdminService is deployed. + /// @param _owner The account address of the AdminService contract's owner. constructor(address _owner) { OWNER = _owner; } - /** - * @notice Allows owner to withdraw all of this contract's native token balance. - */ + /// @inheritdoc IAdminService function extractNative() public payable onlyOwner { payable(msg.sender).transfer(address(this).balance); } - /** - * @notice Allows owner to withdraw all of a specified ERC20 token's balance from this contract. - * @param _token The address of token to remove. - */ + /// @inheritdoc IAdminService function extractERC20(address _token) public payable onlyOwner { uint256 balance = IERC20(_token).balanceOf(address(this)); @@ -41,11 +41,6 @@ contract AdminService { */ fallback() external payable { } // solhint-disable-line no-empty-blocks - /** - * @notice Executes when native is sent to this contract with a plain transaction. - */ - receive() external payable { } // solhint-disable-line no-empty-blocks - /// @notice Check if the caller is the owner of the Position contract modifier onlyOwner() { if (msg.sender != OWNER) revert Unauthorized(); diff --git a/src/services/DebtService.sol b/src/services/DebtService.sol index b90e0f70..c8f9ca68 100644 --- a/src/services/DebtService.sol +++ b/src/services/DebtService.sol @@ -5,15 +5,16 @@ pragma solidity ^0.8.21; import { AdminService } from "src/services/AdminService.sol"; import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol"; import { IPool } from "src/interfaces/aave/IPool.sol"; +import { IDebtService } from "src/interfaces/IDebtService.sol"; import { IERC20 } from "src/interfaces/token/IERC20.sol"; import { IERC20Permit } from "src/interfaces/token/IERC20Permit.sol"; import { IAaveOracle } from "src/interfaces/aave/IAaveOracle.sol"; import { IERC20Metadata } from "src/interfaces/token/IERC20Metadata.sol"; -/// @title DebtService +/// @title The debt service contract /// @author Chain Rule, LLC -/// @notice Manages all debt-related interactions -contract DebtService is AdminService { +/// @notice Manages all debt-related interactions for each position +contract DebtService is AdminService, IDebtService { // Constants: no SLOAD to save gas address private constant AAVE_POOL = 0x794a61358D6845594F94dc1DB02A252b5b4814aD; address private constant AAVE_ORACLE = 0xb56c2F0B653B2e0b10C9b928C8580Ac5Df02C7C7; @@ -21,11 +22,23 @@ contract DebtService is AdminService { // Immutables: no SLOAD to save gas uint64 internal immutable _C_DEC_CONVERSION; uint64 internal immutable _D_DEC_CONVERSION; + + /// @notice The number of decimals the collateral token is denominated in. uint8 public immutable C_DECIMALS; + + /// @notice The number of decimals the debt token is denominated in. uint8 public immutable D_DECIMALS; + + /// @notice The address of collateral token. address public immutable C_TOKEN; + + /// @notice The address of debt token. address public immutable D_TOKEN; + /// @notice This function is called when the DebtService is deployed. + /// @param _owner The account address of the DebtService contract's owner. + /// @param _cToken The address of the token to be used as collateral. + /// @param _dToken The address of the token to be borrowed. constructor(address _owner, address _cToken, address _dToken) AdminService(_owner) { C_TOKEN = _cToken; D_TOKEN = _dToken; @@ -35,18 +48,16 @@ contract DebtService is AdminService { _D_DEC_CONVERSION = uint64(10 ** (18 - D_DECIMALS)); } - /** - * @notice Borrows debt token from Aave. - * @param _cAmt The amount of collateral to be supplied (units: C_DECIMALS). - * @param _ltv The desired loan-to-value ratio for this transaction-specific loan (ex: 75 is 75%). - * @return dAmt The amount of the debt token borrowed (units: D_DECIMALS). - * @dev dAmt is calculated as follows: - * c_amt_wei = _cAmt * _C_DEC_CONVERSION (decimals: 18) - * c_amt_usd = c_amt_wei * cPrice (decimals: 18 + 8 => 26) - * debt_amt_usd = c_amt_usd * _ltv / 100 (decimals: 26) - * debt_amt_usd_d_decimals = debt_amt_usd / _D_DEC_CONVERSION (decimals: 26 - (18 - D_DECIMALS)) - * dAmt = debt_amt_d_decimals = debt_amt_usd_d_decimals / dPrice (decimals: D_DECIMALS) - */ + /// @notice Borrows debt token from Aave. + /// @param _cAmt The amount of collateral to be supplied (units: C_DECIMALS). + /// @param _ltv The desired loan-to-value ratio for this transaction-specific loan (ex: 75 is 75%). + /// @return dAmt The amount of the debt token borrowed (units: D_DECIMALS). + /// @dev dAmt is calculated as follows: + /// @dev c_amt_wei = _cAmt * _C_DEC_CONVERSION (decimals: 18) + /// @dev c_amt_usd = c_amt_wei * cPrice (decimals: 18 + 8 => 26) + /// @dev debt_amt_usd = c_amt_usd * _ltv / 100 (decimals: 26) + /// @dev debt_amt_usd_d_decimals = debt_amt_usd / _D_DEC_CONVERSION (decimals: 26 - (18 - D_DECIMALS)) + /// @dev dAmt = debt_amt_d_decimals = debt_amt_usd_d_decimals / dPrice (decimals: D_DECIMALS) function _takeLoan(uint256 _cAmt, uint256 _ltv) internal returns (uint256 dAmt) { // 1. Supply collateral to Aave SafeTransferLib.safeApprove(ERC20(C_TOKEN), AAVE_POOL, _cAmt); @@ -63,112 +74,64 @@ contract DebtService is AdminService { _borrow(dAmt); } - /** - * @notice Borrows debt token from Aave. - * @param _dAmt The amount of the debt token to be borrowed (units: D_DECIMALS). - */ + /// @notice Borrows debt token from Aave. + /// @param _dAmt The amount of the debt token to be borrowed (units: D_DECIMALS). function _borrow(uint256 _dAmt) internal { IPool(AAVE_POOL).borrow(D_TOKEN, _dAmt, 2, 0, address(this)); } - /** - * @notice Repays debt token to Aave. - * @param _dAmt The amount of debt token to repay to Aave. - */ + /// @notice Repays debt token to Aave. + /// @param _dAmt The amount of debt token to repay to Aave. function _repay(uint256 _dAmt) internal { SafeTransferLib.safeApprove(ERC20(D_TOKEN), AAVE_POOL, _dAmt); IPool(AAVE_POOL).repay(D_TOKEN, _dAmt, 2, address(this)); } - /** - * @notice Returns this contract's total debt (principle + interest). - * @return outstandingDebt This contract's total debt + small buffer (units: D_DECIMALS). - */ - function _getDebtAmt() internal view returns (uint256) { + /// @notice Returns this contract's total debt (principle + interest). + /// @dev Adds 2 units as a small buffer to debt amount to ensure a full repayment (units: D_DECIMALS) + /// @return outstandingDebt This contract's total debt + small buffer (units: D_DECIMALS). + function _getDebtAmt() internal view returns (uint256 outstandingDebt) { address variableDebtTokenAddress = IPool(AAVE_POOL).getReserveData(D_TOKEN).variableDebtTokenAddress; - /// @dev add 2 units as a buffer to debt amount to ensure a full repayment (units: D_DECIMALS) - return IERC20(variableDebtTokenAddress).balanceOf(address(this)) + 2; + outstandingDebt = IERC20(variableDebtTokenAddress).balanceOf(address(this)) + 2; } - /** - * @notice Supplies the contract's base token balance as collateral. - * @param _bToken The address of the base token to be supplied as collateral. - * @param _bAmt The amount of collateral to be supplied (units: D_DECIMALS). - */ + /// @notice Supplies the contract's base token balance as collateral. + /// @param _bToken The address of the base token to be supplied as collateral. + /// @param _bAmt The amount of collateral to be supplied (units: D_DECIMALS). function _supplyBase(address _bToken, uint256 _bAmt) internal { - // 1. Approve Aave to spend _cAmt of this contract's C_TOKEN SafeTransferLib.safeApprove(ERC20(_bToken), AAVE_POOL, _bAmt); - - // 2. Supply collateral to Aave IPool(AAVE_POOL).supply(_bToken, _bAmt, address(this), 0); } - /** - * @notice Increases the collateral amount backing this contract's loan. - * @param _cAmt The amount of collateral to be supplied (units: C_DECIMALS). - */ + /// @inheritdoc IDebtService function addCollateral(uint256 _cAmt) public payable onlyOwner { - // 1. Transfer collateral from owner to this contract SafeTransferLib.safeTransferFrom(ERC20(C_TOKEN), msg.sender, address(this), _cAmt); - - // 2. Approve Aave to spend _cAmt of this contract's C_TOKEN SafeTransferLib.safeApprove(ERC20(C_TOKEN), AAVE_POOL, _cAmt); - - // 3. Supply collateral to Aave IPool(AAVE_POOL).supply(C_TOKEN, _cAmt, address(this), 0); } - /** - * @notice Increases the collateral amount for this contract's loan with permit, obviating the need for a separate approve tx. - * This function can only be used for ERC-2612-compliant tokens. - * @param _cAmt The amount of collateral to be supplied (units: C_DECIMALS). - * @param _deadline The expiration timestamp of the permit. - * @param _v The V parameter of ERC712 signature for the permit. - * @param _r The R parameter of ERC712 signature for the permit. - * @param _s The S parameter of ERC712 signature for the permit. - */ + /// @inheritdoc IDebtService function addCollateralWithPermit(uint256 _cAmt, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) public payable onlyOwner { - // 1. Approve with permit IERC20Permit(C_TOKEN).permit(msg.sender, address(this), _cAmt, _deadline, _v, _r, _s); - - // 2. Add Collateral addCollateral(_cAmt); } - /** - * @notice Withdraws collateral token from Aave to specified recipient. - * @param _token The address of the collateral token to be withdrawn (C_TOKEN or B_TOKEN). - * @param _amt The amount of collateral to be withdrawn (units: C_DECIMALS or B_DECIMALS). - * @param _recipient The recipient of the funds. - */ + /// @inheritdoc IDebtService function withdraw(address _token, uint256 _amt, address _recipient) public payable onlyOwner { IPool(AAVE_POOL).withdraw(_token, _amt, _recipient); } - /** - * @notice Repays any outstanding debt to Aave. - * @param _dAmt The amount of debt token to repay to Aave (units: D_DECIMALS). - * To pay off entire debt, _dAmt = debtOwed + smallBuffer (to account for interest). - */ + /// @inheritdoc IDebtService function repay(uint256 _dAmt) public payable onlyOwner { SafeTransferLib.safeTransferFrom(ERC20(D_TOKEN), msg.sender, address(this), _dAmt); _repay(_dAmt); } - /** - * @notice Repays any outstanding debt to Aave and transfers remaining collateral from Aave to owner, - * with permit, obviating the need for a separate approve tx. This function can only be used for ERC-2612-compliant tokens. - * @param _dAmt The amount of debt token to repay to Aave (units: D_DECIMALS). - * To pay off entire debt, _dAmt = debtOwed + smallBuffer (to account for interest). - * @param _deadline The expiration timestamp of the permit. - * @param _v The V parameter of ERC712 signature for the permit. - * @param _r The R parameter of ERC712 signature for the permit. - * @param _s The S parameter of ERC712 signature for the permit. - */ + /// @inheritdoc IDebtService function repayWithPermit(uint256 _dAmt, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) public payable diff --git a/src/services/SwapService.sol b/src/services/SwapService.sol index 329cd925..f31f3497 100644 --- a/src/services/SwapService.sol +++ b/src/services/SwapService.sol @@ -7,23 +7,21 @@ pragma abicoder v2; import { TransferHelper } from "src/dependencies/uniswap/TransferHelper.sol"; import { ISwapRouter } from "src/interfaces/uniswap/ISwapRouter.sol"; -/// @title SwapService +/// @title The swap service contract /// @author Chain Rule, LLC -/// @notice Manages all swap-related interactions +/// @notice Manages all swap-related interactions for each position contract SwapService { // Constants: no SLOAD to save gas ISwapRouter private constant SWAP_ROUTER = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); - /** - * @notice Swaps an exact amount of input tokens for as many output tokens as possible. - * @param _inToken The address of the input token. - * @param _outToken The address of the output token. - * @param _inTokenAmt The amount of the input token to swap. - * @param _amtOutMin The minimum amount of output tokens that must be received for the tx to go through. - * @param _poolFee The fee of the Uniswap pool. - * @return amtIn The amount of tokens sent to Uniswap. - * @return amtOut The amount of tokens received from Uniswap. - */ + /// @notice Swaps an exact amount of input tokens for as many output tokens as possible. + /// @param _inToken The address of the input token. + /// @param _outToken The address of the output token. + /// @param _inTokenAmt The amount of the input token to swap. + /// @param _amtOutMin The minimum amount of output tokens that must be received for the tx to go through. + /// @param _poolFee The fee of the Uniswap pool. + /// @return amtIn The amount of tokens sent to Uniswap. + /// @return amtOut The amount of tokens received from Uniswap. function _swapExactInput( address _inToken, address _outToken, @@ -48,16 +46,14 @@ contract SwapService { amtOut = SWAP_ROUTER.exactInputSingle(params); } - /** - * @notice Swaps as many of input tokens as necessary for an exact amount of output tokens. - * @param _inToken The address of the input token. - * @param _outToken The address of the output token. - * @param _outTokenAmt The amount of output tokens to receive. - * @param _amtInMax Maximum permissible quantity of input tokens to be exchanged for a specified amount of output tokens. - * @param _poolFee The fee of the Uniswap pool. - * @return amtIn The amount of tokens sent to Uniswap. - * @return amtOut The amount of tokens received from Uniswap. - */ + /// @notice Swaps as many of input tokens as necessary for an exact amount of output tokens. + /// @param _inToken The address of the input token. + /// @param _outToken The address of the output token. + /// @param _outTokenAmt The amount of output tokens to receive. + /// @param _amtInMax Maximum permissible quantity of input tokens to be exchanged for a specified amount of output tokens. + /// @param _poolFee The fee of the Uniswap pool. + /// @return amtIn The amount of tokens sent to Uniswap. + /// @return amtOut The amount of tokens received from Uniswap. function _swapExactOutput( address _inToken, address _outToken, diff --git a/test/FeeCollector.t.sol b/test/FeeCollector.t.sol index eb5ca5d4..7cbef52c 100644 --- a/test/FeeCollector.t.sol +++ b/test/FeeCollector.t.sol @@ -427,29 +427,6 @@ contract FeeCollectorTest is Test, TokenUtils, FeeUtils { } } - /// @dev - // - The FeeCollector's native balance should increase by the amount transferred. - function testFuzz_Receive(uint256 _amount, address _sender) public { - // Assumptions - _amount = bound(_amount, 1, 1_000 ether); - uint256 gasMoney = 1 ether; - vm.deal(_sender, _amount + gasMoney); - - // Pre-Act Data - uint256 preContractBalance = feeCollectorAddr.balance; - - // Act - vm.prank(_sender); - (bool success,) = payable(feeCollectorAddr).call{ value: _amount }(""); - - // Post-Act Data - uint256 postContractBalance = feeCollectorAddr.balance; - - // Assertions - assertTrue(success); - assertEq(postContractBalance, preContractBalance + _amount); - } - /// @dev // - The FeeCollector's native balance should increase by the amount transferred. function testFuzz_Fallback(uint256 _amount, address _sender) public { diff --git a/test/PositionFactory.t.sol b/test/PositionFactory.t.sol index 229b7799..70dcd8f3 100644 --- a/test/PositionFactory.t.sol +++ b/test/PositionFactory.t.sol @@ -267,29 +267,6 @@ contract PositionFactoryTest is Test, TokenUtils { } } - /// @dev - // - The contract's native balance should increase by the amount transferred. - function testFuzz_Receive(uint256 _amount, address _sender) public { - // Assumptions - _amount = bound(_amount, 1, 1_000 ether); - uint256 gasMoney = 1 ether; - vm.deal(_sender, _amount + gasMoney); - - // Pre-Act Data - uint256 preContractBalance = address(positionFactory).balance; - - // Act - vm.prank(_sender); - (bool success,) = payable(address(positionFactory)).call{ value: _amount }(""); - - // Post-Act Data - uint256 postContractBalance = address(positionFactory).balance; - - // Assertions - assertTrue(success); - assertEq(postContractBalance, preContractBalance + _amount); - } - /// @dev // - The contract's native balance should increase by the amount transferred. function testFuzz_Fallback(uint256 _amount, address _sender) public { diff --git a/test/common/utils/DebtUtils.t.sol b/test/common/utils/DebtUtils.t.sol index f2643fbb..1ca3de81 100644 --- a/test/common/utils/DebtUtils.t.sol +++ b/test/common/utils/DebtUtils.t.sol @@ -97,9 +97,7 @@ contract DebtUtils { } } - /** - * @notice Calculates the maximum amount of collateral that can be withdrawn after partial debt repayment. - */ + /// @notice Calculates the maximum amount of collateral that can be withdrawn after partial debt repayment. function _getMaxWithdrawCAmtAfterPartialRepay( address _debtService, address _cToken, diff --git a/test/services/AdminService.t.sol b/test/services/AdminService.t.sol index 774e57c1..0c137860 100644 --- a/test/services/AdminService.t.sol +++ b/test/services/AdminService.t.sol @@ -138,29 +138,6 @@ contract AdminServiceTest is Test, TokenUtils { } } - /// @dev - // - The contract's native balance should increase by the amount transferred. - function testFuzz_Receive(uint256 _amount, address _sender) public { - // Assumptions - _amount = bound(_amount, 1, 1_000 ether); - uint256 gasMoney = 1 ether; - vm.deal(_sender, _amount + gasMoney); - - // Pre-Act Data - uint256 preContractBalance = positionAddr.balance; - - // Act - vm.prank(_sender); - (bool success,) = payable(positionAddr).call{ value: _amount }(""); - - // Post-Act Data - uint256 postContractBalance = positionAddr.balance; - - // Assertions - assertTrue(success); - assertEq(postContractBalance, preContractBalance + _amount); - } - /// @dev // - The contract's native balance should increase by the amount transferred. function testFuzz_Fallback(uint256 _amount, address _sender) public { From d9cd3616dc797e7a6e39e4678462104e87e79b3b Mon Sep 17 00:00:00 2001 From: Adam Cuculich <46691282+cucupac@users.noreply.github.com> Date: Tue, 19 Mar 2024 22:49:51 -0500 Subject: [PATCH 5/6] refactor: move protocol fee to fee collector (#24) --- src/FeeCollector.sol | 13 +++- src/interfaces/IFeeCollector.sol | 3 + src/libraries/FeeLib.sol | 6 +- test/FeeCollector.t.sol | 61 ++++++++++++++++++- test/Position.t.sol | 3 +- test/common/Constants.t.sol | 2 +- test/integration/FeeCollector.add.t.sol | 2 +- .../FeeCollector.addLeverage.t.sol | 2 +- .../integration/FeeCollector.clientFees.t.sol | 4 +- .../integration/Position.add.successive.t.sol | 3 +- test/integration/Position.add.t.sol | 2 +- .../Position.addLeverage.successive.t.sol | 3 +- test/integration/Position.addLeverage.t.sol | 3 +- test/integration/Position.addWithPermit.t.sol | 2 +- test/integration/Position.close.gains.t.sol | 3 +- test/integration/Position.close.losses.t.sol | 3 +- test/integration/Position.close.partial.sol | 3 +- .../FeeCollector.netProtocolFees.t.sol | 4 +- test/libraries/FeeLib.t.sol | 2 +- 19 files changed, 98 insertions(+), 26 deletions(-) diff --git a/src/FeeCollector.sol b/src/FeeCollector.sol index 446520ee..7813f92a 100644 --- a/src/FeeCollector.sol +++ b/src/FeeCollector.sol @@ -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 @@ -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; } diff --git a/src/interfaces/IFeeCollector.sol b/src/interfaces/IFeeCollector.sol index 8cbc2a25..897ce71d 100644 --- a/src/interfaces/IFeeCollector.sol +++ b/src/interfaces/IFeeCollector.sol @@ -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). diff --git a/src/libraries/FeeLib.sol b/src/libraries/FeeLib.sol index 429b73a2..2127d283 100644 --- a/src/libraries/FeeLib.sol +++ b/src/libraries/FeeLib.sol @@ -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; @@ -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; diff --git a/test/FeeCollector.t.sol b/test/FeeCollector.t.sol index 7cbef52c..da1291f6 100644 --- a/test/FeeCollector.t.sol +++ b/test/FeeCollector.t.sol @@ -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"; @@ -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 @@ -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 { diff --git a/test/Position.t.sol b/test/Position.t.sol index d74b4862..32d591d4 100644 --- a/test/Position.t.sol +++ b/test/Position.t.sol @@ -14,6 +14,7 @@ import { CONTRACT_DEPLOYER, DAI, FEE_COLLECTOR, + PROTOCOL_FEE_RATE, TEST_CLIENT, TEST_LTV, TEST_POOL_FEE, @@ -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); diff --git a/test/common/Constants.t.sol b/test/common/Constants.t.sol index 93c221a9..f6bd52d5 100644 --- a/test/common/Constants.t.sol +++ b/test/common/Constants.t.sol @@ -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; diff --git a/test/integration/FeeCollector.add.t.sol b/test/integration/FeeCollector.add.t.sol index cc8cfd1a..c223c017 100644 --- a/test/integration/FeeCollector.add.t.sol +++ b/test/integration/FeeCollector.add.t.sol @@ -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); diff --git a/test/integration/FeeCollector.addLeverage.t.sol b/test/integration/FeeCollector.addLeverage.t.sol index 6264ce0e..3662788c 100644 --- a/test/integration/FeeCollector.addLeverage.t.sol +++ b/test/integration/FeeCollector.addLeverage.t.sol @@ -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); diff --git a/test/integration/FeeCollector.clientFees.t.sol b/test/integration/FeeCollector.clientFees.t.sol index 7656e3eb..649fb199 100644 --- a/test/integration/FeeCollector.clientFees.t.sol +++ b/test/integration/FeeCollector.clientFees.t.sol @@ -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"; @@ -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); } diff --git a/test/integration/Position.add.successive.t.sol b/test/integration/Position.add.successive.t.sol index cc176747..52026a7f 100644 --- a/test/integration/Position.add.successive.t.sol +++ b/test/integration/Position.add.successive.t.sol @@ -15,6 +15,7 @@ import { CONTRACT_DEPLOYER, DAI, FEE_COLLECTOR, + PROTOCOL_FEE_RATE, SUCCESSIVE_ITERATIONS, TEST_CLIENT, TEST_POOL_FEE, @@ -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); diff --git a/test/integration/Position.add.t.sol b/test/integration/Position.add.t.sol index 6d618351..739939d5 100644 --- a/test/integration/Position.add.t.sol +++ b/test/integration/Position.add.t.sol @@ -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); diff --git a/test/integration/Position.addLeverage.successive.t.sol b/test/integration/Position.addLeverage.successive.t.sol index 4a99fb95..f98f929e 100644 --- a/test/integration/Position.addLeverage.successive.t.sol +++ b/test/integration/Position.addLeverage.successive.t.sol @@ -15,6 +15,7 @@ import { CONTRACT_DEPLOYER, DAI, FEE_COLLECTOR, + PROTOCOL_FEE_RATE, SUCCESSIVE_ITERATIONS, TEST_CLIENT, TEST_POOL_FEE, @@ -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); diff --git a/test/integration/Position.addLeverage.t.sol b/test/integration/Position.addLeverage.t.sol index 42152b64..ea71445b 100644 --- a/test/integration/Position.addLeverage.t.sol +++ b/test/integration/Position.addLeverage.t.sol @@ -15,6 +15,7 @@ import { CONTRACT_DEPLOYER, DAI, FEE_COLLECTOR, + PROTOCOL_FEE_RATE, TEST_CLIENT, TEST_LTV, TEST_POOL_FEE, @@ -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); diff --git a/test/integration/Position.addWithPermit.t.sol b/test/integration/Position.addWithPermit.t.sol index c1224860..23d48878 100644 --- a/test/integration/Position.addWithPermit.t.sol +++ b/test/integration/Position.addWithPermit.t.sol @@ -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); diff --git a/test/integration/Position.close.gains.t.sol b/test/integration/Position.close.gains.t.sol index 2abd92ca..01bcfb6c 100644 --- a/test/integration/Position.close.gains.t.sol +++ b/test/integration/Position.close.gains.t.sol @@ -14,6 +14,7 @@ import { DAI, FEE_COLLECTOR, PROFIT_PERCENT, + PROTOCOL_FEE_RATE, SWAP_ROUTER, TEST_CLIENT, TEST_LTV, @@ -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); diff --git a/test/integration/Position.close.losses.t.sol b/test/integration/Position.close.losses.t.sol index daedb468..507c7480 100644 --- a/test/integration/Position.close.losses.t.sol +++ b/test/integration/Position.close.losses.t.sol @@ -13,6 +13,7 @@ import { CONTRACT_DEPLOYER, DAI, FEE_COLLECTOR, + PROTOCOL_FEE_RATE, REPAY_PERCENT, SWAP_ROUTER, TEST_CLIENT, @@ -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); diff --git a/test/integration/Position.close.partial.sol b/test/integration/Position.close.partial.sol index 48b8f60e..6d836032 100644 --- a/test/integration/Position.close.partial.sol +++ b/test/integration/Position.close.partial.sol @@ -13,6 +13,7 @@ import { CONTRACT_DEPLOYER, DAI, FEE_COLLECTOR, + PROTOCOL_FEE_RATE, SWAP_ROUTER, TEST_CLIENT, TEST_LTV, @@ -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); diff --git a/test/invariant/FeeCollector.netProtocolFees.t.sol b/test/invariant/FeeCollector.netProtocolFees.t.sol index 707073ab..e408ba3d 100644 --- a/test/invariant/FeeCollector.netProtocolFees.t.sol +++ b/test/invariant/FeeCollector.netProtocolFees.t.sol @@ -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"; @@ -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); } diff --git a/test/libraries/FeeLib.t.sol b/test/libraries/FeeLib.t.sol index 3f7440d4..af88a615 100644 --- a/test/libraries/FeeLib.t.sol +++ b/test/libraries/FeeLib.t.sol @@ -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); From acd26579c569a676226eb55f77710a2f89a56729 Mon Sep 17 00:00:00 2001 From: Tranquil-Flow <66773372+Tranquil-Flow@users.noreply.github.com> Date: Fri, 22 Mar 2024 12:40:06 +1000 Subject: [PATCH 6/6] refactor: remove fallback() --- src/PositionFactory.sol | 10 ---------- src/services/AdminService.sol | 11 ----------- test/FeeCollector.t.sol | 23 ----------------------- test/PositionFactory.t.sol | 23 ----------------------- test/services/AdminService.t.sol | 23 ----------------------- 5 files changed, 90 deletions(-) diff --git a/src/PositionFactory.sol b/src/PositionFactory.sol index 4dd7beb0..d2c17bac 100644 --- a/src/PositionFactory.sol +++ b/src/PositionFactory.sol @@ -77,14 +77,4 @@ contract PositionFactory is Ownable, IPositionFactory { SafeTransferLib.safeTransfer(ERC20(_token), msg.sender, balance); } - /** -<<<<<<< HEAD - * @notice Executes when native is sent to this contract through a non-existent function. - */ - fallback() external payable { } // solhint-disable-line no-empty-blocks -======= - * @notice Executes when native is sent to this contract with a plain transaction. - */ - receive() external payable { } // solhint-disable-line no-empty-blocks ->>>>>>> f79c7b55671c8204d3d03bb5efe0ec65e1f85a29 } diff --git a/src/services/AdminService.sol b/src/services/AdminService.sol index d04f1ca1..4244d61d 100644 --- a/src/services/AdminService.sol +++ b/src/services/AdminService.sol @@ -36,17 +36,6 @@ contract AdminService is IAdminService { SafeTransferLib.safeTransfer(ERC20(_token), msg.sender, balance); } - /** -<<<<<<< HEAD - * @notice Executes when native is sent to this contract through a non-existent function. - */ - fallback() external payable { } // solhint-disable-line no-empty-blocks -======= - * @notice Executes when native is sent to this contract with a plain transaction. - */ - receive() external payable { } // solhint-disable-line no-empty-blocks ->>>>>>> f79c7b55671c8204d3d03bb5efe0ec65e1f85a29 - /// @notice Check if the caller is the owner of the Position contract modifier onlyOwner() { if (msg.sender != OWNER) revert Unauthorized(); diff --git a/test/FeeCollector.t.sol b/test/FeeCollector.t.sol index da1291f6..4e71e67d 100644 --- a/test/FeeCollector.t.sol +++ b/test/FeeCollector.t.sol @@ -483,27 +483,4 @@ contract FeeCollectorTest is Test, TokenUtils, FeeUtils { feeCollector.extractERC20(token); } } - - /// @dev - // - The FeeCollector's native balance should increase by the amount transferred. - function testFuzz_Fallback(uint256 _amount, address _sender) public { - // Assumptions - vm.assume(_amount != 0 && _amount <= 1000 ether); - uint256 gasMoney = 1 ether; - vm.deal(_sender, _amount + gasMoney); - - // Pre-Act Data - uint256 preContractBalance = feeCollectorAddr.balance; - - // Act - vm.prank(_sender); - (bool success,) = feeCollectorAddr.call{ value: _amount }(abi.encodeWithSignature("nonExistentFn()")); - - // Post-Act Data - uint256 postContractBalance = feeCollectorAddr.balance; - - // Assertions - assertTrue(success); - assertEq(postContractBalance, preContractBalance + _amount); - } } diff --git a/test/PositionFactory.t.sol b/test/PositionFactory.t.sol index 70dcd8f3..b748941c 100644 --- a/test/PositionFactory.t.sol +++ b/test/PositionFactory.t.sol @@ -266,27 +266,4 @@ contract PositionFactoryTest is Test, TokenUtils { positionFactory.extractERC20(supportedAssets[i]); } } - - /// @dev - // - The contract's native balance should increase by the amount transferred. - function testFuzz_Fallback(uint256 _amount, address _sender) public { - // Assumptions - vm.assume(_amount != 0 && _amount <= 1000 ether); - uint256 gasMoney = 1 ether; - vm.deal(_sender, _amount + gasMoney); - - // Pre-Act Data - uint256 preContractBalance = address(positionFactory).balance; - - // Act - vm.prank(_sender); - (bool success,) = address(positionFactory).call{ value: _amount }(abi.encodeWithSignature("nonExistentFn()")); - - // Post-Act Data - uint256 postContractBalance = address(positionFactory).balance; - - // Assertions - assertTrue(success); - assertEq(postContractBalance, preContractBalance + _amount); - } } diff --git a/test/services/AdminService.t.sol b/test/services/AdminService.t.sol index 0c137860..f64ef3bc 100644 --- a/test/services/AdminService.t.sol +++ b/test/services/AdminService.t.sol @@ -138,29 +138,6 @@ contract AdminServiceTest is Test, TokenUtils { } } - /// @dev - // - The contract's native balance should increase by the amount transferred. - function testFuzz_Fallback(uint256 _amount, address _sender) public { - // Assumptions - vm.assume(_amount != 0 && _amount <= 1000 ether); - uint256 gasMoney = 1 ether; - vm.deal(_sender, _amount + gasMoney); - - // Pre-Act Data - uint256 preContractBalance = positionAddr.balance; - - // Act - vm.prank(_sender); - (bool success,) = positionAddr.call{ value: _amount }(abi.encodeWithSignature("nonExistentFn()")); - - // Post-Act Data - uint256 postContractBalance = positionAddr.balance; - - // Assertions - assertTrue(success); - assertEq(postContractBalance, preContractBalance + _amount); - } - /// @dev Necessary for the owner, address(this), to receive native extractNative tests receive() external payable { } }