NFTBridge
60,000 USDC
View results
Submission Details
Severity: medium
Valid

Auto withdrawal enabled on L2 leads to reverting to L1 thus locking of token

Summary

The auto-withdrawal flag is disabled on Ethereum but there is still an option in starknet (L2) contracts to send the tokens with the auto-withdrawal flag enabled. If a user sends a valid token with auto withdrawal enabled. The L1 withdrawal function would revert and a user can’t claim a token.

Vulnerability Details

The auto-withdrawal flag is used only for the L2 to L1 messaging i.e. from Starknet to Ethereum. The auto-withdrawal functionality is disabled atm in Ethereum but from starknet, any user can set auto-withdrawal to true without any restrictions. If a request with auto-withdrawal true is sent from L2 to L1 the message gets passed but the L1 withdrawal function reverts while withdrawing the token. Starknet and Ethereum smart context are different as both are distinct chains. The revert doesn’t apply to the state reversal in starknet, the token gets locked in the bridge in Starknet and the user can’t withdraw in Ethereum.

The depositToken function takes, use_withdraw_auto in bridge.cairo:248

fn deposit_tokens(
ref self: ContractState,
salt: felt252,
collection_l2: ContractAddress,
owner_l1: EthAddress,
token_ids: Span<u256>,
use_withdraw_auto: bool,
use_deposit_burn_auto: bool,
) {

There is no validation of it and it is used to compute the request header in request.cairo:127 where bitwise OR is used to compute the header with WITHDRAW_AUTO config request.cairo:25

fn compute_request_header_v1(
ctype: CollectionType,
use_deposit_burn_auto: bool,
use_withdraw_auto: bool,
) -> felt252 {
let mut header: u256 = HEADER_V1;
if (ctype == CollectionType::ERC721) {
header = header | ERC721_TYPE;
} else if (ctype == CollectionType::ERC1155) {
header = header | ERC1155_TYPE;
} else {
panic_with_felt252('Invalid collection type');
}
if (use_deposit_burn_auto) {
header = header | DEPOSIT_AUTO_BURN;
}
if (use_withdraw_auto) {
header = header | WITHDRAW_AUTO;
}
header.try_into().expect('Invalid header V1')
}

Message gets passed and if branch in L1 bridge.sol:169 checks if it WITHDRAW_AUTO flag is true or not and reverts.
Protocol.sol:57

// Byte 3 of the header: withdraw config.
uint256 private constant WITHDRAW_AUTO = (0x01 << (8 * 3));
/**
@notice Verifies if the given header supports the withdraw auto.
@return True if withdraw auto is supported, false otherwise.
*/
function canUseWithdrawAuto(
uint256 header
)
internal
pure
returns (bool)
{
return (header & WITHDRAW_AUTO) == WITHDRAW_AUTO;
}

Impact

User ERC721/token would be temporarily locked in the starknet bridge where there is no external function to withdraw the token by the user. Starknet sequencer can only call the l1 handler entry-point function to mint or transfer from the escrow. The user still has the chance to get back their token by communicating with the bridge admin and individual transfer from the admin. Again if the admin transfers manually the data redundancy would be there in the deposit escrow function regarding the user token data.

Users get impacted by NFT being locked in Starknet and the bug violates the feature of the protocol.

Tools Used

Manual

Recommendations

Assert to only allow tokens with auto-withdrawal flag set to false or force the value at the contract level.

Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-auto_withdrawn-L2-NFT-stuck

Impact: High, token will be stuck in L2 bridge. Likelyhood: Very low, option is available in L2 but has been disabled since March on L1, would be almost a user error.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.