-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathSwapExactTokensForTokens.t.sol
More file actions
198 lines (155 loc) · 9.12 KB
/
Copy pathSwapExactTokensForTokens.t.sol
File metadata and controls
198 lines (155 loc) · 9.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
// Test
import {Fork_Shared_Test} from "test/fork/EthenaARM/shared/Shared.sol";
// Contracts
import {AbstractARM} from "contracts/AbstractARM.sol";
import {EthenaARM} from "contracts/EthenaARM.sol";
// Interfaces
import {IERC20} from "contracts/Interfaces.sol";
contract Fork_Concrete_EthenaARM_swapExactTokensForTokens_Test_ is Fork_Shared_Test {
uint256 public AMOUNT_IN = 100 ether;
//////////////////////////////////////////////////////
/// --- TESTS
//////////////////////////////////////////////////////
function test_swapExactTokensForTokens_USDE_To_SUSDE_Sig1() public {
// Record balances before swap
uint256 usdeBalanceBefore = usde.balanceOf(address(this));
uint256 susdeBalanceBefore = susde.balanceOf(address(this));
// Precompute expected amount out
uint256 traderate = _sellPrice();
uint256 expectedAmountOut = (susde.convertToShares(AMOUNT_IN) * 1e36) / traderate;
// Expected events
vm.expectEmit({emitter: address(usde)});
emit IERC20.Transfer(address(this), address(ethenaARM), AMOUNT_IN);
vm.expectEmit({emitter: address(susde)});
emit IERC20.Transfer(address(ethenaARM), address(this), expectedAmountOut);
// Perform the swap
uint256[] memory obtained =
ethenaARM.swapExactTokensForTokens(usde, IERC20(address(susde)), AMOUNT_IN, 0, address(this));
// Record balances after swap
uint256 usdeBalanceAfter = usde.balanceOf(address(this));
uint256 susdeBalanceAfter = susde.balanceOf(address(this));
// Assertions
assertEq(obtained[0], AMOUNT_IN, "Obtained USDe amount should match input");
assertEq(obtained[1], expectedAmountOut, "Obtained SUSDe amount should match expected output");
assertEq(usdeBalanceBefore, usdeBalanceAfter + AMOUNT_IN, "USDe balance should have decreased");
assertEq(susdeBalanceAfter, susdeBalanceBefore + expectedAmountOut, "SUSDe balance should have increased");
}
function test_swapExactTokensForTokens_USDE_To_SUSDE_Sig2() public {
// Record balances before swap
uint256 usdeBalanceBefore = usde.balanceOf(address(this));
uint256 susdeBalanceBefore = susde.balanceOf(address(this));
// Precompute expected amount out
uint256 traderate = _sellPrice();
uint256 expectedAmountOut = (susde.convertToShares(AMOUNT_IN) * 1e36) / traderate;
// Expected events
vm.expectEmit({emitter: address(usde)});
emit IERC20.Transfer(address(this), address(ethenaARM), AMOUNT_IN);
vm.expectEmit({emitter: address(susde)});
emit IERC20.Transfer(address(ethenaARM), address(this), expectedAmountOut);
// Perform the swap
address[] memory path = new address[](2);
path[0] = address(usde);
path[1] = address(susde);
uint256[] memory obtained =
ethenaARM.swapExactTokensForTokens(AMOUNT_IN, 0, path, address(this), block.timestamp + 1 hours);
// Record balances after swap
uint256 usdeBalanceAfter = usde.balanceOf(address(this));
uint256 susdeBalanceAfter = susde.balanceOf(address(this));
// Assertions
assertEq(obtained[0], AMOUNT_IN, "Obtained USDe amount should match input");
assertEq(obtained[1], expectedAmountOut, "Obtained SUSDe amount should match expected output");
assertEq(usdeBalanceBefore, usdeBalanceAfter + AMOUNT_IN, "USDe balance should have decreased");
assertEq(susdeBalanceAfter, susdeBalanceBefore + expectedAmountOut, "SUSDe balance should have increased");
}
function test_swapExactTokensForTokens_SUSDE_To_USDE_NoOutstandingWithdrawals_Sig1() public {
// Record balances before swap
uint256 usdeBalanceBefore = usde.balanceOf(address(this));
uint256 susdeBalanceBefore = susde.balanceOf(address(this));
uint256 feesAccruedBefore = ethenaARM.feesAccrued();
// Precompute expected amount out
uint256 traderate = _buyPrice();
uint256 expectedAmountOut = (susde.convertToAssets(AMOUNT_IN) * traderate) / 1e36;
uint256 expectedFee = _expectedBuySideFee(AMOUNT_IN, expectedAmountOut);
// Expected events
vm.expectEmit({emitter: address(susde)});
emit IERC20.Transfer(address(this), address(ethenaARM), AMOUNT_IN);
vm.expectEmit({emitter: address(usde)});
emit IERC20.Transfer(address(ethenaARM), address(this), expectedAmountOut);
// Perform the swap
uint256[] memory obtained =
ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, AMOUNT_IN, 0, address(this));
// Record balances after swap
uint256 usdeBalanceAfter = usde.balanceOf(address(this));
uint256 susdeBalanceAfter = susde.balanceOf(address(this));
// Assertions
assertEq(obtained[0], AMOUNT_IN, "Obtained SUSDe amount should match input");
assertEq(obtained[1], expectedAmountOut, "Obtained USDe amount should match expected output");
assertEq(usdeBalanceAfter, usdeBalanceBefore + expectedAmountOut, "USDe balance should have increased");
assertEq(susdeBalanceBefore, susdeBalanceAfter + AMOUNT_IN, "SUSDe balance should have decreased");
assertEq(ethenaARM.feesAccrued() - feesAccruedBefore, expectedFee, "Fees accrued should match realized gain");
}
function test_swapExactTokensForTokens_SUSDE_To_USDE_WithOutstandingWithdrawals_Sig1() public {
ethenaARM.requestRedeem(AMOUNT_IN);
// Precompute expected amount out
uint256 traderate = _buyPrice();
uint256 expectedAmountOut = (susde.convertToAssets(AMOUNT_IN) * traderate) / 1e36;
// Perform the swap
uint256[] memory obtained =
ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, AMOUNT_IN, 0, address(this));
// Assertions
assertEq(obtained[0], AMOUNT_IN, "Obtained SUSDe amount should match input");
assertEq(obtained[1], expectedAmountOut, "Obtained USDe amount should match expected output");
}
//////////////////////////////////////////////////////
/// --- REVERTING TESTS
//////////////////////////////////////////////////////
function test_RevertWhen_swapExactTokensForTokens_Because_InvalidInToken() public {
vm.expectRevert(AbstractARM.InvalidSwapAssets.selector);
ethenaARM.swapExactTokensForTokens(badToken, usde, AMOUNT_IN, 0, address(this));
}
function test_RevertWhen_swapExactTokensForTokens_Because_InvalidOutToken() public {
vm.expectRevert(AbstractARM.InvalidSwapAssets.selector);
ethenaARM.swapExactTokensForTokens(usde, badToken, AMOUNT_IN, 0, address(this));
vm.expectRevert(AbstractARM.InvalidSwapAssets.selector);
ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), badToken, AMOUNT_IN, 0, address(this));
}
function test_RevertWhen_swapExactTokensForTokens_Because_InsufficientOutputAmount() public {
uint256 highMinAmountOut = 1_000_000 ether;
vm.expectRevert(AbstractARM.InsufficientOutputAmount.selector);
ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, AMOUNT_IN, highMinAmountOut, address(this));
vm.expectRevert(AbstractARM.InsufficientOutputAmount.selector);
ethenaARM.swapExactTokensForTokens(usde, IERC20(address(susde)), AMOUNT_IN, highMinAmountOut, address(this));
address[] memory path = new address[](2);
path[0] = address(usde);
path[1] = address(susde);
vm.expectRevert(AbstractARM.InsufficientOutputAmount.selector);
ethenaARM.swapExactTokensForTokens(AMOUNT_IN, highMinAmountOut, path, address(this), block.timestamp + 1 hours);
}
function test_RevertWhen_swapExactTokensForTokens_Because_DeadlineExpired() public {
uint256 pastDeadline = block.timestamp - 1;
address[] memory path = new address[](2);
path[0] = address(susde);
path[1] = address(usde);
vm.expectRevert(AbstractARM.DeadlineExpired.selector);
ethenaARM.swapExactTokensForTokens(AMOUNT_IN, 0, path, address(this), pastDeadline);
}
function test_RevertWhen_swapExactTokensForTokens_Because_InvalidePathLength() public {
address[] memory shortPath = new address[](1);
shortPath[0] = address(susde);
vm.expectRevert(AbstractARM.InvalidPathLength.selector);
ethenaARM.swapExactTokensForTokens(AMOUNT_IN, 0, shortPath, address(this), block.timestamp + 1 hours);
address[] memory longPath = new address[](3);
longPath[0] = address(susde);
longPath[1] = address(usde);
longPath[2] = address(susde);
vm.expectRevert(AbstractARM.InvalidPathLength.selector);
ethenaARM.swapExactTokensForTokens(AMOUNT_IN, 0, longPath, address(this), block.timestamp + 1 hours);
}
function test_RevertWhen_swapExactTokensForTokens_Because_InsufficientLiquidity() public {
ethenaARM.requestRedeem(ethenaARM.balanceOf(address(this)));
vm.expectRevert(AbstractARM.InsufficientLiquidity.selector);
ethenaARM.swapExactTokensForTokens(IERC20(address(susde)), usde, AMOUNT_IN, 0, address(this));
}
}