pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {Stratax} from "../../src/Stratax.sol";
import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
contract MockERC20 {
string public name;
string public symbol;
uint8 public decimals;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
constructor(string memory _name, string memory _symbol, uint8 _decimals) {
name = _name;
symbol = _symbol;
decimals = _decimals;
}
function mint(address to, uint256 amount) external {
balanceOf[to] += amount;
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
return true;
}
function transfer(address to, uint256 amount) external returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
uint256 allowed = allowance[from][msg.sender];
require(allowed >= amount, "allowance too low");
allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
balanceOf[to] += amount;
return true;
}
}
contract MockAavePool {
MockERC20 public debtToken;
MockERC20 public collateralToken;
constructor(MockERC20 _debtToken, MockERC20 _collateralToken) {
debtToken = _debtToken;
collateralToken = _collateralToken;
}
function flashLoanSimple(address receiver, address asset, uint256 amount, bytes calldata params, uint16) external {
debtToken.transfer(receiver, amount);
Stratax(receiver).executeOperation(asset, amount, 0, receiver, params);
}
function repay(address, uint256 amount, uint256, address) external returns (uint256) {
debtToken.transferFrom(msg.sender, address(this), amount);
return amount;
}
function withdraw(address, uint256 amount, address to) external returns (uint256) {
collateralToken.transfer(to, amount);
return amount;
}
function supply(address, uint256, address, uint16) external {}
function borrow(address, uint256, uint256, uint16, address) external {}
function getUserAccountData(address)
external
pure
returns (uint256, uint256, uint256, uint256, uint256, uint256)
{
return (0, 0, 0, 0, 0, 2e18);
}
}
contract MockDataProvider {
uint256 public liqThreshold;
constructor(uint256 _liqThreshold) {
liqThreshold = _liqThreshold;
}
function getReserveConfigurationData(address)
external
view
returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256)
{
return (0, liqThreshold, 0, 0, 0, 0, 0, 0, 0, 0);
}
function getReserveTokensAddresses(address)
external
view
returns (address, address, address)
{
return (address(0), address(0), address(0));
}
}
contract MockOracle {
mapping(address => uint256) public price;
function setPrice(address token, uint256 value) external {
price[token] = value;
}
function getPrice(address token) external view returns (uint256) {
return price[token];
}
}
contract Mock1InchRouter {
function swap(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut)
external
returns (uint256 returnAmount, uint256 spentAmount)
{
MockERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
if (amountOut > 0) {
MockERC20(tokenOut).transfer(msg.sender, amountOut);
}
return (amountOut, amountIn);
}
}
contract PoC_UnwindSwapDataMismatch is Test {
Stratax public stratax;
MockERC20 public collateralToken;
MockERC20 public debtToken;
MockAavePool public aavePool;
MockDataProvider public dataProvider;
MockOracle public oracle;
Mock1InchRouter public oneInch;
address public ownerTrader = address(0x123);
function setUp() public {
collateralToken = new MockERC20("COLL", "COLL", 18);
debtToken = new MockERC20("DEBT", "DEBT", 6);
aavePool = new MockAavePool(debtToken, collateralToken);
dataProvider = new MockDataProvider(9500);
oracle = new MockOracle();
oneInch = new Mock1InchRouter();
oracle.setPrice(address(collateralToken), 1e8);
oracle.setPrice(address(debtToken), 1e8);
collateralToken.mint(address(aavePool), 2000 * 1e18);
debtToken.mint(address(aavePool), 2000 * 1e6);
Stratax impl = new Stratax();
UpgradeableBeacon beacon = new UpgradeableBeacon(address(impl), address(this));
bytes memory initData = abi.encodeWithSelector(
Stratax.initialize.selector,
address(aavePool),
address(dataProvider),
address(oneInch),
address(debtToken),
address(oracle)
);
BeaconProxy proxy = new BeaconProxy(address(beacon), initData);
stratax = Stratax(address(proxy));
stratax.transferOwnership(ownerTrader);
}
function test_UnwindReverts_WhenSwapDataAmountExceedsWithdrawnCollateral() public {
uint256 debtToRepay = 1000 * 1e6;
uint256 userRequestedCollateral = 2000 * 1e18;
bytes memory swapData =
abi.encodeWithSelector(Mock1InchRouter.swap.selector, address(collateralToken), address(debtToken), userRequestedCollateral, debtToRepay);
vm.startPrank(ownerTrader);
vm.expectRevert();
stratax.unwindPosition(
address(collateralToken),
userRequestedCollateral,
address(debtToken),
debtToRepay,
swapData,
0
);
vm.stopPrank();
}
}