Skip to content

Commit e459e00

Browse files
committed
add randomized sequence
1 parent fd3aa4f commit e459e00

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

packages/ethereum-contracts/test/foundry/superfluid/yieldbackend/AaveYieldBackendIntegration.t.sol

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,5 +325,140 @@ contract AaveYieldBackendIntegrationTest is Test {
325325
// 2 operations: enable deposits + withdraw surplus
326326
_verifyInvariants(false, 2);
327327
}
328+
329+
/*//////////////////////////////////////////////////////////////////////////
330+
Random Sequence Fuzz Tests
331+
//////////////////////////////////////////////////////////////////////////*/
332+
333+
struct YieldBackendStep {
334+
uint8 a; // action type: 0 enable, 1 disable, 2 switch, 3 upgrade, 4 downgrade, 5 withdraw surplus
335+
uint32 v; // action param (amount for upgrade/downgrade, unused for others)
336+
uint16 dt; // time delta (for yield accrual simulation)
337+
}
338+
339+
/// @notice Test random sequence of yield backend operations
340+
/// @dev Simulates real-world usage patterns with appropriate frequency distribution
341+
function testRandomYieldBackendSequence(YieldBackendStep[20] memory steps) external {
342+
// Track state
343+
bool backendEnabled = false;
344+
bool initialExcessPreserved = true; // Track if initial excess has been withdrawn via surplus
345+
uint256 numAaveOperations = 0;
346+
AaveYieldBackend currentBackend = aaveBackend;
347+
348+
for (uint256 i = 0; i < steps.length; ++i) {
349+
YieldBackendStep memory s = steps[i];
350+
uint256 action = s.a % 20; // Use modulo 20 for frequency distribution
351+
352+
// Action frequency distribution:
353+
// 0: Enable (5%)
354+
// 1: Disable (5%)
355+
// 2: Switch (5%)
356+
// 3-16: Upgrade/Downgrade (70%, split evenly: 3-9 upgrade, 10-16 downgrade)
357+
// 17-19: Withdraw surplus (15%)
358+
359+
if (action == 0) {
360+
// Enable yield backend (5% frequency)
361+
if (!backendEnabled) {
362+
vm.startPrank(ADMIN);
363+
superToken.enableYieldBackend(currentBackend);
364+
vm.stopPrank();
365+
backendEnabled = true;
366+
numAaveOperations += 1; // enable deposits all existing underlying
367+
}
368+
} else if (action == 1) {
369+
// Disable yield backend (5% frequency)
370+
if (backendEnabled) {
371+
vm.startPrank(ADMIN);
372+
superToken.disableYieldBackend();
373+
vm.stopPrank();
374+
backendEnabled = false;
375+
numAaveOperations += 1; // disable withdraws max
376+
}
377+
} else if (action == 2) {
378+
// Switch yield backend: disable current, enable new (5% frequency)
379+
if (backendEnabled) {
380+
// Disable current
381+
vm.startPrank(ADMIN);
382+
superToken.disableYieldBackend();
383+
vm.stopPrank();
384+
numAaveOperations += 1; // disable withdraws
385+
386+
// Deploy and enable new backend
387+
AaveYieldBackend newBackend = new AaveYieldBackend(
388+
IERC20(USDC),
389+
IPool(AAVE_POOL),
390+
SURPLUS_RECEIVER
391+
);
392+
vm.startPrank(ADMIN);
393+
superToken.enableYieldBackend(newBackend);
394+
vm.stopPrank();
395+
currentBackend = newBackend;
396+
numAaveOperations += 1; // enable deposits
397+
}
398+
} else if (action >= 3 && action <= 9) {
399+
// Upgrade (35% frequency)
400+
if (backendEnabled) {
401+
// Bound upgrade amount to reasonable range
402+
uint256 upgradeAmount = bound(uint256(s.v), 1e18, 1_000_000 * 1e18);
403+
vm.startPrank(ALICE);
404+
superToken.upgrade(upgradeAmount);
405+
vm.stopPrank();
406+
numAaveOperations += 1; // upgrade deposits
407+
}
408+
} else if (action >= 10 && action <= 16) {
409+
// Downgrade (35% frequency)
410+
if (backendEnabled) {
411+
uint256 aliceBalance = superToken.balanceOf(ALICE);
412+
if (aliceBalance > 0) {
413+
// Bound downgrade amount to available balance
414+
uint256 downgradeAmount = bound(uint256(s.v), 1e18, aliceBalance);
415+
// Don't downgrade more than available
416+
if (downgradeAmount > aliceBalance) {
417+
downgradeAmount = aliceBalance;
418+
}
419+
vm.startPrank(ALICE);
420+
superToken.downgrade(downgradeAmount);
421+
vm.stopPrank();
422+
numAaveOperations += 1; // downgrade withdraws
423+
}
424+
}
425+
} else if (action >= 17 && action <= 19) {
426+
// Withdraw surplus (15% frequency)
427+
if (backendEnabled) {
428+
// Check if there's surplus to withdraw
429+
uint256 underlyingBalance = IERC20(USDC).balanceOf(address(superToken));
430+
uint256 aTokenBalance = IERC20(A_USDC).balanceOf(address(superToken));
431+
(uint256 normalizedTotalSupply,) = superToken.toUnderlyingAmount(superToken.totalSupply());
432+
uint256 totalAssets = underlyingBalance + aTokenBalance;
433+
434+
// Only withdraw if there's actual surplus (after 100 wei margin)
435+
if (totalAssets > normalizedTotalSupply + 100) {
436+
vm.startPrank(ADMIN);
437+
superToken.withdrawSurplusFromYieldBackend();
438+
vm.stopPrank();
439+
numAaveOperations += 1; // withdraw surplus
440+
// After withdrawing surplus, initial excess is also withdrawn
441+
initialExcessPreserved = false;
442+
}
443+
}
444+
}
445+
446+
// Warp time to simulate yield accrual (if dt > 0)
447+
if (s.dt > 0) {
448+
// Bound time warp to reasonable range (1 hour to 30 days)
449+
uint256 timeWarp = bound(uint256(s.dt), 1 hours, 30 days);
450+
vm.warp(block.timestamp + timeWarp);
451+
}
452+
453+
// Verify invariants after each step
454+
// Initial excess should be preserved only if backend is enabled AND surplus hasn't been withdrawn
455+
bool preserveInitialExcess = backendEnabled && initialExcessPreserved;
456+
_verifyInvariants(preserveInitialExcess, numAaveOperations);
457+
}
458+
459+
// Final invariant check
460+
bool finalPreserveInitialExcess = backendEnabled && initialExcessPreserved;
461+
_verifyInvariants(finalPreserveInitialExcess, numAaveOperations);
462+
}
328463
}
329464

0 commit comments

Comments
 (0)