{"record":{"id":"6cc93e724e50ca51","repo":"linera-io/linera-protocol","slug":"deposit-amount-exceeds-u128","errorCode":null,"errorMessage":"deposit amount exceeds u128","messagePattern":"deposit amount exceeds u128","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/contracts/evm-bridge/src/contract.rs","lineNumber":298,"sourceCode":"            .processed_deposits\n            .insert(&deposit_hash)\n            .expect(\"failed to insert deposit hash\");\n\n        // 5b. Cache the verified block hash so subsequent deposits from the same\n        //     block skip the RPC finality check.\n        if !self.state.rpc_endpoint.get().is_empty() {\n            self.state\n                .verified_block_hashes\n                .insert(&block_hash.0)\n                .expect(\"failed to cache verified block hash\");\n        }\n\n        // 6. Convert deposit fields to Linera types and call Mint\n        let amount = U128(\n            deposit\n                .amount\n                .try_into()\n                .expect(\"deposit amount exceeds u128\"),\n        );\n\n        let mint_op = WrappedFungibleOperation::MintAndTransfer {\n            target_account: Account {\n                chain_id: deposit.target_chain_id,\n                owner: deposit.target_account_owner,\n            },\n            amount,\n        };\n\n        // Forward authenticated signer (chain owner = minter) to the fungible app.\n        let fungible_app_id = params.fungible_app_id.with_abi::<WrappedFungibleTokenAbi>();\n        self.runtime\n            .call_application(true, fungible_app_id, &mint_op);\n    }\n\n    /// Drives a user-initiated burn. Runs on the *user's* chain: moves `amount`\n    /// of the authenticated signer's wrapped tokens into the signer's own escrow","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/contracts/evm-bridge/src/contract.rs#L280-L316","documentation":"process_deposit converts the parsed deposit amount from a 256-bit integer to u128 (Linera's Amount/U128) and expects success. The panic means the on-chain DepositInitiated event carried an amount larger than 2^128-1, which the wrapped-fungible token cannot represent. The transaction aborts, so no Mint is issued.","triggerScenarios":"A malicious or buggy source contract emits DepositInitiated with an amount >= 2^128 (e.g. type-cast from uint256 max, or a wei-amount with absurd decimals); a relayer relays such an event instead of filtering it; fuzzed deposit fixtures with U256::MAX.","commonSituations":"Bridging from contracts that mint 2^256-ish supply tokens or use unscaled wei amounts; testnets with deliberately extreme values; token decimals mismatches where the feeder multiplies instead of divides.","solutions":["In the relayer, filter deposits before submission: skip and alert on amount > u128::MAX","Fix or avoid the source contract that emits such amounts (cap at mint time on the EVM side)","If you maintain the bridge, add a configurable per-deposit cap assert before the conversion for a clearer rejection message","For legitimate huge supplies, bridge a scaled representation (e.g. 18-decimal normalization) rather than raw wei"],"exampleFix":"// before\nsubmit(ProcessDeposit { .. }); // contract aborts: deposit amount exceeds u128\n\n// after — relayer-side filter\nlet amount: u128 = deposit.amount\n    .try_into()\n    .map_err(|_| anyhow!(\"deposit {} exceeds u128 — skipping and alerting\", deposit.amount))?;\nif amount > MAX_BRIDGEABLE {\n    notify_ops(\"oversized deposit\", deposit);\n    return Ok(());\n}\nsubmit(ProcessDeposit { .. });","handlingStrategy":"validation","validationCode":"// Filter oversized deposits before submission:\nmatch u128::try_from(deposit.amount) {\n    Ok(amount) if amount <= max_bridgeable => submit(ProcessDeposit { .. }),\n    _ => { alert_ops(format!(\"oversized deposit {} skipped\", deposit.amount)); }","typeGuard":"fn deposit_amount_representable(amount: alloy_primitives::U256) -> bool {\n    amount <= alloy_primitives::U256::from(u128::MAX)\n}","tryCatchPattern":"// The contract aborts atomically; there is no partial mint. Relayer-side,\n// route oversized amounts to an exception queue for manual review instead of\n// retrying.","preventionTips":["Enforce a sane per-deposit cap on the EVM side when the token contract is yours","Normalize decimals at the boundary instead of bridging raw wei of huge-supply tokens","Alert on any deposit > u128::MAX as likely contract malfunction"],"tags":["linera","bridge","ethereum","u256","integer-overflow","amount","panic"],"backgroundTag":"integer-overflow","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}