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.