Skip to content

Commit 5e7362d

Browse files
committed
ensure exchange rate up-only
1 parent edc31b0 commit 5e7362d

File tree

3 files changed

+44
-20
lines changed

3 files changed

+44
-20
lines changed

test/invariants/EthenaARM/Properties.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ abstract contract Properties is TargetFunctions {
4343
// [x] Invariant I: withdrawsClaimed == ∑claimRedeem.amount
4444
// [x] Invariant J: ∀ requestId, request.queued >= request.assets
4545
// [x] Invariant K: ∑feesCollected == feeCollector.balance
46+
// [x] Invariant O: exchangeRate is non-decreasing (done in handlers)
4647
//
4748
// ╔══════════════════════════════════════════════════════════════════════════════╗
4849
// ║ ✦✦✦ LIQUIDITY MANAGEMENT ✦✦✦ ║

test/invariants/EthenaARM/Setup.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,19 @@ abstract contract Setup is Base_Test_ {
309309
return a <= b ? a : b;
310310
}
311311

312+
function getExchangeRate() internal view returns (uint256) {
313+
return arm.totalAssets() * 1e18 / arm.totalSupply();
314+
}
315+
312316
modifier ensureTimeIncrease() {
313317
uint256 oldTimestamp = block.timestamp;
314318
_;
315319
require(block.timestamp >= oldTimestamp, "TIME_DECREASED");
316320
}
321+
322+
modifier ensureExchangeRateIncrease() {
323+
uint256 oldExchangeRate = getExchangeRate();
324+
_;
325+
require(getExchangeRate() >= oldExchangeRate, "EXCHANGE_RATE_DECREASED");
326+
}
317327
}

