Skip to content

Commit 18dfd4a

Browse files
authored
Generalize open_long & open_short test cases into InstanceTest suite (#889)
* First step, migrated open long test cases for stETH to testing suite * Migrated all yield sources to InstanceTestV2 * Removed duplicate AccountBalances and verifyDeposit functions * Bigger delta for ezeth verifyDeposit * remove unused code and comments * cut down on sol warnings * remove more comments * Replace v1 with v2 InstanceTest * natspec for rETH overrides and InstanceTest virtuals * comments in other instances * fix build * Fix lint errors by asserting value stays the same in rETH and lsETH * address nits * update func sig for getSupply * added check for open long return value * Generalize open short with share token test case * Add open short generalization to instance test * better comments for open short cases * Add checks for receiving correct amount of tokens * Update InstanceTest.sol * Address comments
1 parent 5f83c7b commit 18dfd4a

File tree

5 files changed

+734
-836
lines changed

5 files changed

+734
-836
lines changed

test/instances/ezETH/EzETHHyperdrive.t.sol

Lines changed: 103 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,30 @@ contract EzETHHyperdriveTest is InstanceTest {
9191
/// @dev Converts base amount to the equivalent about in EzETH.
9292
function convertToShares(
9393
uint256 baseAmount
94-
) internal view override returns (uint256 shareAmount) {
94+
) internal view override returns (uint256) {
9595
// Get protocol state information used for calculating shares.
9696
(uint256 sharePrice, , ) = getSharePrice();
9797
return baseAmount.divDown(sharePrice);
9898
}
9999

100+
/// @dev Converts share amount to the equivalent amount in ETH.
101+
function convertToBase(
102+
uint256 baseAmount
103+
) internal view override returns (uint256) {
104+
// Get the total TVL priced in ETH from RestakeManager.
105+
(, , uint256 totalTVL) = RESTAKE_MANAGER.calculateTVLs();
106+
107+
// Get the total supply of the ezETH token.
108+
uint256 totalSupply = EZETH.totalSupply();
109+
110+
return
111+
RENZO_ORACLE.calculateRedeemAmount(
112+
baseAmount,
113+
totalSupply,
114+
totalTVL
115+
);
116+
}
117+
100118
/// @dev Deploys the EzETH deployer coordinator contract.
101119
function deployCoordinator() internal override returns (address) {
102120
vm.startPrank(alice);
@@ -114,6 +132,88 @@ contract EzETHHyperdriveTest is InstanceTest {
114132
);
115133
}
116134

135+
/// @dev Fetches the token balance information of an account.
136+
function getTokenBalances(
137+
address account
138+
) internal view override returns (uint256, uint256) {
139+
// EzETH does not have a convenient function for fetching base balance.
140+
return (0, EZETH.balanceOf(account));
141+
}
142+
143+
/// @dev Fetches the total supply of the base and share tokens.
144+
function getSupply() internal view override returns (uint256, uint256) {
145+
(, uint256 totalPooledEther, ) = getSharePrice();
146+
return (totalPooledEther, EZETH.totalSupply());
147+
}
148+
149+
/// @dev Verifies that deposit accounting is correct when opening positions.
150+
function verifyDeposit(
151+
address trader,
152+
uint256 basePaid,
153+
bool asBase,
154+
uint256 totalBaseSupplyBefore,
155+
uint256 totalSharesBefore,
156+
AccountBalances memory traderBalancesBefore,
157+
AccountBalances memory hyperdriveBalancesBefore
158+
) internal override {
159+
if (asBase) {
160+
// Ensure that the amount of pooled ether increased by the base paid.
161+
(, uint256 totalPooledEther, ) = getSharePrice();
162+
assertEq(totalPooledEther, totalBaseSupplyBefore + basePaid);
163+
164+
// Ensure that the ETH balances were updated correctly.
165+
assertEq(
166+
address(hyperdrive).balance,
167+
hyperdriveBalancesBefore.ETHBalance
168+
);
169+
assertEq(bob.balance, traderBalancesBefore.ETHBalance - basePaid);
170+
171+
// Ensure ezETH shares were updated correctly.
172+
assertEq(
173+
EZETH.balanceOf(trader),
174+
traderBalancesBefore.sharesBalance
175+
);
176+
177+
// Ensure that the ezETH shares were updated correctly.
178+
uint256 expectedShares = RENZO_ORACLE.calculateMintAmount(
179+
totalBaseSupplyBefore,
180+
basePaid,
181+
totalSharesBefore
182+
);
183+
assertEq(EZETH.totalSupply(), totalSharesBefore + expectedShares);
184+
assertEq(
185+
EZETH.balanceOf(address(hyperdrive)),
186+
hyperdriveBalancesBefore.sharesBalance + expectedShares
187+
);
188+
assertEq(EZETH.balanceOf(bob), traderBalancesBefore.sharesBalance);
189+
} else {
190+
// Ensure that the amount of pooled ether stays the same.
191+
(, uint256 totalPooledEther, ) = getSharePrice();
192+
assertEq(totalPooledEther, totalBaseSupplyBefore);
193+
194+
// Ensure that the ETH balances were updated correctly.
195+
assertEq(
196+
address(hyperdrive).balance,
197+
hyperdriveBalancesBefore.ETHBalance
198+
);
199+
assertEq(trader.balance, traderBalancesBefore.ETHBalance);
200+
201+
// Ensure that the ezETH shares were updated correctly.
202+
uint256 expectedShares = convertToShares(basePaid);
203+
assertEq(EZETH.totalSupply(), totalSharesBefore);
204+
assertApproxEqAbs(
205+
EZETH.balanceOf(address(hyperdrive)),
206+
hyperdriveBalancesBefore.sharesBalance + expectedShares,
207+
2 // Higher tolerance due to rounding when converting back into shares.
208+
);
209+
assertApproxEqAbs(
210+
EZETH.balanceOf(trader),
211+
traderBalancesBefore.sharesBalance - expectedShares,
212+
2 // Higher tolerance due to rounding when converting back into shares.
213+
);
214+
}
215+
}
216+
117217
/// Getters ///
118218

119219
function test_getters() external {
@@ -184,74 +284,6 @@ contract EzETHHyperdriveTest is InstanceTest {
184284

185285
/// Long ///
186286

187-
function test_open_long_with_eth(uint256 basePaid) external {
188-
// Bob opens a long by depositing ETH.
189-
basePaid = basePaid.normalizeToRange(
190-
2 * hyperdrive.getPoolConfig().minimumTransactionAmount,
191-
HyperdriveUtils.calculateMaxLong(hyperdrive)
192-
);
193-
194-
// Ensure that we get an UnsupportedToken error. Opening positions
195-
// with ETH are not allowed right now. There is a great enough
196-
// precision loss when minting ezeth that warrants some investigation
197-
// before we can turn this on. Until then, we can zap ezeth into the
198-
// pool.
199-
vm.expectRevert(IHyperdrive.NotPayable.selector);
200-
hyperdrive.openLong{ value: basePaid }(
201-
basePaid,
202-
0, // min bond proceeds
203-
0, // min vault share price
204-
IHyperdrive.Options({
205-
destination: bob,
206-
asBase: true,
207-
extraData: new bytes(0)
208-
})
209-
);
210-
}
211-
212-
function test_open_long_with_ezeth(uint256 basePaid) external {
213-
vm.stopPrank();
214-
vm.startPrank(bob);
215-
216-
// Get some balance information before the deposit.
217-
(
218-
,
219-
uint256 totalPooledEtherBefore,
220-
uint256 totalSharesBefore
221-
) = getSharePrice();
222-
AccountBalances memory bobBalancesBefore = getAccountBalances(bob);
223-
AccountBalances memory hyperdriveBalancesBefore = getAccountBalances(
224-
address(hyperdrive)
225-
);
226-
227-
// Calculate the maximum amount of basePaid we can test.
228-
uint256 maxLong = HyperdriveUtils.calculateMaxLong(hyperdrive);
229-
uint256 maxEzEth = EZETH.balanceOf(address(bob));
230-
uint256 maxRange = maxLong > maxEzEth ? maxEzEth : maxLong;
231-
basePaid = basePaid.normalizeToRange(
232-
2 * hyperdrive.getPoolConfig().minimumTransactionAmount,
233-
maxRange
234-
);
235-
236-
// Convert to shares and approve hyperdrive.
237-
uint256 sharesPaid = getAndApproveShares(basePaid);
238-
239-
// Open the position.
240-
openLong(bob, sharesPaid, false);
241-
242-
// Ensure that Renzo's aggregates and the token balances were updated
243-
// correctly during the trade.
244-
verifyDeposit(
245-
bob,
246-
basePaid,
247-
false,
248-
totalPooledEtherBefore,
249-
totalSharesBefore,
250-
bobBalancesBefore,
251-
hyperdriveBalancesBefore
252-
);
253-
}
254-
255287
function test_close_long_with_eth(uint256 basePaid) external {
256288
vm.stopPrank();
257289
vm.startPrank(bob);
@@ -364,88 +396,6 @@ contract EzETHHyperdriveTest is InstanceTest {
364396

365397
// /// Short ///
366398

367-
function test_open_short_with_eth(uint256 shortAmount) external {
368-
vm.stopPrank();
369-
vm.startPrank(bob);
370-
371-
// Bob opens a short by depositing ETH.
372-
shortAmount = shortAmount.normalizeToRange(
373-
2 * hyperdrive.getPoolConfig().minimumTransactionAmount,
374-
HyperdriveUtils.calculateMaxShort(hyperdrive)
375-
);
376-
377-
// Ensure that we get an UnsupportedToken error. Opening positions
378-
// with ETH are not allowed right now. There is a great enough
379-
// precision loss when minting ezeth that warrants some investigation
380-
// before we can turn this on. Until then, we can zap ezeth into the
381-
// pool.
382-
vm.expectRevert(IHyperdrive.NotPayable.selector);
383-
hyperdrive.openShort{ value: shortAmount }(
384-
shortAmount,
385-
shortAmount,
386-
0,
387-
IHyperdrive.Options({
388-
destination: bob,
389-
asBase: true,
390-
extraData: new bytes(0)
391-
})
392-
);
393-
}
394-
395-
function test_open_short_with_ezeth(uint256 shortAmount) external {
396-
vm.stopPrank();
397-
vm.startPrank(bob);
398-
399-
// Get some balance information before the deposit.
400-
(
401-
,
402-
uint256 totalPooledEtherBefore,
403-
uint256 totalSharesBefore
404-
) = getSharePrice();
405-
AccountBalances memory bobBalancesBefore = getAccountBalances(bob);
406-
AccountBalances memory hyperdriveBalancesBefore = getAccountBalances(
407-
address(hyperdrive)
408-
);
409-
410-
// Calculate the maximum amount we can short.
411-
shortAmount = shortAmount.normalizeToRange(
412-
2 * hyperdrive.getPoolConfig().minimumTransactionAmount,
413-
HyperdriveUtils.calculateMaxShort(hyperdrive)
414-
);
415-
416-
// Bob opens a short by depositing ezETH.
417-
EZETH.approve(address(hyperdrive), shortAmount);
418-
(, uint256 sharesPaid) = openShort(bob, shortAmount, false);
419-
420-
// Get the base Bob paid for the short.
421-
(, uint256 totalPooledEther, uint256 totalShares) = getSharePrice();
422-
uint256 basePaid = sharesPaid.mulDivDown(totalPooledEther, totalShares);
423-
424-
// Ensure that the amount of base paid by the short is reasonable.
425-
uint256 realizedRate = HyperdriveUtils.calculateAPRFromRealizedPrice(
426-
shortAmount - basePaid,
427-
shortAmount,
428-
1e18
429-
);
430-
assertGt(basePaid, 0);
431-
assertGe(
432-
realizedRate,
433-
FIXED_RATE.mulDown(POSITION_DURATION_15_DAYS.divDown(365 days))
434-
);
435-
436-
// Ensure that Renzo's aggregates and the token balances were updated
437-
// correctly during the trade.
438-
verifyDeposit(
439-
bob,
440-
basePaid,
441-
false,
442-
totalPooledEtherBefore,
443-
totalSharesBefore,
444-
bobBalancesBefore,
445-
hyperdriveBalancesBefore
446-
);
447-
}
448-
449399
function test_open_short_refunds() external {
450400
vm.startPrank(bob);
451401

@@ -651,76 +601,6 @@ contract EzETHHyperdriveTest is InstanceTest {
651601
);
652602
}
653603

654-
function verifyDeposit(
655-
address trader,
656-
uint256 basePaid,
657-
bool asBase,
658-
uint256 totalPooledEtherBefore,
659-
uint256 totalSharesBefore,
660-
AccountBalances memory traderBalancesBefore,
661-
AccountBalances memory hyperdriveBalancesBefore
662-
) internal {
663-
if (asBase) {
664-
// Ensure that the amount of pooled ether increased by the base paid.
665-
(, uint256 totalPooledEther, ) = getSharePrice();
666-
assertEq(totalPooledEther, totalPooledEtherBefore + basePaid);
667-
668-
// Ensure that the ETH balances were updated correctly.
669-
assertEq(
670-
address(hyperdrive).balance,
671-
hyperdriveBalancesBefore.ETHBalance
672-
);
673-
assertEq(bob.balance, traderBalancesBefore.ETHBalance - basePaid);
674-
675-
// Ensure ezETH shares were updated correctly.
676-
assertEq(
677-
EZETH.balanceOf(trader),
678-
traderBalancesBefore.ezethBalance
679-
);
680-
681-
// Ensure that the ezETH shares were updated correctly.
682-
uint256 expectedShares = RENZO_ORACLE.calculateMintAmount(
683-
totalPooledEtherBefore,
684-
basePaid,
685-
totalSharesBefore
686-
);
687-
assertEq(EZETH.totalSupply(), totalSharesBefore + expectedShares);
688-
assertEq(
689-
EZETH.balanceOf(address(hyperdrive)),
690-
hyperdriveBalancesBefore.ezethBalance + expectedShares
691-
);
692-
assertEq(EZETH.balanceOf(bob), traderBalancesBefore.ezethBalance);
693-
} else {
694-
// Ensure that the amount of pooled ether stays the same.
695-
(, uint256 totalPooledEther, ) = getSharePrice();
696-
assertEq(totalPooledEther, totalPooledEtherBefore);
697-
698-
// Ensure that the ETH balances were updated correctly.
699-
assertEq(
700-
address(hyperdrive).balance,
701-
hyperdriveBalancesBefore.ETHBalance
702-
);
703-
assertEq(trader.balance, traderBalancesBefore.ETHBalance);
704-
705-
// Ensure that the ezETH shares were updated correctly.
706-
uint256 expectedShares = basePaid.mulDivDown(
707-
totalSharesBefore,
708-
totalPooledEtherBefore
709-
);
710-
assertEq(EZETH.totalSupply(), totalSharesBefore);
711-
assertApproxEqAbs(
712-
EZETH.balanceOf(address(hyperdrive)),
713-
hyperdriveBalancesBefore.ezethBalance + expectedShares,
714-
1
715-
);
716-
assertApproxEqAbs(
717-
EZETH.balanceOf(trader),
718-
traderBalancesBefore.ezethBalance - expectedShares,
719-
1
720-
);
721-
}
722-
}
723-
724604
function verifyEzethWithdrawal(
725605
address trader,
726606
uint256 baseProceeds,
@@ -748,12 +628,12 @@ contract EzETHHyperdriveTest is InstanceTest {
748628
);
749629
assertApproxEqAbs(
750630
EZETH.balanceOf(address(hyperdrive)),
751-
hyperdriveBalancesBefore.ezethBalance - expectedShares,
631+
hyperdriveBalancesBefore.sharesBalance - expectedShares,
752632
1
753633
);
754634
assertApproxEqAbs(
755635
EZETH.balanceOf(trader),
756-
traderBalancesBefore.ezethBalance + expectedShares,
636+
traderBalancesBefore.sharesBalance + expectedShares,
757637
1
758638
);
759639
}
@@ -804,21 +684,6 @@ contract EzETHHyperdriveTest is InstanceTest {
804684
}
805685
}
806686

807-
struct AccountBalances {
808-
uint256 ezethBalance;
809-
uint256 ETHBalance;
810-
}
811-
812-
function getAccountBalances(
813-
address account
814-
) internal view returns (AccountBalances memory) {
815-
return
816-
AccountBalances({
817-
ezethBalance: EZETH.balanceOf(account),
818-
ETHBalance: account.balance
819-
});
820-
}
821-
822687
// returns share price information.
823688
function getSharePrice()
824689
internal

0 commit comments

Comments
 (0)