onApprove() can force accidental contract creation
Summary
Deposit transactions in the portal allow the user to specify whether or not the intention is contract creation on L2. The onApprove() function makes the assumption that all transactions to address(0) are intended to be contract creations, and can thus redirect otherwise valid transactions into failed contract creations.
Root Cause
When depositTransaction() is called on the Optimism Portal, two of the values passed are to and isCreation.
These two values have the following relationship:
1) IF _isCreation is true, then _to must be address(0).
2) IF _to is address(0), _isCreation can be either true or false.
As we can see in the op-node code here, in the event that _isCreation = true, we set the address to nil, which causes a contract to be created. Otherwise, we leave the address as address(0) and perform the transaction as normal.
While it is unlikely that a user would want to send a transaction to address(0), it is possible, which is why the above flow exists. These checks and requirements are the exact same in the Tokamak version of the contracts.
However, there is another way that the Tokamak contract allows a user to initiate a deposit transaction, and that is with the approveAndCall() from the native token. In this case, we can see the logic below:
As we can see, in this case _isCreation is automatically set to true when to == address(0).
Internal Preconditions
None
External Preconditions
None
Attack Path
A user wants to send an L2 transaction to address(0).
They use the approveAndCall() function on the native token to send the transaction.
Their L2 transaction is turned into a contract creation, which wasn't intended.
Impact
L2 transactions can occur differently than expected, breaking user expectations.
PoC
N/A
Mitigation
The onApprove() data should be updated to include the _isCreation flag, so that the user can specify whether or not the transaction is intended to be a contract creation.
obront
Medium
onApprove()
can force accidental contract creationSummary
Deposit transactions in the portal allow the user to specify whether or not the intention is contract creation on L2. The
onApprove()
function makes the assumption that all transactions toaddress(0)
are intended to be contract creations, and can thus redirect otherwise valid transactions into failed contract creations.Root Cause
When
depositTransaction()
is called on the Optimism Portal, two of the values passed areto
andisCreation
.These two values have the following relationship: 1) IF
_isCreation
is true, then_to
must beaddress(0)
. 2) IF_to
isaddress(0)
,_isCreation
can be eithertrue
orfalse
.As we can see in the
op-node
code here, in the event that_isCreation = true
, we set the address tonil
, which causes a contract to be created. Otherwise, we leave the address asaddress(0)
and perform the transaction as normal.While it is unlikely that a user would want to send a transaction to
address(0)
, it is possible, which is why the above flow exists. These checks and requirements are the exact same in the Tokamak version of the contracts.However, there is another way that the Tokamak contract allows a user to initiate a deposit transaction, and that is with the
approveAndCall()
from the native token. In this case, we can see the logic below:As we can see, in this case
_isCreation
is automatically set to true whento == address(0)
.Internal Preconditions
None
External Preconditions
None
Attack Path
address(0)
.approveAndCall()
function on the native token to send the transaction.Impact
L2 transactions can occur differently than expected, breaking user expectations.
PoC
N/A
Mitigation
The
onApprove()
data should be updated to include the_isCreation
flag, so that the user can specify whether or not the transaction is intended to be a contract creation.