Users bridging a native L1 collection to L2 can't bridge it back leading to their tokens being stuck permanently in the bridge.
Let's have the scenario where an L1 collection is deployed on L1, L2 collection is not yet deployed and we're bridging L1 -> L2 for the 1st time.
We call depositTokens
, the request is created and let's see the value of this:
req.collectionL1
will be the address of the collection on L1 and req.collectionL2
will be 0 since the collection is yet to be deployed on L2 and the mapping is empty.
Now the message is sent and withdraw_auto_from_l1
is called on L2. We go to this line:
and let's see the code of the ensure_erc721_deployment
function:
let collection_l2 = verify_collection_address
will return 0 since the collection is yet to be deployed, we'll proceed with let l2_addr_from_deploy = deploy_erc721_bridgeable
which deploys the function and the mappings on the L2 contract will be updated:
Now, let's try and bridge back the same token from the collection to L1.
We call deposit_tokens
, where collection_l2
is the address of the collection on L2, let collection_l1 = self.l2_to_l1_addresses.read(collection_l2);
returns the address of the collection on L1 since that mapping has been updated.
Message is processed, we call withdrawTokens
on L1:
Let's focus on this line address collectionL1 = _verifyRequestAddresses(req.collectionL1, req.collectionL2);
, where req.collectionL1
and req.collectionL2
are the addresses of the collection on L1 and L2 accordingly as they are passed by the L2 contract which has the updated mappings as we confirmed.
We enter into this if (l2Req > 0 && l1Req > address(0))
since both l2Req
and l1Req
are not 0, and we go into the following.
However, address l1Mapping = _l2ToL1Addresses[collectionL2Req];
is address(0)
since that mapping was never updated, and l1Req
is not 0 as we already pointed out, so the function enters the if and reverts.
Now, the users' token on the L2 is in the escrow and he can't claim it back, and he can't withdraw his token on L1 leading to his NFTs being stuck forever.
Once you bridge a token from a native L1 collection to L2 you can't bridge it back leading to your tokens being stuck.
Manual review
In the case where both l2Req
and l1Req
are present just return l1Req
since that value is passed from the L2 bridge contract and can be trusted.
Likelyhood: High, any collections bridged, without bridge owner action, will be unable to bridge back. Impact: High, L2 -> L1 tokens will be stuck in the bridge. L1 -> L2 will need to ask for a cancellation.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.