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

Insufficient Gas Fee Validation in Cross-Chain NFT Bridge

Summary

The smart contract allows users to deposit NFTs into an escrow and initiate a cross-chain transfer to StarkNet (L2) with an arbitrary gas fee. The lack of a meaningful minimum gas fee requirement can result in NFTs being locked in the escrow contract if the L2 message is never processed due to insufficient gas.

[Link](https://github.com/Cyfrin/2024-07-ark-project/blob/main/apps/blockchain/ethereum/src/Bridge.sol#L84)

function depositTokens(
uint256 salt,
address collectionL1,
snaddress ownerL2,
uint256[] calldata ids,
bool useAutoBurn
)
external
payable
{
if (!Cairo.isFelt252(snaddress.unwrap(ownerL2))) {
revert CairoWrapError();
}
if (!_enabled) {
revert BridgeNotEnabledError();
}
CollectionType ctype = TokenUtil.detectInterface(collectionL1);
if (ctype == CollectionType.ERC1155) {
revert NotSupportedYetError();
}
if (!_isWhiteListed(collectionL1)) {
revert NotWhiteListedError();
}
Request memory req;
// The withdraw auto is only available for request originated from
// Starknet side as the withdraw on starknet is automatically done
// by the sequencer.
req.header = Protocol.requestHeaderV1(ctype, useAutoBurn, false);
req.hash = Protocol.requestHash(salt, collectionL1, ownerL2, ids);
// TODO: store request hash in storage to avoid replay attack.
// or can it be safe to use block timestamp? Not sure as
// several tx may have the exact same block.
req.collectionL1 = collectionL1;
req.collectionL2 = _l1ToL2Addresses[collectionL1];
req.ownerL1 = msg.sender;
req.ownerL2 = ownerL2;
if (ctype == CollectionType.ERC721) {
(req.name, req.symbol, req.uri, req.tokenURIs) = TokenUtil.erc721Metadata(
collectionL1,
ids
);
} else {
(req.uri) = TokenUtil.erc1155Metadata(collectionL1);
}
_depositIntoEscrow(ctype, collectionL1, ids);
req.tokenIds = ids;
uint256[] memory payload = Protocol.requestSerialize(req);
if (payload.length >= MAX_PAYLOAD_LENGTH) {
revert TooManyTokensError();
}
IStarknetMessaging(_starknetCoreAddress).sendMessageToL2{value: msg.value}(
snaddress.unwrap(_starklaneL2Address),
felt252.unwrap(_starklaneL2Selector),
payload
);
emit DepositRequestInitiated(req.hash, block.timestamp, payload);
}
function sendMessageToL2(
uint256 toAddress,
uint256 selector,
uint256[] calldata payload
) external payable override returns (bytes32, uint256) {
require(msg.value > 0, "L1_MSG_FEE_MUST_BE_GREATER_THAN_0");
require(msg.value <= getMaxL1MsgFee(), "MAX_L1_MSG_FEE_EXCEEDED");
uint256 nonce = l1ToL2MessageNonce(); // seems this is static
NamedStorage.setUintValue(L1L2_MESSAGE_NONCE_TAG, nonce + 1);
emit LogMessageToL2(msg.sender, toAddress, selector, payload, nonce, msg.value);
bytes32 msgHash = getL1ToL2MsgHash(toAddress, selector, payload, nonce);
// Note that the inclusion of the unique nonce in the message hash implies that
// l1ToL2Messages()[msgHash] was not accessed before.
l1ToL2Messages()[msgHash] = msg.value + 1;
return (msgHash, nonce);
}

Vulnerability Details

Users can deposit their NFTs into the escrow contract with a very low gas fee (e.g., 1-10 wei).

  • The L2 message might never be processed due to the low gas fee, as nodes are unlikely to pick up such transactions.

  • The NFTs become stuck in the escrow contract, as the L2 side never receives the message to complete the bridge process.

  • There's no apparent mechanism to retrieve the NFTs if the L2 message fails.

Impact

Users can potentially lose access to their NFTs indefinitely if they provide an insufficient gas fee.

Tools Used

Manual Review

Recommendations

1.) Implement an higher minimum gas fee

2.) Add a reclaim mechanism: Implement a function that allows users to reclaim their NFTs after a certain period if the L2 transaction hasn't been processed.

uint256 constant MIN_GAS_FEE = 1e15; // 0.001 ETH or adjust as needed
require(msg.value >= MIN_GAS_FEE, "Insufficient gas fee");
Updates

Lead Judging Commences

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

finding-not-enough-fee-can-block-NFT

Impact: Medium/High. Need an admin to start a cancellation and wait for 5 days once done. DoS > 5 days. Likelyhood: Low. Everytime a wallet/or a user do not send enough gas

Support

FAQs

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