Era

ZKsync
FoundryLayer 2
500,000 USDC
View results
Submission Details
Severity: medium
Valid

`L1ERC20Bridge` Uses Unsafe Approvals

Summary

The L1ERC20Bridgewill be incompatible with void return tokens such as USDT on Ethereum mainnet.

Vulnerability Details

The L1ERC20Bridge contract uses the IERC20 interface to manage token approvals directly:

function _approveFundsToAssetRouter(address _from, IERC20 _token, uint256 _amount) internal returns (uint256) {
uint256 balanceBefore = _token.balanceOf(address(this));
_token.safeTransferFrom(_from, address(this), _amount);
@> bool success = _token.approve(address(L1_ASSET_ROUTER), _amount);
@> if (!success) {
@> revert ApprovalFailed();
@> }
uint256 balanceAfter = _token.balanceOf(address(this));
return balanceAfter - balanceBefore;
}

https://github.com/Cyfrin/2024-10-zksync/blob/cfc1251de29379a9548eeff1eea3c78267288356/era-contracts/l1-contracts/contracts/bridge/L1ERC20Bridge.sol#L227C5-L237C6

// clearing approval
@> bool success = IERC20(_l1Token).approve(address(L1_ASSET_ROUTER), 0);
@> if (!success) {
@> revert ApprovalFailed();
@> }

https://github.com/Cyfrin/2024-10-zksync/blob/cfc1251de29379a9548eeff1eea3c78267288356/era-contracts/l1-contracts/contracts/bridge/L1ERC20Bridge.sol#L206C9-L210C10

The IERC20interface explicitly instructs the Solidity to compiler to parse for boolean returndata, which when absent (such as in a call to approve(address,uint256) on USDT), will cause the transaction to revert:

/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender, 0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
}

https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7/advanced#code

Impact

Inability to bridge USDT.

Tools Used

Manual Review

Recommendations

Use IERC20.safeApproveinstead.

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`L1ERC20Bridge` Uses Unsafe Approvals - USDT won't work

Support

FAQs

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