22pragma solidity ^ 0.8.19 ;
33
44import { IFeePolicy } from "./_interfaces/IFeePolicy.sol " ;
5- import { IPerpetualTranche, IBondController } from "./_interfaces/IPerpetualTranche.sol " ;
6- import { IVault } from "./_interfaces/IVault.sol " ;
75
86import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol " ;
97import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol " ;
10- import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol " ;
118import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol " ;
129import { Sigmoid } from "./_utils/Sigmoid.sol " ;
1310
14- import { UnacceptableSwap } from "./_interfaces/ProtocolErrors.sol " ;
11+ /// @notice Expected perc value to be at most (1 * 10**DECIMALS), i.e) 1.0 or 100%.
12+ error InvalidPerc ();
13+
14+ /// @notice Expected target subscription ratio to be within defined bounds.
15+ error InvalidTargetSRBounds ();
16+
17+ /// @notice Expected sigmoid asymptotes to be within defined bounds.
18+ error InvalidSigmoidAsymptotes ();
1519
1620/**
1721 * @title FeePolicy
@@ -24,7 +28,7 @@ import { UnacceptableSwap } from "./_interfaces/ProtocolErrors.sol";
2428 *
2529 * Fees are computed based on the deviation between the system's current subscription ratio
2630 * and the target subscription ratio.
27- * - `subscriptionRatio` = (vaultTVL * perpTR ) / (perpTVL * vaultTR )
31+ * - `subscriptionRatio` = (vaultTVL * seniorTR ) / (perpTVL * 1-seniorTR )
2832 * - `deviationRatio` (dr) = subscriptionRatio / targetSubscriptionRatio
2933 *
3034 * When the system is "under-subscribed" (dr <= 1):
@@ -58,13 +62,19 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {
5862 using MathUpgradeable for uint256 ;
5963 using SafeCastUpgradeable for uint256 ;
6064
65+ // Replicating value used here:
66+ // https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol
67+ uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000 ;
68+
6169 /// @dev The returned fee percentages are fixed point numbers with {DECIMALS} places.
6270 /// The decimals should line up with value expected by consumer (perp, vault).
6371 /// NOTE: 10**DECIMALS => 100% or 1.0
6472 uint8 public constant DECIMALS = 8 ;
6573 uint256 public constant ONE = (1 * 10 ** DECIMALS); // 1.0 or 100%
6674
75+ /// @dev SIGMOID_BOUND is set to 1%, i.e) the rollover fee can be at most 1% on either direction.
6776 uint256 public constant SIGMOID_BOUND = ONE / 100 ; // 0.01 or 1%
77+
6878 uint256 public constant SR_LOWER_BOUND = (ONE * 75 ) / 100 ; // 0.75 or 75%
6979 uint256 public constant SR_UPPER_BOUND = 2 * ONE; // 2.0 or 200%
7080
@@ -149,33 +159,40 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {
149159 /// @notice Updates the target subscription ratio.
150160 /// @param targetSubscriptionRatio_ The new target subscription ratio as a fixed point number with {DECIMALS} places.
151161 function updateTargetSubscriptionRatio (uint256 targetSubscriptionRatio_ ) external onlyOwner {
152- require (targetSubscriptionRatio_ > SR_LOWER_BOUND, "FeeStrategy: sr too low " );
153- require (targetSubscriptionRatio_ <= SR_UPPER_BOUND, "FeeStrategy: sr high low " );
162+ if (targetSubscriptionRatio_ < SR_LOWER_BOUND || targetSubscriptionRatio_ > SR_UPPER_BOUND) {
163+ revert InvalidTargetSRBounds ();
164+ }
154165 targetSubscriptionRatio = targetSubscriptionRatio_;
155166 }
156167
157168 /// @notice Updates the perp mint fee parameters.
158169 /// @param perpMintFeePerc_ The new perp mint fee ceiling percentage
159170 /// as a fixed point number with {DECIMALS} places.
160171 function updatePerpMintFees (uint256 perpMintFeePerc_ ) external onlyOwner {
161- require (perpMintFeePerc_ <= ONE, "FeeStrategy: perc too high " );
172+ if (perpMintFeePerc_ > ONE) {
173+ revert InvalidPerc ();
174+ }
162175 perpMintFeePerc = perpMintFeePerc_;
163176 }
164177
165178 /// @notice Updates the perp burn fee parameters.
166179 /// @param perpBurnFeePerc_ The new perp burn fee ceiling percentage
167180 /// as a fixed point number with {DECIMALS} places.
168181 function updatePerpBurnFees (uint256 perpBurnFeePerc_ ) external onlyOwner {
169- require (perpBurnFeePerc_ <= ONE, "FeeStrategy: perc too high " );
182+ if (perpBurnFeePerc_ > ONE) {
183+ revert InvalidPerc ();
184+ }
170185 perpBurnFeePerc = perpBurnFeePerc_;
171186 }
172187
173188 /// @notice Update the parameters determining the slope and asymptotes of the sigmoid fee curve.
174189 /// @param p Lower, Upper and Growth sigmoid paramters are fixed point numbers with {DECIMALS} places.
175190 function updatePerpRolloverFees (RolloverFeeSigmoidParams calldata p ) external onlyOwner {
176- require (p.lower >= - int256 (SIGMOID_BOUND), "FeeStrategy: sigmoid lower bound too low " );
177- require (p.upper <= int256 (SIGMOID_BOUND), "FeeStrategy: sigmoid upper bound too high " );
178- require (p.lower <= p.upper, "FeeStrategy: sigmoid asymptotes invalid " );
191+ // If the bond duration is 28 days and 13 rollovers happen per year,
192+ // perp can be inflated or enriched up to ~13% annually.
193+ if (p.lower < - int256 (SIGMOID_BOUND) || p.upper > int256 (SIGMOID_BOUND) || p.lower > p.upper) {
194+ revert InvalidSigmoidAsymptotes ();
195+ }
179196 perpRolloverFee.lower = p.lower;
180197 perpRolloverFee.upper = p.upper;
181198 perpRolloverFee.growth = p.growth;
@@ -185,15 +202,19 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {
185202 /// @param vaultMintFeePerc_ The new vault mint fee ceiling percentage
186203 /// as a fixed point number with {DECIMALS} places.
187204 function updateVaultMintFees (uint256 vaultMintFeePerc_ ) external onlyOwner {
188- require (vaultMintFeePerc_ <= ONE, "FeeStrategy: perc too high " );
205+ if (vaultMintFeePerc_ > ONE) {
206+ revert InvalidPerc ();
207+ }
189208 vaultMintFeePerc = vaultMintFeePerc_;
190209 }
191210
192211 /// @notice Updates the vault burn fee parameters.
193212 /// @param vaultBurnFeePerc_ The new vault burn fee ceiling percentage
194213 /// as a fixed point number with {DECIMALS} places.
195214 function updateVaultBurnFees (uint256 vaultBurnFeePerc_ ) external onlyOwner {
196- require (vaultBurnFeePerc_ <= ONE, "FeeStrategy: perc too high " );
215+ if (vaultBurnFeePerc_ > ONE) {
216+ revert InvalidPerc ();
217+ }
197218 vaultBurnFeePerc = vaultBurnFeePerc_;
198219 }
199220
@@ -206,14 +227,18 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {
206227 /// @notice Updates the vault's share of the underlying to perp swap fee.
207228 /// @param feePerc The new fee percentage.
208229 function updateVaultUnderlyingToPerpSwapFeePerc (uint256 feePerc ) external onlyOwner {
209- require (feePerc <= ONE, "FeeStrategy: perc too high " );
230+ if (feePerc > ONE) {
231+ revert InvalidPerc ();
232+ }
210233 vaultUnderlyingToPerpSwapFeePerc = feePerc;
211234 }
212235
213236 /// @notice Updates the vault's share of the perp to underlying swap fee.
214237 /// @param feePerc The new fee percentage.
215238 function updateVaultPerpToUnderlyingSwapFeePerc (uint256 feePerc ) external onlyOwner {
216- require (feePerc <= ONE, "FeeStrategy: perc too high " );
239+ if (feePerc > ONE) {
240+ revert InvalidPerc ();
241+ }
217242 vaultPerpToUnderlyingSwapFeePerc = feePerc;
218243 }
219244
@@ -286,13 +311,18 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {
286311 }
287312
288313 /// @inheritdoc IFeePolicy
289- function computeDeviationRatio (IFeePolicy.SubscriptionParams memory s ) external view returns (uint256 ) {
314+ function computeDeviationRatio (IFeePolicy.SubscriptionParams memory s ) public view returns (uint256 ) {
315+ return computeDeviationRatio (s.perpTVL, s.vaultTVL, s.seniorTR);
316+ }
317+
318+ /// @inheritdoc IFeePolicy
319+ function computeDeviationRatio (
320+ uint256 perpTVL ,
321+ uint256 vaultTVL ,
322+ uint256 seniorTR
323+ ) public view returns (uint256 ) {
290324 // NOTE: We assume that perp's TVL and vault's TVL values have the same base denomination.
291- return
292- s.vaultTVL.mulDiv (s.perpTR, (s.perpTVL * s.vaultTR), MathUpgradeable.Rounding.Up).mulDiv (
293- ONE,
294- targetSubscriptionRatio,
295- MathUpgradeable.Rounding.Up
296- );
325+ uint256 juniorTR = TRANCHE_RATIO_GRANULARITY - seniorTR;
326+ return (vaultTVL * seniorTR).mulDiv (ONE, (perpTVL * juniorTR)).mulDiv (ONE, targetSubscriptionRatio);
297327 }
298328}
0 commit comments