pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Stratax} from "../../src/Stratax.sol";
import {StrataxOracle} from "../../src/StrataxOracle.sol";
import {IERC20} from "forge-std/interfaces/IERC20.sol";
* @title Practical Minimal POC
* @notice Bug Bounty: Multiple Positions Overlap - No Position Tracking
*/
contract PracticalMinimalPOC is Test {
Stratax public stratax;
StrataxOracle public oracle;
address aavePool = address(0x100);
address aaveDataProvider = address(0x101);
address oneInchRouter = address(0x102);
address usdc = address(0x200);
address weth = address(0x201);
address debtToken = address(0x402);
uint256 position1DebtAmount = 0.66e18;
uint256 position2DebtAmount = 0.4e18;
function setUp() public {
oracle = new StrataxOracle();
stratax = new Stratax();
stratax.initialize(aavePool, aaveDataProvider, oneInchRouter, usdc, address(oracle));
vm.mockCall(address(0x300), abi.encodeWithSignature("latestRoundData()"),
abi.encode(uint80(1), int256(100000000), uint256(block.timestamp), uint256(block.timestamp), uint80(1)));
vm.mockCall(address(0x300), abi.encodeWithSignature("decimals()"), abi.encode(uint8(8)));
oracle.setPriceFeed(usdc, address(0x300));
oracle.setPriceFeed(weth, address(0x300));
vm.mockCall(usdc, abi.encodeWithSelector(IERC20.transferFrom.selector), abi.encode(true));
vm.mockCall(usdc, abi.encodeWithSelector(IERC20.approve.selector), abi.encode(true));
vm.mockCall(usdc, abi.encodeWithSelector(IERC20.decimals.selector), abi.encode(uint8(6)));
vm.mockCall(weth, abi.encodeWithSelector(IERC20.approve.selector), abi.encode(true));
vm.mockCall(weth, abi.encodeWithSelector(IERC20.balanceOf.selector), abi.encode(0));
vm.mockCall(weth, abi.encodeWithSelector(IERC20.decimals.selector), abi.encode(uint8(18)));
vm.mockCall(aavePool, abi.encodeWithSignature("supply(address,uint256,address,uint16)"), abi.encode());
vm.mockCall(aavePool, abi.encodeWithSignature("borrow(address,uint256,uint256,uint16,address)"), abi.encode());
vm.mockCall(aavePool, abi.encodeWithSignature("getUserAccountData(address)"),
abi.encode(uint256(5000e8), uint256(2000e8), uint256(0), uint256(0), uint256(0), uint256(150e16)));
vm.mockCall(aaveDataProvider, abi.encodeWithSignature("getReserveConfigurationData(address)"),
abi.encode(uint256(0), uint256(8000), uint256(8500), uint256(10500), uint256(0), false, false, false, false, false));
vm.mockCall(aaveDataProvider, abi.encodeWithSignature("getReserveTokensAddresses(address)"),
abi.encode(address(0x400), address(0x401), debtToken));
vm.mockCall(oneInchRouter, abi.encodeWithSignature("swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)"),
abi.encode(uint256(2100e6), uint256(0)));
}
function test_MultiplePositions_NoTracking() public {
stratax.createLeveragedPosition(usdc, 2000e6, 1000e6, weth, 0.66e18, "", 0);
vm.mockCall(debtToken, abi.encodeWithSelector(IERC20.balanceOf.selector, address(stratax)),
abi.encode(position1DebtAmount));
(uint256 collateral1, uint256 debt1) = stratax.calculateUnwindParams(usdc, weth);
stratax.createLeveragedPosition(usdc, 1200e6, 800e6, weth, 0.4e18, "", 0);
uint256 totalDebt = position1DebtAmount + position2DebtAmount;
vm.mockCall(debtToken, abi.encodeWithSelector(IERC20.balanceOf.selector, address(stratax)),
abi.encode(totalDebt));
(uint256 collateral2, uint256 debt2) = stratax.calculateUnwindParams(usdc, weth);
assertGt(debt2, debt1, "Debt should increase after 2nd position");
assertEq(debt2, totalDebt, "Returns TOTAL debt of both positions");
}
function test_NoPositionIdParameter() public {
vm.mockCall(debtToken, abi.encodeWithSelector(IERC20.balanceOf.selector, address(stratax)),
abi.encode(1e18));
(uint256 collateral, uint256 debt) = stratax.calculateUnwindParams(usdc, weth);
assertGt(debt, 0, "Returns total debt without position ID");
}
}
// src/Stratax.sol
contract Stratax is Initializable {
+ struct Position {
+ address collateralToken;
+ uint256 collateralAmount;
+ address debtToken;
+ uint256 debtAmount;
+ bool isActive;
+ }
+
+ Position[] public positions;
+ uint256 public nextPositionId;
function createLeveragedPosition(
address _flashLoanToken,
uint256 _flashLoanAmount,
uint256 _collateralAmount,
address _borrowToken,
uint256 _borrowAmount,
bytes calldata _oneInchSwapData,
uint256 _minReturnAmount
- ) public onlyOwner {
+ ) public onlyOwner returns (uint256 positionId) {
+ positionId = nextPositionId++;
+ positions.push(Position({
+ collateralToken: _flashLoanToken,
+ collateralAmount: _collateralAmount,
+ debtToken: _borrowToken,
+ debtAmount: _borrowAmount,
+ isActive: true
+ }));
// ... rest of function
}
- function calculateUnwindParams(address _collateralToken, address _debtToken)
+ function calculateUnwindParams(uint256 _positionId)
public view
returns (uint256 collateralAmount, uint256 debtAmount)
{
+ require(_positionId < positions.length, "Invalid position");
+ Position memory position = positions[_positionId];
+ require(position.isActive, "Position not active");
+ return (position.collateralAmount, position.debtAmount);
- (uint256 totalCollateralBase, uint256 totalDebtBase,,,,) =
- aavePool.getUserAccountData(address(this));
}
function unwindPosition(
+ uint256 _positionId,
// ... other parameters
) public onlyOwner {
+ require(_positionId < positions.length, "Invalid position");
+ Position storage position = positions[_positionId];
+ require(position.isActive, "Position not active");
// ... unwind logic
+ position.isActive = false;
}
}