Frontrunning Vulnerability in approve()
function of ERC20
#725
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
duplicate-145
sufficient quality report
This report is of sufficient quality
unsatisfactory
does not satisfy C4 submission criteria; not eligible for awards
Lines of code
https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137
Vulnerability details
Impact
The
approve()
function in the provided ERC20 contract is susceptible to a frontrunning attack. Malicious actors can exploit the timing between anapprove()
transaction being sent and it getting mined, potentially allowing them to spend more tokens than the token owner intended. If an owner tries to change the allowance of a spender, the spender can spend the original allowance before the new one takes effect, and then spend the new allowance, resulting in a total expenditure exceeding the latest set amount.Proof of Concept
Consider the following scenario:
https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137
Tools Used
Manual
Recommended Mitigation Steps
Before increasing or decreasing the allowance, the contract could require that the current allowance be set to 0. This way, Alice would have to first send a transaction setting the allowance to 0 before setting it to a new value, ensuring Bob cannot front-run in between. The downside is that this requires two separate transactions, which might not be very user-friendly.
https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137
function approve(address spender, uint256 value) external returns (bool) { + require((allowance[msg.sender][spender] == 0), "ERC20: set allowance to 0 first"); allowance[_msgSender()][spender] = value; emit Approval(_msgSender(), spender, value); return true; }
Alternatively, simply remove
approve()
since users can always useincreaseAllowance()
for the same intended purpose.In your contract, you've provided the
increaseAllowance()
anddecreaseAllowance()
functions, which is good. Informing users to prefer these methods over a directapprove()
for changing allowances can mitigate the front-running risk.Assessed type
Timing
The text was updated successfully, but these errors were encountered: