Skip to content

Commit c5ee703

Browse files
added Sxc and refactored the redeemCollateral handlers with detailed natspec
1 parent 11a0f40 commit c5ee703

2 files changed

Lines changed: 99 additions & 7 deletions

File tree

src/SXCEngine.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol
55
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
66
import {StableXCoin} from "./StableXCoin.sol";
77
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
8+
89
import "../utils/SafeMath.sol"; // Assuming SafeMath is correctly implemented and available
910

1011
/**

test/fuzz/Handler.t.sol

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pragma solidity 0.8.24;
6464
import {Test} from "forge-std/Test.sol";
6565
import {SXCEngine} from "../../src/SXCEngine.sol";
6666
import {StableXCoin} from "../../src/StableXCoin.sol";
67-
import {ERC20Mock} from "@openzeppelin/contracts/mocks/ERC20Mock.sol";
67+
import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol";
6868

6969
contract Handler is Test {
7070
// ========================
@@ -77,6 +77,8 @@ contract Handler is Test {
7777
ERC20Mock public weth;
7878
ERC20Mock public wbtc;
7979

80+
uint256 MAX_DEPOSIT_SIZE = type(uint96).max;
81+
8082
/// @notice Initializes the handler with the deployed engine and stablecoin
8183
/// @param _engine The SXCEngine contract to be tested
8284
/// @param _sxc The StableXCoin token contract
@@ -98,16 +100,105 @@ contract Handler is Test {
98100
/// @param collateralSeed Determines which token to use (WETH or WBTC)
99101
/// @param amountCollateral Amount of tokens to deposit
100102
function depositCollateral(uint256 collateralSeed, uint256 amountCollateral) public {
101-
ERC20Mock collateral = _getCollateralFromSeed(collateralSeed);
103+
ERC20Mock collateral = _getCollateralFromSeed(collateralSeed);
104+
uint256 clampedAmount = bound(amountCollateral, 1, MAX_DEPOSIT_SIZE);
105+
106+
vm.startPrank(msg.sender);
107+
collateral.mint(msg.sender, clampedAmount);
108+
collateral.approve(address(engine), clampedAmount);
109+
engine.depositCollateral(address(collateral), clampedAmount);
110+
vm.stopPrank();
111+
}
112+
113+
114+
/// @notice Attempts to mint StableXCoin (SXC) for the caller based on their collateral value
115+
/// @dev This function is used during fuzz testing to simulate minting. It ensures the user can only mint
116+
/// up to 50% of their collateral value in USD to maintain overcollateralization (e.g., 200% collateral ratio).
117+
/// If the user has already minted the maximum or has insufficient collateral, the function exits safely.
118+
/// @param amount The amount of StableXCoin (SXC) the user attempts to mint. This is bounded internally.
119+
/// @custom:invariant The function avoids minting if it would violate the collateralization ratio.
120+
function minSxc(uint256 amount) public {
121+
// Clamp the initial amount to prevent unrealistic fuzzing input
122+
amount = bound(amount, 1, MAX_DEPOSIT_SIZE);
123+
124+
// Get how much SXC the user has minted and how much collateral (in USD) they have deposited
125+
(uint256 totalSxcMinted, uint256 collateralValueInUsd) = engine.getAccountInformation(msg.sender);
126+
127+
// Calculate the maximum amount of SXC the user is allowed to mint (200% collateralization ratio)
128+
int256 maxSxcToMint = (int256(collateralValueInUsd) / 2) - int256(totalSxcMinted);
129+
130+
// If the user is over their mint limit, exit safely
131+
if (maxSxcToMint < 0) {
132+
return;
133+
}
102134

103-
// Mint tokens to the caller so they can deposit
104-
collateral.mint(address(this), amountCollateral);
135+
// Clamp the mint amount to not exceed what they're allowed to mint
136+
amount = bound(amount, 0, uint256(maxSxcToMint));
105137

106-
// Approve and deposit to the engine
107-
collateral.approve(address(engine), amountCollateral);
108-
engine.depositCollateral(address(collateral), amountCollateral);
138+
// If the result is 0, skip the minting
139+
if (amount == 0) {
140+
return;
109141
}
110142

143+
// Simulate a user calling mint on the engine
144+
vm.startPrank(msg.sender);
145+
engine.minSxc(amount);
146+
vm.stopPrank();
147+
}
148+
149+
150+
// function depositCollateral(uint256 collateralSeed, uint256 amountCollateral) public {
151+
// ERC20Mock collateral = _getCollateralFromSeed(collateralSeed);
152+
// amountCollateral = bound(amountCollateral, 1, MAX_DEPOSIT_SIZE);
153+
154+
// // Mint tokens to the caller so they can deposit
155+
// collateral.mint(address(this), amountCollateral);
156+
157+
// // Approve and deposit to the engine
158+
// collateral.approve(address(engine), amountCollateral);
159+
// engine.depositCollateral(address(collateral), amountCollateral);
160+
// }
161+
162+
// function redeemCollateral(uint256 collateralSeed, uint256 amountCollateral){
163+
// ERC20Mock collateral = _getCollateralFromSeed(collateralSeed);
164+
// uint256 maxCollateralToRedeem = engine.getCollateralBalanceOfUser(address(collateral), msg.sender);
165+
// amountCollateral = bound(amountCollateral, 1, maxCollateralToRedeem);
166+
// if (amountCollateral == 0 ){
167+
// return;
168+
// }
169+
// engine.redeemCollateral(address(colateral), amountCollateral);
170+
// }
171+
172+
173+
174+
/// @notice Simulates redeeming collateral (WETH or WBTC) from the SXCEngine by the caller
175+
/// @dev Used during fuzz testing to simulate user withdrawals. The function ensures that the user
176+
/// only attempts to redeem an amount they have deposited (tracked by the engine).
177+
/// If the user has no redeemable balance or attempts to redeem zero, the function exits gracefully.
178+
/// @param collateralSeed Determines which collateral token to use (e.g., even = WETH, odd = WBTC)
179+
/// @param amountCollateral The amount of collateral to attempt to redeem. Bounded internally to a safe range.
180+
/// @custom:invariant The function avoids redeeming more than the user's tracked collateral balance in the engine.
181+
function redeemCollateral(uint256 collateralSeed, uint256 amountCollateral) public {
182+
// Get WETH or WBTC based on the seed value
183+
ERC20Mock collateral = _getCollateralFromSeed(collateralSeed);
184+
185+
// Query the max amount of this token the user has in the engine
186+
uint256 maxCollateralToRedeem = engine.getCollateralBalanceOfUser(address(collateral), msg.sender);
187+
188+
// Clamp the amount to a valid, redeemable range
189+
uint256 clampedAmount = bound(amountCollateral, 1, maxCollateralToRedeem);
190+
191+
// If they have nothing to redeem or fuzzed to 0, exit early
192+
if (clampedAmount == 0) return;
193+
194+
// Simulate the user calling redeem
195+
vm.startPrank(msg.sender);
196+
engine.redeemCollateral(address(collateral), clampedAmount);
197+
vm.stopPrank();
198+
}
199+
200+
201+
111202
// ========================
112203
// HELPERS
113204
// ========================

0 commit comments

Comments
 (0)