@@ -15,6 +15,7 @@ import { IPool } from "aave-v3/interfaces/IPool.sol";
1515 */
1616contract SuperTokenYieldForkTest is Test {
1717 address constant ALICE = address (0x420 );
18+ address constant ADMIN = address (0xAAA );
1819
1920 // Base network constants
2021 uint256 internal constant CHAIN_ID = 8453 ;
@@ -37,11 +38,6 @@ contract SuperTokenYieldForkTest is Test {
3738 /// @notice Aave V3 Pool contract
3839 IPool public aavePool;
3940
40- /// @notice Admin address (this contract)
41- address public admin;
42- /// @notice Test user address
43- address public user;
44-
4541 /// @notice Set up the test environment by forking Base and deploying AaveYieldBackend
4642 function setUp () public {
4743 // Fork Base using public RPC
@@ -50,10 +46,6 @@ contract SuperTokenYieldForkTest is Test {
5046 // Verify we're on Base
5147 assertEq (block .chainid , CHAIN_ID, "Chainid mismatch " );
5248
53- // Initialize test accounts
54- admin = address (this );
55- user = address (0x1234 );
56-
5749 // Get Aave Pool
5850 aavePool = IPool (AAVE_POOL);
5951
@@ -77,14 +69,33 @@ contract SuperTokenYieldForkTest is Test {
7769 superToken.updateCode (address (newSuperTokenLogic));
7870 vm.stopPrank ();
7971
72+ // designate an admin for the SuperToken
73+ vm.startPrank (address (superToken.getHost ()));
74+ superToken.changeAdmin (ADMIN);
75+ vm.stopPrank ();
76+
77+ // provide ALICE with underlying and let her approve for upgrade
78+ deal (USDC, ALICE, type (uint128 ).max);
79+ vm.startPrank (ALICE);
80+ IERC20 (USDC).approve (address (superToken), type (uint256 ).max);
81+
8082 console.log ("aaveBackend address " , address (aaveBackend));
8183 }
8284
8385 function _enableYieldBackend () public {
84- vm.startPrank (address (superToken. getHost ()) );
85- superToken.setYieldBackend ( address ( aaveBackend) );
86+ vm.startPrank (ADMIN );
87+ superToken.enableYieldBackend ( aaveBackend);
8688 vm.stopPrank ();
8789 }
90+
91+ function _verifyInvariants () internal view {
92+ // underlyingBalance + aTokenBalance >= superToken.supply()
93+ uint256 underlyingBalance = IERC20 (USDC).balanceOf (address (superToken));
94+ uint256 aTokenBalance = IERC20 (aUSDC).balanceOf (address (superToken));
95+ (uint256 superTokenNormalizedSupply ,) = superToken.toUnderlyingAmount (superToken.totalSupply ());
96+
97+ assertGe (underlyingBalance + aTokenBalance, superTokenNormalizedSupply, "invariant failed: underlyingBalance + aTokenBalance insufficient " );
98+ }
8899
89100 /// @notice Test that we're forking the correct Base network
90101 function testForkBaseNetwork () public view {
@@ -116,37 +127,30 @@ contract SuperTokenYieldForkTest is Test {
116127 console.log ("aUSDC balance of SuperToken " , IERC20 (aUSDC).balanceOf (address (superToken)));
117128 // TODO: We'd want asset balance to equal aToken balance. But that's not exactly the case.
118129 // what else shall be require?
130+ _verifyInvariants ();
119131 }
120132
121133 function testDisableYieldBackend () public {
122- // store underlying balance before enabling yield backend
123- uint256 underlyingBalanceBefore = IERC20 (USDC).balanceOf (address (superToken));
124-
125134 _enableYieldBackend ();
126135
127- vm.startPrank (address (superToken. getHost ()) );
128- superToken.setYieldBackend ( address ( 0 ) );
136+ vm.startPrank (ADMIN );
137+ superToken.disableYieldBackend ( );
129138 vm.stopPrank ();
130139 assertEq (address (superToken.getYieldBackend ()), address (0 ), "Yield backend mismatch " );
131140
132141 // the SuperToken should now have a non-zero USDC balance and a zero aUSDC balance
133142 assertGt (IERC20 (USDC).balanceOf (address (superToken)), 0 , "USDC balance should be non-zero " );
134143 assertEq (IERC20 (aUSDC).balanceOf (address (superToken)), 0 , "aUSDC balance should be zero " );
135144
136- // get underlying balance after disabling yield backend
137- uint256 underlyingBalanceAfter = IERC20 (USDC).balanceOf (address (superToken));
138- //assertEq(underlyingBalanceAfter, underlyingBalanceBefore, "Underlying balance should be the same");
145+ _verifyInvariants ();
139146 }
140147
141148 // TODO: bool fuzz arg for disabled/enabled backend
142149 function testUpgradeDowngrade () public {
143150 _enableYieldBackend ();
144151
145- deal (USDC, ALICE, 1000 ether);
146-
147152 uint256 aTokenBalanceBefore = IERC20 (aUSDC).balanceOf (address (superToken));
148153 vm.startPrank (ALICE);
149- IERC20 (USDC).approve (address (superToken), type (uint256 ).max);
150154 superToken.upgrade (1 ether);
151155 vm.stopPrank ();
152156
@@ -167,7 +171,7 @@ contract SuperTokenYieldForkTest is Test {
167171 superToken.downgrade (1 ether);
168172 vm.stopPrank ();
169173
170- uint256 aTokenBalanceAfterDowngrade = IERC20 (aUSDC). balanceOf ( address (superToken) );
174+ _verifyInvariants ( );
171175 }
172176
173177 // ============ Gas Benchmarking Tests ============
@@ -176,19 +180,13 @@ contract SuperTokenYieldForkTest is Test {
176180 /// @dev Separate test function to avoid cold/warm storage slot interference
177181 function testGasUpgrade_WithoutYieldBackend () public {
178182 // Ensure yield backend is NOT set
179- vm.startPrank (address (superToken.getHost ()));
180- superToken.setYieldBackend (address (0 ));
181- vm.stopPrank ();
182183 assertEq (address (superToken.getYieldBackend ()), address (0 ), "Yield backend should not be set " );
183184
184185 // Prepare test state
185186 // 1000 USDC = 1000 * 1e6 (USDC has 6 decimals)
186187 // In SuperToken units (18 decimals), this is 1000 * 1e18
187188 uint256 upgradeAmount = 1000 * 1e18 ;
188- deal (USDC, ALICE, 1000 * 1e6 );
189189 vm.startPrank (ALICE);
190- IERC20 (USDC).approve (address (superToken), type (uint256 ).max);
191-
192190 // Measure gas for upgrade
193191 uint256 gasBefore = gasleft ();
194192 superToken.upgrade (upgradeAmount);
@@ -211,10 +209,7 @@ contract SuperTokenYieldForkTest is Test {
211209 // 1000 USDC = 1000 * 1e6 (USDC has 6 decimals)
212210 // In SuperToken units (18 decimals), this is 1000 * 1e18
213211 uint256 upgradeAmount = 1000 * 1e18 ;
214- deal (USDC, ALICE, 1000 * 1e6 );
215212 vm.startPrank (ALICE);
216- IERC20 (USDC).approve (address (superToken), type (uint256 ).max);
217-
218213 // Measure gas for upgrade
219214 uint256 gasBefore = gasleft ();
220215 superToken.upgrade (upgradeAmount);
@@ -230,17 +225,13 @@ contract SuperTokenYieldForkTest is Test {
230225 /// @dev Separate test function to avoid cold/warm storage slot interference
231226 function testGasDowngrade_WithoutYieldBackend () public {
232227 // Ensure yield backend is NOT set
233- vm.startPrank (address (superToken.getHost ()));
234- superToken.setYieldBackend (address (0 ));
235- vm.stopPrank ();
228+ assertEq (address (superToken.getYieldBackend ()), address (0 ), "Yield backend should not be set " );
236229
237230 // First, upgrade some tokens for ALICE to downgrade later
238231 // 1000 USDC = 1000 * 1e6 (USDC has 6 decimals)
239232 // In SuperToken units (18 decimals), this is 1000 * 1e18
240233 uint256 initialUpgradeAmount = 1000 * 1e18 ;
241- deal (USDC, ALICE, 1000 * 1e6 );
242234 vm.startPrank (ALICE);
243- IERC20 (USDC).approve (address (superToken), type (uint256 ).max);
244235 superToken.upgrade (initialUpgradeAmount);
245236 vm.stopPrank ();
246237
@@ -270,9 +261,7 @@ contract SuperTokenYieldForkTest is Test {
270261 // 1000 USDC = 1000 * 1e6 (USDC has 6 decimals)
271262 // In SuperToken units (18 decimals), this is 1000 * 1e18
272263 uint256 initialUpgradeAmount = 1000 * 1e18 ;
273- deal (USDC, ALICE, 1000 * 1e6 );
274264 vm.startPrank (ALICE);
275- IERC20 (USDC).approve (address (superToken), type (uint256 ).max);
276265 superToken.upgrade (initialUpgradeAmount);
277266 vm.stopPrank ();
278267
@@ -291,5 +280,45 @@ contract SuperTokenYieldForkTest is Test {
291280 console.log ("Gas used " , gasUsed);
292281 console.log ("Amount downgraded " , amountToDowngrade);
293282 }
283+
284+ function testWithdrawSurplusFromYieldBackend () public {
285+ address SURPLUS_RECEIVER = 0xac808840f02c47C05507f48165d2222FF28EF4e1 ;
286+
287+ // Simulate yield accumulation by transferring extra underlying to SuperToken
288+ uint256 surplusAmount = 100 * 1e6 ; // 100 USDC
289+ deal (USDC, address (this ), surplusAmount);
290+
291+ _enableYieldBackend ();
292+
293+ // Upgrade tokens to create supply
294+ uint256 upgradeAmount = 1000 * 1e18 ;
295+ vm.startPrank (ALICE);
296+ superToken.upgrade (upgradeAmount);
297+ vm.stopPrank ();
298+
299+ uint256 receiverBalanceBefore = IERC20 (USDC).balanceOf (SURPLUS_RECEIVER);
300+ uint256 aTokenBalanceBefore = IERC20 (aUSDC).balanceOf (address (superToken));
301+
302+ // log USDC and aUSDC balances of SuperToken
303+ console.log ("USDC balance of SuperToken " , IERC20 (USDC).balanceOf (address (superToken)));
304+ console.log ("aUSDC balance of SuperToken " , IERC20 (aUSDC).balanceOf (address (superToken)));
305+ // log normalized total supply
306+ (uint256 normalizedTotalSupply , uint256 adjustedAmount ) = superToken.toUnderlyingAmount (superToken.totalSupply ());
307+ console.log ("normalized total supply " , normalizedTotalSupply);
308+ console.log ("adjusted amount " , adjustedAmount);
309+
310+ vm.startPrank (ADMIN);
311+ superToken.withdrawSurplusFromYieldBackend ();
312+ vm.stopPrank ();
313+
314+ uint256 receiverBalanceAfter = IERC20 (USDC).balanceOf (SURPLUS_RECEIVER);
315+ uint256 aTokenBalanceAfter = IERC20 (aUSDC).balanceOf (address (superToken));
316+ console.log ("aToken balance after " , aTokenBalanceAfter);
317+ console.log ("aToken balance diff " , aTokenBalanceBefore - aTokenBalanceAfter);
318+
319+ assertGt (receiverBalanceAfter, receiverBalanceBefore, "Surplus should be withdrawn to receiver " );
320+ assertLt (aTokenBalanceAfter, aTokenBalanceBefore, "aToken balance should decrease " );
321+ _verifyInvariants ();
322+ }
294323}
295324
0 commit comments