11// SPDX-License-Identifier: BUSL-1.1
2- pragma solidity ^ 0.8.18 ;
2+ pragma solidity ^ 0.8.19 ;
33
44import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol " ;
55import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol " ;
66import { SignedMathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SignedMathUpgradeable.sol " ;
7+ import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol " ;
78import { SignedMathHelpers } from "../_utils/SignedMathHelpers.sol " ;
89
910import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol " ;
@@ -26,8 +27,8 @@ error UnacceptablePercValue(int256 perc);
2627contract BasicFeeStrategy is IFeeStrategy , OwnableUpgradeable {
2728 using SignedMathUpgradeable for int256 ;
2829 using SignedMathHelpers for int256 ;
29- using SafeCastUpgradeable for uint256 ;
3030 using SafeCastUpgradeable for int256 ;
31+ using SafeCastUpgradeable for uint256 ;
3132
3233 /// @dev {10 ** PERC_DECIMALS} is considered 1%
3334 uint8 public constant PERC_DECIMALS = 6 ;
@@ -38,6 +39,10 @@ contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable {
3839 /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
3940 IERC20Upgradeable public immutable override feeToken;
4041
42+ /// @notice The address of the fee reserve.
43+ /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
44+ address public immutable feeReserve;
45+
4146 /// @notice Fixed percentage of the mint amount to be used as fee.
4247 int256 public mintFeePerc;
4348
@@ -47,6 +52,11 @@ contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable {
4752 /// @notice Fixed percentage of the rollover amount to be used as fee.
4853 int256 public rolloverFeePerc;
4954
55+ /// @notice Allows debasement of perp supply when the fee reserve is empty.
56+ /// @dev When the fee amount is negative, ie) paid from the reserve to the user
57+ /// this flag stops paying out more than the reserve balance through perp supply inflation.
58+ bool public allowDebase;
59+
5060 // EVENTS
5161
5262 /// @notice Event emitted when the mint fee percentage is updated.
@@ -61,10 +71,16 @@ contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable {
6171 /// @param rolloverFeePerc Rollover fee percentage.
6272 event UpdatedRolloverPerc (int256 rolloverFeePerc );
6373
74+ /// @notice Event emitted when the debasement rule is updated.
75+ /// @param allow If debasement is allowed or not.
76+ event UpdatedDebasementRule (bool allow );
77+
6478 /// @notice Contract constructor.
6579 /// @param feeToken_ Address of the fee ERC-20 token contract.
66- constructor (IERC20Upgradeable feeToken_ ) {
80+ /// @param feeReserve_ Address of the fee reserve.
81+ constructor (IERC20Upgradeable feeToken_ , address feeReserve_ ) {
6782 feeToken = feeToken_;
83+ feeReserve = feeReserve_;
6884 }
6985
7086 /// @notice Contract initializer.
@@ -103,21 +119,41 @@ contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable {
103119 emit UpdatedRolloverPerc (rolloverFeePerc_);
104120 }
105121
122+ /// @notice Update debasement rule.
123+ /// @param allow If debasement is allowed or not.
124+ function allowDebasement (bool allow ) public onlyOwner {
125+ allowDebase = allow;
126+ emit UpdatedDebasementRule (allow);
127+ }
128+
106129 /// @inheritdoc IFeeStrategy
107130 function computeMintFees (uint256 mintAmt ) external view override returns (int256 , uint256 ) {
108- uint256 absoluteFee = (mintFeePerc.abs () * mintAmt) / HUNDRED_PERC;
109- return (mintFeePerc.sign () * absoluteFee.toInt256 (), 0 );
131+ return (_computeFeeAmt (mintAmt, mintFeePerc), 0 );
110132 }
111133
112134 /// @inheritdoc IFeeStrategy
113135 function computeBurnFees (uint256 burnAmt ) external view override returns (int256 , uint256 ) {
114- uint256 absoluteFee = (burnFeePerc.abs () * burnAmt) / HUNDRED_PERC;
115- return (burnFeePerc.sign () * absoluteFee.toInt256 (), 0 );
136+ return (_computeFeeAmt (burnAmt, burnFeePerc), 0 );
116137 }
117138
118139 /// @inheritdoc IFeeStrategy
119140 function computeRolloverFees (uint256 rolloverAmt ) external view override returns (int256 , uint256 ) {
120- uint256 absoluteFee = (rolloverFeePerc.abs () * rolloverAmt) / HUNDRED_PERC;
121- return (rolloverFeePerc.sign () * absoluteFee.toInt256 (), 0 );
141+ return (_computeFeeAmt (rolloverAmt, rolloverFeePerc), 0 );
142+ }
143+
144+ /// @dev Given the token amount and fee percentage, computes the integer amount to be charged as fees.
145+ function _computeFeeAmt (uint256 amount , int256 feePerc ) private view returns (int256 ) {
146+ uint256 absoluteFee = (feePerc.abs () * amount) / HUNDRED_PERC;
147+ int256 feeAmt = feePerc.sign () * absoluteFee.toInt256 ();
148+
149+ // when fee is to be paid by the user or when debasement is allowed
150+ // use the entire fee amount
151+ if (feeAmt >= 0 || allowDebase) {
152+ return feeAmt;
153+ }
154+
155+ // fee is to be paid to the user and debasement is not allowed
156+ // pay out only till the reserve is depleted
157+ return - 1 * MathUpgradeable.min (feeToken.balanceOf (feeReserve), absoluteFee).toInt256 ();
122158 }
123159}
0 commit comments