DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: medium
Invalid

Miners Can Front-Run the Token Swap Process, Causing Keepers to Fail and Exposing the Protocol to DoS Vulnerabilities

Summary

In the StrategyOp and StrategyArb contracts, the claimAndSwap function is callable only by designated keepers. This function allows keepers to claim WETH from the transmuter and swap it for alETH (Alchemix ETH), serving as a mechanism to utilize and compound yield. However, an oversight in the _swapUnderlyingToAsset function within both strategies restricts successful token swaps due to the improper use of block.timestamp as the swap deadline.

Key Code Blocks

StrategyOp::claimAndSwap:

function claimAndSwap(uint256 _amountClaim, uint256 _minOut, IVeloRouter.route[] calldata _path)
external
onlyKeepers
{
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
@> _swapUnderlyingToAsset(_amountClaim, _minOut, _path);
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
transmuter.deposit(asset.balanceOf(address(this)), address(this));
}

StrategyArb::claimAndSwap:

function claimAndSwap(uint256 _amountClaim, uint256 _minOut, IRamsesRouter.route[] calldata _path)
external
onlyKeepers
{
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
@> _swapUnderlyingToAsset(_amountClaim, _minOut, _path);
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
transmuter.deposit(asset.balanceOf(address(this)), address(this));
}

StrategyArb::_swapUnderlyingToAsset:

function _swapUnderlyingToAsset(uint256 _amount, uint256 minOut, IRamsesRouter.route[] calldata _path) internal {
require(minOut > _amount, "minOut too low");
uint256 underlyingBalance = underlying.balanceOf(address(this));
require(underlyingBalance >= _amount, "not enough underlying balance");
@> IRamsesRouter(router).swapExactTokensForTokens(_amount, minOut, _path, address(this), block.timestamp);
}

StrategyOp::_swapUnderlyingToAsset:

function _swapUnderlyingToAsset(uint256 _amount, uint256 minOut, IVeloRouter.route[] calldata _path) internal {
require(minOut > _amount, "minOut too low");
uint256 underlyingBalance = underlying.balanceOf(address(this));
require(underlyingBalance >= _amount, "not enough underlying balance");
@> IVeloRouter(router).swapExactTokensForTokens(_amount, minOut, _path, address(this), block.timestamp);
}

Problematic Lines:

IVeloRouter(router).swapExactTokensForTokens(_amount, minOut, _path, address(this), block.timestamp);
-----------------------------------------------------------------------------------------^ // deadline set to current block timestamp
IRamsesRouter(router).swapExactTokensForTokens(_amount, minOut, _path, address(this), block.timestamp);
-----------------------------------------------------------------------------------------^ // deadline set to current block timestamp

Vulnerability Details

The use of block.timestamp as the deadline for the swap introduces a vulnerability related to transaction execution timing, miner manipulation, and network latency.

  1. Transaction Execution Timing:

    • The block.timestamp used in _swapUnderlyingToAsset might differ from the one used when the transaction is initially submitted. This discrepancy can result in a failed deadline validation.

  2. Miner Manipulation:

    • Miners can adjust the block timestamp within a small range (up to 15 seconds). By delaying transaction execution slightly, a malicious miner can manipulate the block timestamp to invalidate the swap deadline, causing the transaction to revert.

  3. Network Latency:

    • Due to propagation delays in the network, the block.timestamp seen by the contract might lag behind the real-world time. This discrepancy can result in legitimate swaps failing due to an expired deadline.

  4. Miner Collusion:

    • A miner, or an entity colluding with one, could delay the swap transaction long enough to force the deadline validation to fail.

Example Scenario:

Both RamsesRouter and VeloRouter use the following check to validate deadlines:

// both routers use logic almost identical to below one...
modifier ensure(uint256 deadline) {
if (deadline < block.timestamp) revert Expired();
_;
}

If a keeper submits the swap with a deadline equal to the current block.timestamp, a miner can delay processing the transaction just enough to cause the block.timestamp to exceed the deadline.

Impact

  1. Transaction Failure: Miners can manipulate or delay transactions, causing swaps to fail due to expired deadlines.

  2. Denial of Service (DoS): Repeated failures prevent keepers from successfully claiming and swapping tokens, effectively locking yield-generation mechanisms.

  3. Protocol Vulnerability: The protocol becomes susceptible to external manipulation, making it unreliable for yield strategies.

Tools Used

  • Manual Code Review

  • Foundry Cheatcodes

Recommendations

To mitigate this issue, it is recommended to modify the swap deadline to include a buffer that accounts for miner manipulation and network delays. A practical approach is to add a small, acceptable margin (e.g., 15 seconds) to the block.timestamp when setting the deadline.

Suggested Fix:

function _swapUnderlyingToAsset(uint256 _amount, uint256 minOut, IVeloRouter.route[] calldata _path) internal {
require(minOut > _amount, "minOut too low");
uint256 underlyingBalance = underlying.balanceOf(address(this));
require(underlyingBalance >= _amount, "not enough underlying balance");
+ uint256 deadline = block.timestamp + 15; // Add buffer to account for miner delays
- IVeloRouter(router).swapExactTokensForTokens(_amount, minOut, _path, address(this), block.timestamp);
+ IVeloRouter(router).swapExactTokensForTokens(_amount, minOut, _path, address(this), deadline);
}

Additional Safeguards:

  1. Use Block Numbers: If possible, use block.number for deadlines instead of block.timestamp to mitigate miner manipulation.

  2. Off-Chain Oracles: Leverage decentralized oracles to validate timestamps independently.

  3. Transaction Retries: Implement retry mechanisms for failed swaps to ensure robustness.

  4. Reasonable Buffers: Always provide a reasonable deadline buffer to accommodate delays in transaction propagation and processing.

Conclusion

By using block.timestamp directly as a deadline, the protocol becomes vulnerable to miner manipulation and network delays, leading to potential DoS attacks. Adding a buffer to the deadline mitigates these risks and ensures the system remains reliable.

Updates

Appeal created

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Known issue
inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.