56528 sc insight unbounded slippagebps can freeze withdrawals
Previous57918 sc high incorrect totallocked collateral accounting in alchemistv3Next57825 sc high forced repay cover enables double counted debt reduction in redeem
Was this helpful?
Was this helpful?
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import "forge-std/Test.sol";
import {stdError} from "forge-std/StdError.sol";
import {MYTStrategy} from "../MYTStrategy.sol";
import {IMYTStrategy} from "../interfaces/IMYTStrategy.sol";
import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol";
contract MYTStrategyBaseTest is Test {
MockERC20 internal asset;
MockERC20 internal receipt;
MockVault internal vault;
MYTStrategy internal strategy;
function setUp() public {
asset = new MockERC20("Asset", "ASSET", 18);
receipt = new MockERC20("Receipt", "RECEIPT", 18);
vault = new MockVault(asset);
IMYTStrategy.StrategyParams memory params = IMYTStrategy.StrategyParams({
owner: address(this),
name: "Base Strategy",
protocol: "Test Protocol",
riskClass: IMYTStrategy.RiskClass.LOW,
cap: 0,
globalCap: 0,
estimatedYield: 0,
additionalIncentives: false,
slippageBPS: 0
});
strategy = new MYTStrategy(address(vault), params, address(0x1234), address(receipt));
}
function test_previewAdjustedWithdrawRevertsWhenSlippageExceedsOneHundredPercent() public {
IMYTStrategy.StrategyParams memory badParams = IMYTStrategy.StrategyParams({
owner: address(this),
name: "Bad Slippage Strategy",
protocol: "Test Protocol",
riskClass: IMYTStrategy.RiskClass.LOW,
cap: 0,
globalCap: 0,
estimatedYield: 0,
additionalIncentives: false,
slippageBPS: 20_000
});
SlippagePreviewStrategy badStrategy = new SlippagePreviewStrategy(address(vault), badParams, address(0x1234), address(receipt));
vm.expectRevert(stdError.arithmeticError);
badStrategy.previewAdjustedWithdraw(1 ether);
}
}
contract SlippagePreviewStrategy is MYTStrategy {
constructor(address _myt, StrategyParams memory _params, address _permit2Address, address _receiptToken)
MYTStrategy(_myt, _params, _permit2Address, _receiptToken)
{}
function _previewAdjustedWithdraw(uint256 amount) internal view override returns (uint256) {
return amount - (amount * slippageBPS / 10_000);
}
}
contract MockVault {
MockERC20 public immutable asset;
mapping(address => uint256) private allocations;
constructor(MockERC20 _asset) {
asset = _asset;
}
function allocate(address strategy, uint256 amount) external {
bytes memory data = abi.encode(allocations[strategy]);
asset.transfer(strategy, amount);
(, int256 change) = IMYTStrategy(strategy).allocate(data, amount, this.allocate.selector, msg.sender);
allocations[strategy] = _applyChange(allocations[strategy], change);
}
function deallocate(address strategy, uint256 amount) external {
bytes memory data = abi.encode(allocations[strategy]);
(, int256 change) = IMYTStrategy(strategy).deallocate(data, amount, this.deallocate.selector, msg.sender);
allocations[strategy] = _applyChange(allocations[strategy], change);
asset.transferFrom(strategy, address(this), amount);
}
function currentAllocation(address strategy) external view returns (uint256) {
return allocations[strategy];
}
function _applyChange(uint256 current, int256 change) internal pure returns (uint256) {
if (change > 0) {
return current + uint256(change);
} else if (change < 0) {
return current - uint256(-change);
}
return current;
}
}