If the first token of a collection on L2 is going to be bridged from L2 to L1, since the associated L1 collection address is not known yet, the forwarded address would be address(0)
. On L1 side, during withdrawal, the L1 collection would be updated and the mappings _l1ToL2Addresses
and _l2ToL1Addresses
would be updated accordingly.
The issue is when this NFT on L1 is going to be bridged back to L2. On L2 side, during withdrawal, the mapping l1_to_l2_addresses
returns 0
as it does not know the L2 collection associated with the L1 collection. So, during verification, it reverts by Invalid collection L2 address
. Since the withdrawal on L2 is auto, retrying such withdrawal is not possible, so it would be impossible to withdraw the NFT on L2, and the NFT would be stuck on L1 permanently.
When the first token of an L2 collection is bridged from L2 to L1, during the function call deposit_tokens
, the field collection_l1
would be address(0)
because the L1 collection is not yet deployed and it is not known yet.
On L1 side, during withdrawal, the L1 collection will be deployed as collectionL1 = address(0)
.
https://github.com/Cyfrin/2024-07-ark-project/blob/main/apps/blockchain/ethereum/src/Bridge.sol#L179
After deployment the mappings _l1ToL2Addresses
and _l2ToL1Addresses
would be updated.
The issue happens hereafter:
If this NFT on L1 is going to be bridged back to L2, during the deposit, both req.collectionL1
and req.collectionL2
are known and are forwarded as parameters to L2.
On L2 side, during withdrawal, it invokes the internal function ensure_erc721_deployment
to calculate the L2 collection associated with this forwarded L1 collection.
In this function, both req.collection_l1
and req.collection_l2
are known (since they are forwarded as payload from L1). But, the mappings self.l2_to_l1_addresses.read(l2_req)
and self.l1_to_l2_addresses.read(l1_req)
return address(0)
and 0
, respectively. Because, L2 is not aware of recently-deployed L1 collection.
As a result, in the function verify_collection_address
, since l2_bridge = 0
and l2_req > 0
we have l2_bridge != l2_req
, so it will revert by Invalid collection L2 address
.
Thus, the withdrawal on L2 reverts, and the escrowed NFT on L1 will be stuck there forever. Note that the withdrawal on L2 is auto, so it is not possible to retry it.
Even if the owner sets the mappings on L2, it will not solve the already stuck NFTs.
https://github.com/Cyfrin/2024-07-ark-project/blob/main/apps/blockchain/starknet/src/bridge.cairo#L360
Please note that:
This finding is almost similar to the report with title "Issue with bridging back an L1-native NFT from L2 to L1", but the root cause and fix are different. In other words, this finding explains the issue when a collection is native to L2, then bridged to L1, and later bridged back to L2. But, the other report explains the issue when a collection is native to L1, then bridged to L2, and later bridged back to L1.
Stuck of an NFT on L1 without being withdrawable on L1 and L2.
The following modifications are recommended:
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.