When Bridging Collections L1<->L2, we are checking if that NFT collection has a pair on the destination chain or not. and if it has an address on the destination, then we use it instead of redeploying new one.
CollectionManager.sol#L111-L149
This function (_verifyRequestAddresses) is called whenever we withdraw tokens, where if the request came from L2 Bridge has a valid collectionL1 address (l1Req), we are doing checks that the matching of addresses is the same on both chains.
l2Req is the NFT collection we withdrew from on L2, and it should be a valid NFT collection address
l1Req is the l2_to_l1_addresses on L2 where if the collection has matching on L2 it will use that address when bridging tokens from L2 to L1.
So if the NFT collection has an L1<->L2 matching on L2 we will do a check that ensures the NFT collection L1 and L2 addresses on L2Bridge are the same in L1Bridge.
The problem is that setting l1<->l2 addresses on the L2Bridge doesn't mean that that value is always set on L1Bridge.
We are only setting l1<->l2 on the chain we are withdrawing from, so when bridging from L1 to L2. L2Bridge will set the matching between collections but L1 will not set that matching. So if we tried to withdraw from L2 to L1 the withdrawing will revert as it will compare a collection address with the address zero.
There is an NFT collection on L1, this collection has no matching on either L1 or L2
UserA bridged tokens from that collections to L2
req.collectionL2 is address(0) as the is no l1<->l2 matching on L1Bridge
Starknet Sequencer called withdraw_auto_from_l1
calling ensure_erc721_deployment to check if the collection on L1 has an address on L2 or not
Verify Collection address
l1_req is the original NFT collection address on L1, l2_req is address(0) as we show when calling L1Bridge::depositTokens() and the other two parameters are also address(0) as there is no matching in L2
verify_collection_address() will return address_zero as l2_req is address(0) and there is no matching.
Since the returned value is address zero, we will deploy a new address for that L1 Collection on L2, and match it on L2
Now L2 has collection matched but L1 has not.
UserA tried to Bridge Tokens from the same collection but this time from L2 to L1.
Calling L2Bridge::deposit_tokens(), and req.collection_l1 will be the original L1 collection address that is matched to the L2 collection address we deployed in the prev steps.
Starknet Sequencer accepted the message after verification.
UserA called L1Bridge::withdrawTokens(), and we will verify the request.
req.collectionL1 is the original NFT address on L1 we got it as there is a matching on L2 and req.collectionL2 is the address we deployed on L2 for that collection when we first bridge from L1 to L2, so both values are set with values.
Inside _verifyRequestAddresses, l1Req and l2Req have correct values as illustrated in the prev step, so we will go on to the second if condition.
Since there is no matching in L1, l1Mapping and l2Mapping are address(0), and will go for the check l1Mapping != l1Req, which will be true, ending up withdrawing from L1 getting reverted.
Add the following test function function in apps/blockchain/ethereum/test/Bridge.t.sol.
In the cmd write the following command.
Output
The function will revert with an error message InvalidCollectionL1Address()
Lossing of NFTs Bridged from L2 to L1
Manual Review + Foundry
Do not check the correct l1<->l2 matching on L1 and L2 if the L1 has no matching yet.
We illustrated the issue when Bridging tokens from L2 to L1 and they have matchings on L2 but not in L1, this issue existed also when Bridging from L1 to L2 when L1 has a matching but L2 has not. where the implementaion of the verification function is the same.
collection_manager.cairo#L170-L200
If l1_req and l2_req have values (there is a matching in L1) we will go for the else block, and since l2_bridge is address_zero (no matching on L2) withdrawing process will revert on L2.
The scenario is the same as we illustrated but with replacing L1 by L2 and L2 by L1. So to not repeat ourselves we mentioned it here briefly.
To mitigate the issue on L2 we will do the same as in L1 where if there is no matching in L2 but there is in L1 we will just return the addresses.
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.
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.