Skip to content

Commit ada8cc5

Browse files
authored
Merge pull request #190 from ampleforth/v2-unit-tests
Contract code cleanup and unit tests
2 parents ebc38ce + ea642b2 commit ada8cc5

40 files changed

Lines changed: 6229 additions & 4747 deletions

spot-contracts/contracts/BondIssuer.sol

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/O
99
import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
1010
import { BondHelpers } from "./_utils/BondHelpers.sol";
1111

12+
/// @notice Expected tranche ratios to sum up to {TRANCHE_RATIO_GRANULARITY}.
13+
error UnacceptableTrancheRatios();
14+
1215
/**
1316
* @title BondIssuer
1417
*
@@ -102,7 +105,10 @@ contract BondIssuer is IBondIssuer, OwnableUpgradeable {
102105
for (uint8 i = 0; i < trancheRatios_.length; i++) {
103106
ratioSum += trancheRatios_[i];
104107
}
105-
require(ratioSum == TRANCHE_RATIO_GRANULARITY, "BondIssuer: Invalid tranche ratios");
108+
109+
if (ratioSum > TRANCHE_RATIO_GRANULARITY) {
110+
revert UnacceptableTrancheRatios();
111+
}
106112
}
107113

108114
/// @notice Updates the bond frequency and offset.

spot-contracts/contracts/FeePolicy.sol

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22
pragma solidity ^0.8.19;
33

44
import { IFeePolicy } from "./_interfaces/IFeePolicy.sol";
5-
import { IPerpetualTranche, IBondController } from "./_interfaces/IPerpetualTranche.sol";
6-
import { IVault } from "./_interfaces/IVault.sol";
75

86
import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
97
import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
10-
import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
118
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
129
import { 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

Comments
 (0)