Key protocol liquidity management and swapping (formPOL and withdrawPOL) are prone to front-running and the loss can accumulate overtime with upkeep calls.
Note that despite the arbitrage mechanism, front-running is still possible and profitable.
Proof of Concept
In Upkeep.sol, perfromUpkeep() is permissionless and includes multiple protocol liquidity management and token-swapping calls. However, all of the protocol-owned liquidity (POL) related swaps and liquidity management have both slippage and stale tx protections disabled at all times.
step1(), step3(), step4() and step5() all contain unprotected swapping or POL liquidity management calls. In all cases, minimum out value is hardcoded to 0 and tx deadline is block.timestamp. 0 out value allows any amount of swap out or liquidity assets; block.timestamp means the transaction can be settled at any time.
For example, step5() swap remaining WETH (arbitrage profits) into SALT, which will be distributed as rewards across pools. But pools.depositSwapWithdraw() allows any amount of SALT output and allow performUpKeep() to settle at any time. step1() might call Dao.sol to withdrawPOL(). step3()step4() will swap tokens to deposit liquidity through formPOL().
Even though there is an arbitrage mechanism, front-running is still possible and profitable. And when both slippage and stale tx protections are disabled, front-running is easier and the loss will accumulate over time.
Tools Used
Manual
Recommended Mitigation Steps
Although difficult to implement in the permissionless context, consider implementing priceFeed to ensure a reasonable minimal output amount for WETH to usds, dai and salt swap.
Lines of code
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/DAO.sol#L372 https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/DAO.sol#L321 https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/Upkeep.sol#L132-L133 https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/Upkeep.sol#L178
Vulnerability details
Impact
Key protocol liquidity management and swapping (
formPOL
andwithdrawPOL
) are prone to front-running and the loss can accumulate overtime with upkeep calls.Note that despite the arbitrage mechanism, front-running is still possible and profitable.
Proof of Concept
In Upkeep.sol,
perfromUpkeep()
is permissionless and includes multiple protocol liquidity management and token-swapping calls. However, all of the protocol-owned liquidity (POL) related swaps and liquidity management have both slippage and stale tx protections disabled at all times.step1()
,step3()
,step4()
andstep5()
all contain unprotected swapping or POL liquidity management calls. In all cases, minimum out value is hardcoded to 0 and tx deadline isblock.timestamp
. 0 out value allows any amount of swap out or liquidity assets;block.timestamp
means the transaction can be settled at any time.For example,
step5()
swap remaining WETH (arbitrage profits) into SALT, which will be distributed as rewards across pools. Butpools.depositSwapWithdraw()
allows any amount of SALT output and allowperformUpKeep()
to settle at any time.step1()
might call Dao.sol towithdrawPOL()
.step3()step4()
will swap tokens to deposit liquidity throughformPOL()
.See locations of above-mentioned cases:
(https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/Upkeep.sol#L132-L133)
(https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/dao/DAO.sol#L372C1-L372C156)
Even though there is an arbitrage mechanism, front-running is still possible and profitable. And when both slippage and stale tx protections are disabled, front-running is easier and the loss will accumulate over time.
Tools Used
Manual
Recommended Mitigation Steps
Although difficult to implement in the permissionless context, consider implementing priceFeed to ensure a reasonable minimal output amount for WETH to usds, dai and salt swap.
Assessed type
MEV