test/invariants/EthenaARM/TargetFunctions.sol

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
6565
// ╔══════════════════════════════════════════════════════════════════════════════╗
6666
// ║ ✦✦✦ ETHENA ARM ✦✦✦ ║
6767
// ╚══════════════════════════════════════════════════════════════════════════════╝
68-
function targetARMDeposit(uint88 amount, uint256 randomAddressIndex) external {
68+
function targetARMDeposit(uint88 amount, uint256 randomAddressIndex) external ensureExchangeRateIncrease {
6969
// Select a random user from makers
7070
address user = makers[randomAddressIndex % MAKERS_COUNT];
7171

@@ -94,7 +94,10 @@ abstract contract TargetFunctions is Setup, StdUtils {
9494
mintedUSDe[user] += amount;
9595
}
9696

97-
function targetARMRequestRedeem(uint88 shareAmount, uint248 randomAddressIndex) external {
97+
function targetARMRequestRedeem(uint88 shareAmount, uint248 randomAddressIndex)
98+
external
99+
ensureExchangeRateIncrease
100+
{
98101
address user;
99102
uint256 balance;
100103
// Todo: mirgate it to Find library
@@ -134,7 +137,11 @@ abstract contract TargetFunctions is Setup, StdUtils {
134137
sumUSDeUserRequest += amount;
135138
}
136139

137-
function targetARMClaimRedeem(uint248 randomAddressIndex, uint248 randomArrayIndex) external ensureTimeIncrease {
140+
function targetARMClaimRedeem(uint248 randomAddressIndex, uint248 randomArrayIndex)
141+
external
142+
ensureExchangeRateIncrease
143+
ensureTimeIncrease
144+
{
138145
address user;
139146
uint256 requestId;
140147
uint256 claimTimestamp;
@@ -207,7 +214,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
207214
}
208215
}
209216

210-
function targetARMSetARMBuffer(uint256 pct) external {
217+
function targetARMSetARMBuffer(uint256 pct) external ensureExchangeRateIncrease {
211218
pct = _bound(pct, 0, 100);
212219

213220
vm.prank(operator);
@@ -218,7 +225,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
218225
}
219226
}
220227

221-
function targetARMSetActiveMarket(bool isActive) external {
228+
function targetARMSetActiveMarket(bool isActive) external ensureExchangeRateIncrease {
222229
// If isActive is true it will `setActiveMarket` with MorphoMarket
223230
// else it will set it to address(0)
224231
address currentMarket = arm.activeMarket();
@@ -252,7 +259,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
252259
}
253260
}
254261

255-
function targetARMAllocate() external {
262+
function targetARMAllocate() external ensureExchangeRateIncrease {
256263
address currentMarket = arm.activeMarket();
257264
if (assume(currentMarket != address(0))) return;
258265

@@ -281,7 +288,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
281288
}
282289
}
283290

284-
function targetARMSetPrices(uint256 buyPrice, uint256 sellPrice) external {
291+
function targetARMSetPrices(uint256 buyPrice, uint256 sellPrice) external ensureExchangeRateIncrease {
285292
uint256 crossPrice = arm.crossPrice();
286293
// Bound sellPrice
287294
sellPrice = uint120(_bound(sellPrice, crossPrice, (1e37 - 1) / 9)); // -> min traderate0 -> 0.9e36
@@ -301,7 +308,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
301308
}
302309
}
303310

304-
function targetARMSetCrossPrice(uint256 crossPrice) external {
311+
function targetARMSetCrossPrice(uint256 crossPrice) external ensureExchangeRateIncrease {
305312
uint256 maxCrossPrice = 1e36;
306313
uint256 minCrossPrice = 1e36 - 20e32;
307314
uint256 sellT1 = 1e72 / (arm.traderate0());
@@ -325,6 +332,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
325332

326333
function targetARMSwapExactTokensForTokens(bool token0ForToken1, uint88 amountIn, uint256 randomAddressIndex)
327334
external
335+
ensureExchangeRateIncrease
328336
{
329337
(IERC20 tokenIn, IERC20 tokenOut) = token0ForToken1
330338
? (IERC20(address(usde)), IERC20(address(susde)))
@@ -398,6 +406,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
398406

399407
function targetARMSwapTokensForExactTokens(bool token0ForToken1, uint88 amountOut, uint256 randomAddressIndex)
400408
external
409+
ensureExchangeRateIncrease
401410
{
402411
(IERC20 tokenIn, IERC20 tokenOut) = token0ForToken1
403412
? (IERC20(address(usde)), IERC20(address(susde)))
@@ -472,7 +481,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
472481
}
473482
}
474483

475-
function targetARMCollectFees() external {
484+
function targetARMCollectFees() external ensureExchangeRateIncrease {
476485
uint256 feesAccrued = arm.feesAccrued();
477486
uint256 balance = usde.balanceOf(address(arm));
478487
uint256 outstandingWithdrawals = arm.withdrawsQueued() - arm.withdrawsClaimed();
@@ -488,7 +497,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
488497
sumUSDeFeesCollected += feesCollected;
489498
}
490499

491-
function targetARMSetFees(uint256 fee) external {
500+
function targetARMSetFees(uint256 fee) external ensureExchangeRateIncrease {
492501
// Ensure current fee can be collected
493502
uint256 feesAccrued = arm.feesAccrued();
494503
if (feesAccrued != 0) {
@@ -510,7 +519,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
510519
sumUSDeFeesCollected += feesAccrued;
511520
}
512521

513-
function targetARMRequestBaseWithdrawal(uint88 amount) external {
522+
function targetARMRequestBaseWithdrawal(uint88 amount) external ensureExchangeRateIncrease {
514523
uint256 balance = susde.balanceOf(address(arm));
515524
if (assume(balance > 1)) return;
516525
amount = uint88(_bound(amount, 1, balance));
@@ -560,7 +569,11 @@ abstract contract TargetFunctions is Setup, StdUtils {
560569
sumSUSDeBaseRedeem += amount;
561570
}
562571

563-
function targetARMClaimBaseWithdrawals(uint256 randomAddressIndex) external ensureTimeIncrease {
572+
function targetARMClaimBaseWithdrawals(uint256 randomAddressIndex)
573+
external
574+
ensureExchangeRateIncrease
575+
ensureTimeIncrease
576+
{
564577
if (assume(unstakerIndices.length != 0)) return;
565578
// Select a random unstaker index from used unstakers
566579
uint256 selectedIndex = unstakerIndices[randomAddressIndex % unstakerIndices.length];
@@ -613,7 +626,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
613626
// ╔══════════════════════════════════════════════════════════════════════════════╗
614627
// ║ ✦✦✦ SUSDE ✦✦✦ ║
615628
// ╚══════════════════════════════════════════════════════════════════════════════╝
616-
function targetSUSDeDeposit(uint88 amount) external {
629+
function targetSUSDeDeposit(uint88 amount) external ensureExchangeRateIncrease {
617630
// Ensure we don't mint 0 shares.
618631
uint256 totalAssets = susde.totalAssets();
619632
uint256 totalSupply = susde.totalSupply();
@@ -635,7 +648,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
635648
}
636649
}
637650

638-
function targetSUSDeCooldownShares(uint88 shareAmount) external {
651+
function targetSUSDeCooldownShares(uint88 shareAmount) external ensureExchangeRateIncrease {
639652
// Cache balance
640653
uint256 balance = susde.balanceOf(grace);
641654

@@ -657,7 +670,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
657670
}
658671
}
659672

660-
function targetSUSDeUnstake() external ensureTimeIncrease {
673+
function targetSUSDeUnstake() external ensureExchangeRateIncrease ensureTimeIncrease {
661674
// Check grace's cooldown
662675
UserCooldown memory cooldown = susde.cooldowns(grace);
663676

@@ -696,7 +709,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
696709
MockERC20(address(usde)).burn(grace, cooldown.underlyingAmount);
697710
}
698711

699-
function targetSUSDeTransferInRewards(uint8 bps) external ensureTimeIncrease {
712+
function targetSUSDeTransferInRewards(uint8 bps) external ensureExchangeRateIncrease ensureTimeIncrease {
700713
// Ensure enough time has passed since last distribution
701714
uint256 lastDistribution = susde.lastDistributionTimestamp();
702715
if (block.timestamp < 8 hours + lastDistribution) {
@@ -735,7 +748,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
735748
// ╔══════════════════════════════════════════════════════════════════════════════╗
736749
// ║ ✦✦✦ MORPHO ✦✦✦ ║
737750
// ╚══════════════════════════════════════════════════════════════════════════════╝
738-
function targetMorphoDeposit(uint88 amount) external {
751+
function targetMorphoDeposit(uint88 amount) external ensureExchangeRateIncrease {
739752
// Ensure we don't mint 0 shares.
740753
uint256 totalAssets = morpho.totalAssets();
741754
uint256 totalSupply = morpho.totalSupply();
@@ -757,7 +770,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
757770
}
758771
}
759772

760-
function targetMorphoWithdraw(uint88 amount) external {
773+
function targetMorphoWithdraw(uint88 amount) external ensureExchangeRateIncrease {
761774
// Check harry's balance
762775
uint256 balance = morpho.balanceOf(harry);
763776

@@ -783,7 +796,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
783796
MockERC20(address(usde)).burn(harry, amount);
784797
}
785798

786-
function targetMorphoTransferInRewards(uint8 bps) external {
799+
function targetMorphoTransferInRewards(uint8 bps) external ensureExchangeRateIncrease {
787800
uint256 balance = usde.balanceOf(address(morpho));
788801
bps = uint8(_bound(bps, 1, 10));
789802
uint256 rewards = (balance * bps) / 10_000;
@@ -794,7 +807,7 @@ abstract contract TargetFunctions is Setup, StdUtils {
794807
}
795808
}
796809

797-
function targetMorphoSetUtilizationRate(uint256 pct) external {
810+
function targetMorphoSetUtilizationRate(uint256 pct) external ensureExchangeRateIncrease {
798811
pct = _bound(pct, 0, 100);
799812

800813
morpho.setUtilizationRate(pct * 1e16);

0 commit comments

Comments
 (0)