HE1M
medium
It is possible to increase the volume of bridging transaction significantly. It can be done by getting a flash loan of USDC (for example) and transfers this large amount between L1/L2 but in reality this large amount only is deposited in bridge contract on L1 and in the same transaction it is withdrawn to repay the flash loan. But, it seems that the user is bridging large amount between L1 and L2.
- Bob (a malicious user) creates a fake ERC20 token contract (called
FakeL2Token
) on L2, and mints1_000_000 * 10 ** 6
to himself. - Bob creates a contract (called
BobL1Contract
) on L1. - Bob calls the function
bridgeERC20To
on L2 to bridge1_000_000 * 10 ** 6
ofFakeL2Token
to L1, with following parameters:_localToken
= address ofFakeL2Token
on L2_remoteToken
= address of USDC on L1_to
= address ofBobL1Contract
on L1_amount
= 1_000_000 * 10 ** 6_minGasLimit
= just enough to have a successful transaction_extraData
= bytes("") https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L265-L272
- Then the internal function
_initiateBridgeERC20
will be called. https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L397 - Since
FakeL2Token
is not Optimism Mintable ERC20, theelse block
will be executed, in which we will have:
deposits[FakeL2Token][USDC] = 1_000_000 * 10 ** 6;
- After validation, and challenge period elapsed, on the L1 side, the function
finalizeBridgeERC20
will be called. https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L324 - Since USDC is not Optimism Mintable ERC20, the
else block
will be executed. But, it will be reverted, because the mappingdeposits[USDC][FakeL2Token] = 0
, so it can not unlock 1_000_000 * 10 ** 6 USDC. https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L339 - Since, the finalization was not successful, it is possible to retry finalization again later. https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L331
BobL1Contract
takes a flash loan of 1_000_000 * 10 ** 6 USDC from AAVE.- In the same transaction,
BobL1Contract
calls the functionbridgeERC20To
on L1 with the following parameters:_localToken
= address of USDC on L1_remoteToken
= address ofFakeL2Token
on L2_to
= address of Bob_amount
= 1_000_000 * 10 ** 6_minGasLimit
= just enough to have a successful transaction_extraData
= bytes("")
- Since USDC is not Optimism Mintable ERC20, 1_000_000 * 10 ** 6 USDC will be transferred from
BobL1Contract
to theL1StandardBridge
, and we will have:
deposits[USDC][FakeL2Token] = 1_000_000 * 10 ** 6;
- In the same transaction,
BobL1Contract
retries the finalization of the previous failed transaction by calling the functionrelayMessage
. https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L256 - Since the mapping
deposits[USDC][FakeL2Token]
has the value of 1_000_000 * 10 ** 6 (because of the previous bridge initiation), this time the ERC20 bridging will be finalized properly, and this amount will be transferred toBobL1Contract
, and we will havedeposits[USDC][FakeL2Token] = 0
. https://github.com/sherlock-audit/2023-01-optimism/blob/main/optimism/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L341 - In the same transaction,
BobL1Contract
repays the flash loan to AAVE. - On the L2 side, the same amount 1_000_000 * 10 ** 6
FakeL2Token
will be transferred to Bob.
Please note that the same scenario can also happen for ERC721 bridging.
- It may have some impact on the Bots (defenders, like what implemented by OpenZeppelin) who monitors the deposit and withdrawal amount continuously. For example, if a Bot is monitoring the Optimism bridges to see if some suspicious large amount of funds are being transferred between L1/L2 to do some action (like setting off an alarm or pausing some critical functions), it will lead to a false alarm.
- Moreover, Bob can increase his volume of transaction with optimism fraudulently, and can increase his chance of getting airdrop (if any) significantly.
Manual Review
Maybe one solution is to check that at least one of the tokens (remote or local) be Optimism Mintable ERC20 on one of the chains. For example, Since USDC is not Optimism Mintable ERC20, then FakeL2Token
must be Optimism Mintable ERC20 on L2.
function finalizeBridgeERC20(
address _localToken,
address _remoteToken,
address _from,
address _to,
uint256 _amount,
bytes calldata _extraData
) public onlyOtherBridge {
if (_isOptimismMintableERC20(_localToken)) {
require(
_isCorrectTokenPair(_localToken, _remoteToken),
"StandardBridge: wrong remote token for Optimism Mintable ERC20 local token"
);
OptimismMintableERC20(_localToken).mint(_to, _amount);
} else {
require(_isOptimismMintableERC20OnRemoteChain(_remoteToken), "at least one token must be Optimism Mintable ERC20");
deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] - _amount;
IERC20(_localToken).safeTransfer(_to, _amount);
}
emit ERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
}