TW
Tripwire
Find contracts. Test them. Review real vulns.

Analysis Run

019c0ea9-07a4-7162-9dd3-67d9c56c3671

failed
Chain ID
1
Address
Block Number
Created
Fri, Jan 30, 2026 11:28 AM
Review Summary

What survived the funnel

Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?

Confirmed
0
Validated
0
Source
Verified
Review Backing
Present
Best current signal
Untrusted DELEGATECALL target reachable
crit 0 high 0 proxy context implementation known
Review Checklist
1. Read the findings table first. Ignore raw jobs until the result looks real.
2. Confirm attacker control, not just the existence of a sink.
3. Confirm money or privilege impact.
4. Keep it if the behavior is real. Kill it if this is normal proxy, admin, or upgrade plumbing.

Processing Jobs

Status & attempts
ID Type Status Attempts Heartbeat
41641 artifact.fetch complete 1 / 3 2 months ago
41642 proxy.resolve complete 1 / 3 2 months ago
41643 tool.cast_disasm complete 1 / 3 2 months ago
41644 analysis.bundle complete 1 / 3 2 months ago
41645 capability.graph complete 1 / 3 2 months ago
41646 detector.run complete 1 / 3 2 months ago
41647 validation.fork complete 1 / 3 2 months ago
42242 report.consolidate complete 1 / 6 2 months ago
42243 tool.slither failed 2 / 2 2 months ago

Artifact

Runtime bytecode: available
Creation TX:
Deployer:
Proxy: yes confirmed
Verified Source: yes
Proxy evidence
{
    "method": "etherscan+onchain",
    "status": "confirmed",
    "etherscan_hint": {
        "source": "etherscan",
        "etherscan": {
            "proxy_flag": true,
            "implementation": "0x3203e813930bd710043c1d899fe38dd359307352"
        }
    },
    "implementation": "0x3203e813930bd710043c1d899fe38dd359307352",
    "onchain_method": "eip1967"
}

Slither

tool.slither
Status
failed slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-i08qivd9) 'forge config --json' running 'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-i08qivd9) Traceback (most recent call last): File "/var/www/tripwire/.venv/bin/slither", line 10, in <module> sys.exit(main()) ~~~~^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main main_impl(all_detector_classes=detectors, all_printer_classes=printers) ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl ) = process_all(filename, args, detector_classes, printer_classes) ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all compilations = compile_all(target, **vars(args)) File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all compilations.append(CryticCompile(target, **kwargs)) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__ self._compile(**kwargs) ~~~~~~~~~~~~~^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile self._platform.compile(self, **kwargs) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile hardhat_like_parsing( ~~~~~~~~~~~~~~~~~~~~^ crytic_compile, str(self._target), build_directory, str(self._project_root) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing raise InvalidCompilation(txt) crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command? /tmp/slither-i08qivd9/out/build-info is not a directory.
Attempts
2 / 2
Findings
0
Solc
View stderr
Traceback (most recent call last):
  File "/var/www/tripwire/worker/worker.py", line 198, in main
    payload = dispatch_job(job_type, job_id, job_meta, run_data, config, laravel_client)
  File "/var/www/tripwire/worker/worker.py", line 114, in dispatch_job
    return slither_run.run(job_id, job_meta, run_data, config, log, laravel_client)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/worker/jobs/slither_run.py", line 1084, in run
    stdout_text, stderr_text, result_json = _run_slither(
                                            ~~~~~~~~~~~~^
        workspace,
        ^^^^^^^^^^
    ...<4 lines>...
        target_path=target_path,
        ^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/var/www/tripwire/worker/jobs/slither_run.py", line 958, in _run_slither
    raise RuntimeError(f"slither failed (exit {result.returncode}): {stderr_text or stdout_text}")
RuntimeError: slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-i08qivd9)
'forge config --json' running
'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-i08qivd9)
Traceback (most recent call last):
  File "/var/www/tripwire/.venv/bin/slither", line 10, in <module>
    sys.exit(main())
             ~~~~^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main
    main_impl(all_detector_classes=detectors, all_printer_classes=printers)
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl
    ) = process_all(filename, args, detector_classes, printer_classes)
        ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all
    compilations = compile_all(target, **vars(args))
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all
    compilations.append(CryticCompile(target, **kwargs))
                        ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__
    self._compile(**kwargs)
    ~~~~~~~~~~~~~^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile
    self._platform.compile(self, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile
    hardhat_like_parsing(
    ~~~~~~~~~~~~~~~~~~~~^
        crytic_compile, str(self._target), build_directory, str(self._project_root)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing
    raise InvalidCompilation(txt)
crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command?
/tmp/slither-i08qivd9/out/build-info is not a directory.

Codex

report.consolidate
Job Status
complete
Attempts
1 / 6
Findings
2
No findings?
no
Summary
Two issues found: the proxy initializer is unrestricted (allowing first-caller takeover if not initialized atomically) and refund recipient aliasing is not enforced when a contract address is provided, which can strand refunds on L2.
View stdout
{"no_findings":false,"summary":"Two issues found: the proxy initializer is unrestricted (allowing first-caller takeover if not initialized atomically) and refund recipient aliasing is not enforced when a contract address is provided, which can strand refunds on L2.","findings":[{"severity":"high","category":"upgradeability","title":"Unrestricted initialize allows proxy takeover if not initialized atomically","exploit_scenario":"If the proxy is deployed without atomically calling `initialize`, any account can call it first and set `l2Bridge`, `l2TokenBeacon`, and `l2TokenProxyBytecodeHash` to attacker-controlled values. The attacker can then deploy or point `l2Bridge` to a malicious L2 contract that emits withdrawal messages to drain L1 token balances or permanently disrupt deposits/withdrawals.","recommendation":"Require an access control check on `initialize` (e.g., onlyProxyAdmin/onlyOwner) or ensure the proxy is deployed with initialization calldata in the same transaction so no external caller can front-run the first initialization.","evidence":"`initialize` is `external payable` and only guarded by `reentrancyGuardInitializer`, with no caller restriction or admin check.","locations":["contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol:82"]},{"severity":"low","category":"other","title":"Refund recipient aliasing not enforced for explicit L1 contract addresses","exploit_scenario":"A user (or UI) may pass an L1 contract address as `_refundRecipient` expecting automatic aliasing. The code only aliases when `_refundRecipient == address(0)`, so the refund will be sent to the same address on L2, which the L1 contract does not control. If the L2 transaction fails, the refund can be lost or claimed by whoever controls that L2 address.","recommendation":"If `_refundRecipient` is a contract on L1, automatically apply `AddressAliasHelper.applyL1ToL2Alias`, or explicitly require callers to pass an already-aliased L2 address and update the documentation accordingly.","evidence":"Aliasing is only applied when `_refundRecipient == address(0)`; there is no contract-detection branch for explicit `_refundRecipient` values despite the comment implying it.","locations":["contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol:264"]}]}
View stderr
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-noz_u1fv
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019c0ee2-3807-7eb2-8027-24c037ed242d
--------
user
Context: The user requested the proxy at 0xad16edcf7deb7e90096a259c81269d811544b6b6.
Audit the implementation at 0x3203e813930bd710043c1d899fe38dd359307352.
Findings should refer to the implementation unless explicitly about proxy mechanics.

You are a security auditor. Audit this Solidity codebase for exploitable vulnerabilities.
Focus on: reentrancy, access control, upgradeability/proxy issues, signature validation, oracle manipulation,
accounting/invariant breaks, external call patterns, unchecked return values, and dangerous delegatecall.

Scope rules:
- You MUST fully read `AUDIT_BUNDLE.txt` before concluding. It contains the complete contents of all first-party Solidity sources in this workspace.
- Treat `lib/` and `@openzeppelin/` as third-party dependencies; skip them unless there is a concrete misuse angle in first-party code.
- If there are only dependency files and no first-party sources, set no_findings=true and explain in summary.

Output MUST be valid JSON matching the provided schema.
- If you find nothing meaningful: set no_findings=true and findings=[].
- Otherwise: set no_findings=false and include findings with severity/category/title and file/line locations when possible.

Output constraints (IMPORTANT):
- severity MUST be exactly one of: info, low, medium, high, critical (lowercase).
- category MUST be exactly one of: access_control, reentrancy, math, oracle, upgradeability, auth, other.
Keep summary short and concrete.
mcp startup: no servers
exec
/bin/bash -lc 'cat AUDIT_BUNDLE.txt' in /tmp/codex-noz_u1fv succeeded in 6ms:
This file concatenates the full contents of first-party Solidity sources for auditing.
Do not treat dependency code under lib/ or @openzeppelin/ as first-party.

================================================================================
FILE: contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol
================================================================================
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IL1BridgeLegacy} from "./interfaces/IL1BridgeLegacy.sol";
import {IL1Bridge} from "./interfaces/IL1Bridge.sol";
import {IL2Bridge} from "./interfaces/IL2Bridge.sol";
import {IL2ERC20Bridge} from "./interfaces/IL2ERC20Bridge.sol";

import {BridgeInitializationHelper} from "./libraries/BridgeInitializationHelper.sol";
exec
/bin/bash -lc "sed -n '1,240p' contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol" in /tmp/codex-noz_u1fv succeeded in 6ms:
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IL1BridgeLegacy} from "./interfaces/IL1BridgeLegacy.sol";
import {IL1Bridge} from "./interfaces/IL1Bridge.sol";
import {IL2Bridge} from "./interfaces/IL2Bridge.sol";
import {IL2ERC20Bridge} from "./interfaces/IL2ERC20Bridge.sol";

import {BridgeInitializationHelper} from "./libraries/BridgeInitializationHelper.sol";

import {IMailbox, TxStatus} from "../zksync/interfaces/IMailbox.sol";
import {L2Message} from "../zksync/Storage.sol";
import {UnsafeBytes} from "../common/libraries/UnsafeBytes.sol";
import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol";
import {ReentrancyGuard} from "../common/ReentrancyGuard.sol";
exec
/bin/bash -lc "sed -n '240,520p' contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol" in /tmp/codex-noz_u1fv succeeded in 5ms:
        l2TxHash = _deposit(
            _l2Receiver,
            _l1Token,
            _amount,
            _l2TxGasLimit,
            _l2TxGasPerPubdataByte,
            _refundRecipient,
            true
        );
    }

    function _deposit(
        address _l2Receiver,
        address _l1Token,
        uint256 _amount,
        uint256 _l2TxGasLimit,
        uint256 _l2TxGasPerPubdataByte,
        address _refundRecipient,
        bool _toMerge
    ) internal returns (bytes32 l2TxHash) {
exec
/bin/bash -lc "rg --files -g '*.sol'" in /tmp/codex-noz_u1fv succeeded in 34ms:
@openzeppelin/contracts/token/ERC20/IERC20.sol
@openzeppelin/contracts/utils/Address.sol
@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol
contracts/zksync/l1-contracts/zksync/interfaces/IMailbox.sol
contracts/zksync/l1-contracts/zksync/Storage.sol
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol
contracts/zksync/l1-contracts/bridge/libraries/BridgeInitializationHelper.sol
contracts/zksync/l1-contracts/bridge/interfaces/IL1Bridge.sol
contracts/zksync/l1-contracts/bridge/interfaces/IL2ERC20Bridge.sol
contracts/zksync/l1-contracts/bridge/interfaces/IL2Bridge.sol
contracts/zksync/l1-contracts/bridge/interfaces/IL1BridgeLegacy.sol
contracts/zksync/l1-contracts/common/ReentrancyGuard.sol
contracts/zksync/l1-contracts/vendor/AddressAliasHelper.sol
contracts/zksync/l1-contracts/common/L2ContractAddresses.sol
contracts/zksync/l1-contracts/common/interfaces/IL2ContractDeployer.sol
contracts/zksync/l1-contracts/common/libraries/L2ContractHelper.sol
contracts/zksync/l1-contracts/common/libraries/UnsafeBytes.sol
exec
/bin/bash -lc "sed -n '1,240p' contracts/zksync/l1-contracts/bridge/libraries/BridgeInitializationHelper.sol" in /tmp/codex-noz_u1fv succeeded in 8ms:
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../../zksync/interfaces/IMailbox.sol";
import "../../vendor/AddressAliasHelper.sol";
import "../../common/libraries/L2ContractHelper.sol";
import {L2_DEPLOYER_SYSTEM_CONTRACT_ADDR} from "../../common/L2ContractAddresses.sol";
import "../../common/interfaces/IL2ContractDeployer.sol";

/// @author Matter Labs
/// @custom:security-contact security@matterlabs.dev
/// @dev A helper library for initializing L2 bridges in zkSync L2 network.
library BridgeInitializationHelper {
    /// @dev The L2 gas limit for requesting L1 -> L2 transaction of deploying L2 bridge instance.
    /// @dev It is big enough to deploy any contract, so we can use the same value for all bridges.
    /// NOTE: this constant will be accurately calculated in the future.
    uint256 constant DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT = 10000000;

    /// @dev The default l2GasPricePerPubdata to be used in bridges.
exec
/bin/bash -lc "sed -n '1,200p' contracts/zksync/l1-contracts/common/ReentrancyGuard.sol" in /tmp/codex-noz_u1fv succeeded in 6ms:
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @custom:security-contact security@matterlabs.dev
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
exec
/bin/bash -lc "sed -n '1,240p' contracts/zksync/l1-contracts/common/libraries/L2ContractHelper.sol" in /tmp/codex-noz_u1fv succeeded in 6ms:
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @author Matter Labs
 * @custom:security-contact security@matterlabs.dev
 * @notice Helper library for working with L2 contracts on L1.
 */
library L2ContractHelper {
    /// @dev The prefix used to create CREATE2 addresses.
    bytes32 private constant CREATE2_PREFIX = keccak256("zksyncCreate2");

    /// @notice Validate the bytecode format and calculate its hash.
    /// @param _bytecodeLength The bytecode length.
    /// @param _bytecodeHash The bytecode hash.
    /// @return hashedBytecode The 32-byte hash of the bytecode.
    /// Note: The function reverts the execution if the bytecode has non expected format:
    /// - Bytecode bytes length is not a multiple of 32
    /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words)
exec
/bin/bash -lc "sed -n '1,160p' contracts/zksync/l1-contracts/common/L2ContractAddresses.sol" in /tmp/codex-noz_u1fv succeeded in 7ms:
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @dev The address of the L2 deployer system contract.
address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(0x8006);

/// @dev The special reserved L2 address. It is located in the system contracts space but doesn't have deployed
/// bytecode.
/// @dev The L2 deployer system contract allows changing bytecodes on any address if the `msg.sender` is this address.
/// @dev So, whenever the governor wants to redeploy system contracts, it just initiates the L1 upgrade call deployer
/// system contract
/// via the L1 -> L2 transaction with `sender == L2_FORCE_DEPLOYER_ADDR`. For more details see the
/// `diamond-initializers` contracts.
address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007);

/// @dev The address of the special smart contract that can send arbitrary length message as an L2 log
address constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = address(0x8008);

/// @dev The formal address of the initial program of the system: the bootloader
exec
/bin/bash -lc "sed -n '1,200p' contracts/zksync/l1-contracts/bridge/interfaces/IL1Bridge.sol" in /tmp/codex-noz_u1fv succeeded in 11ms:
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @title L1 Bridge contract interface
/// @author Matter Labs
/// @custom:security-contact security@matterlabs.dev
interface IL1Bridge {
    event DepositInitiated(
        bytes32 indexed l2DepositTxHash,
        address indexed from,
        address indexed to,
        address l1Token,
        uint256 amount
    );

    event DepositToMergeInitiated(
        bytes32 indexed l2DepositTxHash,
        address indexed from,
        address indexed to,
exec
/bin/bash -lc "sed -n '1,200p' contracts/zksync/l1-contracts/bridge/interfaces/IL1BridgeLegacy.sol" in /tmp/codex-noz_u1fv succeeded in 6ms:
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @title L1 Bridge contract legacy interface
/// @author Matter Labs
/// @custom:security-contact security@matterlabs.dev
interface IL1BridgeLegacy {
    function deposit(
        address _l2Receiver,
        address _l1Token,
        uint256 _amount,
        uint256 _l2TxGasLimit,
        uint256 _l2TxGasPerPubdataByte
    ) external payable returns (bytes32 txHash);
}
exec
/bin/bash -lc "nl -ba contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol | sed -n '50,160p'" in /tmp/codex-noz_u1fv succeeded in 10ms:
    50	    /// @dev The bytecode hash of the L2 token contract
    51	    bytes32 public l2TokenProxyBytecodeHash;
    52	
    53	    mapping(address => uint256) private __DEPRECATED_lastWithdrawalLimitReset;
    54	
    55	    /// @dev A mapping L1 token address => the accumulated withdrawn amount during the withdrawal limit window
    56	    mapping(address => uint256) private __DEPRECATED_withdrawnAmountInWindow;
    57	
    58	    /// @dev The accumulated deposited amount per user.
    59	    /// @dev A mapping L1 token address => user address => the total deposited amount by the user
    60	    mapping(address => mapping(address => uint256)) private __DEPRECATED_totalDepositedAmountPerUser;
    61	
    62	    /// @dev Contract is expected to be used as proxy implementation.
    63	    /// @dev Initialize the implementation to prevent Parity hack.
    64	    constructor(IMailbox _zkSync) reentrancyGuardInitializer {
    65	        zkSync = _zkSync;
    66	    }
    67	
    68	    /// @dev Initializes a contract bridge for later use. Expected to be used in the proxy
    69	    /// @dev During initialization deploys L2 bridge counterpart as well as provides some factory deps for it
exec
/bin/bash -lc "nl -ba contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol | sed -n '200,320p'" in /tmp/codex-noz_u1fv succeeded in 6ms:
   200	            _amount,
   201	            _l2TxGasLimit,
   202	            _l2TxGasPerPubdataByte,
   203	            _refundRecipient,
   204	            false
   205	        );
   206	    }
   207	
   208	    /// @notice Initiates a deposit by locking funds on the contract and sending the request
   209	    /// of processing an L2 transaction where merge tokens would be minted
   210	    /// @dev If the token is bridged for the first time, the L2 token and merge token contract will be deployed. Note however, that the
   211	    /// newly-deployed token does not support any custom logic, i.e. rebase tokens' functionality is not supported.
   212	    /// @param _l2Receiver The account address that should receive funds on L2
   213	    /// @param _l1Token The L1 token address which is deposited
   214	    /// @param _amount The total amount of tokens to be bridged
   215	    /// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction
   216	    /// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction
   217	    /// @param _refundRecipient The address on L2 that will receive the refund for the transaction.
   218	    /// @dev If the L2 deposit finalization transaction fails, the `_refundRecipient` will receive the `_l2Value`.
   219	    /// Please note, the contract may change the refund recipient's address to eliminate sending funds to addresses
codex
{"no_findings":false,"summary":"Two issues found: the proxy initializer is unrestricted (allowing first-caller takeover if not initialized atomically) and refund recipient aliasing is not enforced when a contract address is provided, which can strand refunds on L2.","findings":[{"severity":"high","category":"upgradeability","title":"Unrestricted initialize allows proxy takeover if not initialized atomically","exploit_scenario":"If the proxy is deployed without atomically calling `initialize`, any account can call it first and set `l2Bridge`, `l2TokenBeacon`, and `l2TokenProxyBytecodeHash` to attacker-controlled values. The attacker can then deploy or point `l2Bridge` to a malicious L2 contract that emits withdrawal messages to drain L1 token balances or permanently disrupt deposits/withdrawals.","recommendation":"Require an access control check on `initialize` (e.g., onlyProxyAdmin/onlyOwner) or ensure the proxy is deployed with initialization calldata in the same transaction so no external caller can front-run the first initialization.","evidence":"`initialize` is `external payable` and only guarded by `reentrancyGuardInitializer`, with no caller restriction or admin check.","locations":["contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol:82"]},{"severity":"low","category":"other","title":"Refund recipient aliasing not enforced for explicit L1 contract addresses","exploit_scenario":"A user (or UI) may pass an L1 contract address as `_refundRecipient` expecting automatic aliasing. The code only aliases when `_refundRecipient == address(0)`, so the refund will be sent to the same address on L2, which the L1 contract does not control. If the L2 transaction fails, the refund can be lost or claimed by whoever controls that L2 address.","recommendation":"If `_refundRecipient` is a contract on L1, automatically apply `AddressAliasHelper.applyL1ToL2Alias`, or explicitly require callers to pass an already-aliased L2 address and update the documentation accordingly.","evidence":"Aliasing is only applied when `_refundRecipient == address(0)`; there is no contract-detection branch for explicit `_refundRecipient` values despite the comment implying it.","locations":["contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol:264"]}]}
tokens used
62,757
Metadata
{
    "codex": {
        "ran_at": "2026-01-30T12:32:46.632757+00:00",
        "returncode": 0
    },
    "project": {
        "mode": "source",
        "file_count": 19,
        "import_count": 23,
        "first_party_contracts_count": 14
    },
    "summary": "Two issues found: the proxy initializer is unrestricted (allowing first-caller takeover if not initialized atomically) and refund recipient aliasing is not enforced when a contract address is provided, which can strand refunds on L2.",
    "raw_final": {
        "summary": "Two issues found: the proxy initializer is unrestricted (allowing first-caller takeover if not initialized atomically) and refund recipient aliasing is not enforced when a contract address is provided, which can strand refunds on L2.",
        "findings": [
            {
                "title": "Unrestricted initialize allows proxy takeover if not initialized atomically",
                "category": "upgradeability",
                "evidence": "`initialize` is `external payable` and only guarded by `reentrancyGuardInitializer`, with no caller restriction or admin check.",
                "severity": "high",
                "locations": [
                    "contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol:82"
                ],
                "recommendation": "Require an access control check on `initialize` (e.g., onlyProxyAdmin/onlyOwner) or ensure the proxy is deployed with initialization calldata in the same transaction so no external caller can front-run the first initialization.",
                "exploit_scenario": "If the proxy is deployed without atomically calling `initialize`, any account can call it first and set `l2Bridge`, `l2TokenBeacon`, and `l2TokenProxyBytecodeHash` to attacker-controlled values. The attacker can then deploy or point `l2Bridge` to a malicious L2 contract that emits withdrawal messages to drain L1 token balances or permanently disrupt deposits/withdrawals."
            },
            {
                "title": "Refund recipient aliasing not enforced for explicit L1 contract addresses",
                "category": "other",
                "evidence": "Aliasing is only applied when `_refundRecipient == address(0)`; there is no contract-detection branch for explicit `_refundRecipient` values despite the comment implying it.",
                "severity": "low",
                "locations": [
                    "contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol:264"
                ],
                "recommendation": "If `_refundRecipient` is a contract on L1, automatically apply `AddressAliasHelper.applyL1ToL2Alias`, or explicitly require callers to pass an already-aliased L2 address and update the documentation accordingly.",
                "exploit_scenario": "A user (or UI) may pass an L1 contract address as `_refundRecipient` expecting automatic aliasing. The code only aliases when `_refundRecipient == address(0)`, so the refund will be sent to the same address on L2, which the L1 contract does not control. If the L2 transaction fails, the refund can be lost or claimed by whoever controls that L2 address."
            }
        ],
        "no_findings": false
    },
    "no_findings": false,
    "proxy_context": {
        "proxy_run_id": "019c0ea9-07a4-7162-9dd3-67d9c56c3671",
        "proxy_address": "0xad16edcf7deb7e90096a259c81269d811544b6b6",
        "implementation_run_id": "019c0ea9-c986-7300-a119-1cc3848d4c55",
        "implementation_address": "0x3203e813930bd710043c1d899fe38dd359307352"
    },
    "schema_version": 1
}

Opcode Heuristics

tool.cast_disasm
Delegatecall
2
Selfdestruct
0
CREATE2
0
CALL-family (heavy)
0
EXT*/BALANCE
2
Total opcodes
717
Flags
delegatecall_present
View cast disassembly output
00000000: PUSH1 0x80
00000002: PUSH1 0x40
00000004: MSTORE
00000005: PUSH2 0x000c
00000008: PUSH2 0x000e
0000000b: JUMP
0000000c: JUMPDEST
0000000d: STOP
0000000e: JUMPDEST
0000000f: PUSH32 0x000000000000000000000000315255c1ba35a1ddac48cf054bc4e3a0929160b2
00000030: PUSH1 0x01
00000032: PUSH1 0x01
00000034: PUSH1 0xa0
00000036: SHL
00000037: SUB
00000038: AND
00000039: CALLER
0000003a: SUB
0000003b: PUSH2 0x007b
0000003e: JUMPI
0000003f: PUSH1 0x00
00000041: CALLDATALOAD
00000042: PUSH1 0x01
00000044: PUSH1 0x01
00000046: PUSH1 0xe0
00000048: SHL
00000049: SUB
0000004a: NOT
0000004b: AND
0000004c: PUSH4 0x278f7943
00000051: PUSH1 0xe1
00000053: SHL
00000054: EQ
00000055: PUSH2 0x0071
00000058: JUMPI
00000059: PUSH1 0x40
0000005b: MLOAD
0000005c: PUSH4 0x34ad5dbb
00000061: PUSH1 0xe2
00000063: SHL
00000064: DUP2
00000065: MSTORE
00000066: PUSH1 0x04
00000068: ADD
00000069: PUSH1 0x40
0000006b: MLOAD
0000006c: DUP1
0000006d: SWAP2
0000006e: SUB
0000006f: SWAP1
00000070: REVERT
00000071: JUMPDEST
00000072: PUSH2 0x0079
00000075: PUSH2 0x0083
00000078: JUMP
00000079: JUMPDEST
0000007a: JUMP
0000007b: JUMPDEST
0000007c: PUSH2 0x0079
0000007f: PUSH2 0x00b2
00000082: JUMP
00000083: JUMPDEST
00000084: PUSH1 0x00
00000086: DUP1
00000087: PUSH2 0x0093
0000008a: CALLDATASIZE
0000008b: PUSH1 0x04
0000008d: DUP2
0000008e: DUP5
0000008f: PUSH2 0x0312
00000092: JUMP
00000093: JUMPDEST
00000094: DUP2
00000095: ADD
00000096: SWAP1
00000097: PUSH2 0x00a0
0000009a: SWAP2
0000009b: SWAP1
0000009c: PUSH2 0x0352
0000009f: JUMP
000000a0: JUMPDEST
000000a1: SWAP2
000000a2: POP
000000a3: SWAP2
000000a4: POP
000000a5: PUSH2 0x00ae
000000a8: DUP3
000000a9: DUP3
000000aa: PUSH2 0x00c2
000000ad: JUMP
000000ae: JUMPDEST
000000af: POP
000000b0: POP
000000b1: JUMP
000000b2: JUMPDEST
000000b3: PUSH2 0x0079
000000b6: PUSH2 0x00bd
000000b9: PUSH2 0x011d
000000bc: JUMP
000000bd: JUMPDEST
000000be: PUSH2 0x0155
000000c1: JUMP
000000c2: JUMPDEST
000000c3: PUSH2 0x00cb
000000c6: DUP3
000000c7: PUSH2 0x0179
000000ca: JUMP
000000cb: JUMPDEST
000000cc: PUSH1 0x40
000000ce: MLOAD
000000cf: PUSH1 0x01
000000d1: PUSH1 0x01
000000d3: PUSH1 0xa0
000000d5: SHL
000000d6: SUB
000000d7: DUP4
000000d8: AND
000000d9: SWAP1
000000da: PUSH32 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b
000000fb: SWAP1
000000fc: PUSH1 0x00
000000fe: SWAP1
000000ff: LOG2
00000100: DUP1
00000101: MLOAD
00000102: ISZERO
00000103: PUSH2 0x0115
00000106: JUMPI
00000107: PUSH2 0x0110
0000010a: DUP3
0000010b: DUP3
0000010c: PUSH2 0x01f5
0000010f: JUMP
00000110: JUMPDEST
00000111: POP
00000112: POP
00000113: POP
00000114: JUMP
00000115: JUMPDEST
00000116: PUSH2 0x00ae
00000119: PUSH2 0x026b
0000011c: JUMP
0000011d: JUMPDEST
0000011e: PUSH1 0x00
00000120: PUSH2 0x0150
00000123: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
00000144: SLOAD
00000145: PUSH1 0x01
00000147: PUSH1 0x01
00000149: PUSH1 0xa0
0000014b: SHL
0000014c: SUB
0000014d: AND
0000014e: SWAP1
0000014f: JUMP
00000150: JUMPDEST
00000151: SWAP1
00000152: POP
00000153: SWAP1
00000154: JUMP
00000155: JUMPDEST
00000156: CALLDATASIZE
00000157: PUSH1 0x00
00000159: DUP1
0000015a: CALLDATACOPY
0000015b: PUSH1 0x00
0000015d: DUP1
0000015e: CALLDATASIZE
0000015f: PUSH1 0x00
00000161: DUP5
00000162: GAS
00000163: DELEGATECALL
00000164: RETURNDATASIZE
00000165: PUSH1 0x00
00000167: DUP1
00000168: RETURNDATACOPY
00000169: DUP1
0000016a: DUP1
0000016b: ISZERO
0000016c: PUSH2 0x0174
0000016f: JUMPI
00000170: RETURNDATASIZE
00000171: PUSH1 0x00
00000173: RETURN
00000174: JUMPDEST
00000175: RETURNDATASIZE
00000176: PUSH1 0x00
00000178: REVERT
00000179: JUMPDEST
0000017a: DUP1
0000017b: PUSH1 0x01
0000017d: PUSH1 0x01
0000017f: PUSH1 0xa0
00000181: SHL
00000182: SUB
00000183: AND
00000184: EXTCODESIZE
00000185: PUSH1 0x00
00000187: SUB
00000188: PUSH2 0x01b4
0000018b: JUMPI
0000018c: PUSH1 0x40
0000018e: MLOAD
0000018f: PUSH4 0x4c9c8ce3
00000194: PUSH1 0xe0
00000196: SHL
00000197: DUP2
00000198: MSTORE
00000199: PUSH1 0x01
0000019b: PUSH1 0x01
0000019d: PUSH1 0xa0
0000019f: SHL
000001a0: SUB
000001a1: DUP3
000001a2: AND
000001a3: PUSH1 0x04
000001a5: DUP3
000001a6: ADD
000001a7: MSTORE
000001a8: PUSH1 0x24
000001aa: ADD
000001ab: JUMPDEST
000001ac: PUSH1 0x40
000001ae: MLOAD
000001af: DUP1
000001b0: SWAP2
000001b1: SUB
000001b2: SWAP1
000001b3: REVERT
000001b4: JUMPDEST
000001b5: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
000001d6: DUP1
000001d7: SLOAD
000001d8: PUSH1 0x01
000001da: PUSH1 0x01
000001dc: PUSH1 0xa0
000001de: SHL
000001df: SUB
000001e0: NOT
000001e1: AND
000001e2: PUSH1 0x01
000001e4: PUSH1 0x01
000001e6: PUSH1 0xa0
000001e8: SHL
000001e9: SUB
000001ea: SWAP3
000001eb: SWAP1
000001ec: SWAP3
000001ed: AND
000001ee: SWAP2
000001ef: SWAP1
000001f0: SWAP2
000001f1: OR
000001f2: SWAP1
000001f3: SSTORE
000001f4: JUMP
000001f5: JUMPDEST
000001f6: PUSH1 0x60
000001f8: PUSH1 0x00
000001fa: DUP1
000001fb: DUP5
000001fc: PUSH1 0x01
000001fe: PUSH1 0x01
00000200: PUSH1 0xa0
00000202: SHL
00000203: SUB
00000204: AND
00000205: DUP5
00000206: PUSH1 0x40
00000208: MLOAD
00000209: PUSH2 0x0212
0000020c: SWAP2
0000020d: SWAP1
0000020e: PUSH2 0x0422
00000211: JUMP
00000212: JUMPDEST
00000213: PUSH1 0x00
00000215: PUSH1 0x40
00000217: MLOAD
00000218: DUP1
00000219: DUP4
0000021a: SUB
0000021b: DUP2
0000021c: DUP6
0000021d: GAS
0000021e: DELEGATECALL
0000021f: SWAP2
00000220: POP
00000221: POP
00000222: RETURNDATASIZE
00000223: DUP1
00000224: PUSH1 0x00
00000226: DUP2
00000227: EQ
00000228: PUSH2 0x024d
0000022b: JUMPI
0000022c: PUSH1 0x40
0000022e: MLOAD
0000022f: SWAP2
00000230: POP
00000231: PUSH1 0x1f
00000233: NOT
00000234: PUSH1 0x3f
00000236: RETURNDATASIZE
00000237: ADD
00000238: AND
00000239: DUP3
0000023a: ADD
0000023b: PUSH1 0x40
0000023d: MSTORE
0000023e: RETURNDATASIZE
0000023f: DUP3
00000240: MSTORE
00000241: RETURNDATASIZE
00000242: PUSH1 0x00
00000244: PUSH1 0x20
00000246: DUP5
00000247: ADD
00000248: RETURNDATACOPY
00000249: PUSH2 0x0252
0000024c: JUMP
0000024d: JUMPDEST
0000024e: PUSH1 0x60
00000250: SWAP2
00000251: POP
00000252: JUMPDEST
00000253: POP
00000254: SWAP2
00000255: POP
00000256: SWAP2
00000257: POP
00000258: PUSH2 0x0262
0000025b: DUP6
0000025c: DUP4
0000025d: DUP4
0000025e: PUSH2 0x028a
00000261: JUMP
00000262: JUMPDEST
00000263: SWAP6
00000264: SWAP5
00000265: POP
00000266: POP
00000267: POP
00000268: POP
00000269: POP
0000026a: JUMP
0000026b: JUMPDEST
0000026c: CALLVALUE
0000026d: ISZERO
0000026e: PUSH2 0x0079
00000271: JUMPI
00000272: PUSH1 0x40
00000274: MLOAD
00000275: PUSH4 0xb398979f
0000027a: PUSH1 0xe0
0000027c: SHL
0000027d: DUP2
0000027e: MSTORE
0000027f: PUSH1 0x04
00000281: ADD
00000282: PUSH1 0x40
00000284: MLOAD
00000285: DUP1
00000286: SWAP2
00000287: SUB
00000288: SWAP1
00000289: REVERT
0000028a: JUMPDEST
0000028b: PUSH1 0x60
0000028d: DUP3
0000028e: PUSH2 0x029f
00000291: JUMPI
00000292: PUSH2 0x029a
00000295: DUP3
00000296: PUSH2 0x02e9
00000299: JUMP
0000029a: JUMPDEST
0000029b: PUSH2 0x02e2
0000029e: JUMP
0000029f: JUMPDEST
000002a0: DUP2
000002a1: MLOAD
000002a2: ISZERO
000002a3: DUP1
000002a4: ISZERO
000002a5: PUSH2 0x02b6
000002a8: JUMPI
000002a9: POP
000002aa: PUSH1 0x01
000002ac: PUSH1 0x01
000002ae: PUSH1 0xa0
000002b0: SHL
000002b1: SUB
000002b2: DUP5
000002b3: AND
000002b4: EXTCODESIZE
000002b5: ISZERO
000002b6: JUMPDEST
000002b7: ISZERO
000002b8: PUSH2 0x02df
000002bb: JUMPI
000002bc: PUSH1 0x40
000002be: MLOAD
000002bf: PUSH4 0x9996b315
000002c4: PUSH1 0xe0
000002c6: SHL
000002c7: DUP2
000002c8: MSTORE
000002c9: PUSH1 0x01
000002cb: PUSH1 0x01
000002cd: PUSH1 0xa0
000002cf: SHL
000002d0: SUB
000002d1: DUP6
000002d2: AND
000002d3: PUSH1 0x04
000002d5: DUP3
000002d6: ADD
000002d7: MSTORE
000002d8: PUSH1 0x24
000002da: ADD
000002db: PUSH2 0x01ab
000002de: JUMP
000002df: JUMPDEST
000002e0: POP
000002e1: DUP1
000002e2: JUMPDEST
000002e3: SWAP4
000002e4: SWAP3
000002e5: POP
000002e6: POP
000002e7: POP
000002e8: JUMP
000002e9: JUMPDEST
000002ea: DUP1
000002eb: MLOAD
000002ec: ISZERO
000002ed: PUSH2 0x02f9
000002f0: JUMPI
000002f1: DUP1
000002f2: MLOAD
000002f3: DUP1
000002f4: DUP3
000002f5: PUSH1 0x20
000002f7: ADD
000002f8: REVERT
000002f9: JUMPDEST
000002fa: PUSH1 0x40
000002fc: MLOAD
000002fd: PUSH4 0x0a12f521
00000302: PUSH1 0xe1
00000304: SHL
00000305: DUP2
00000306: MSTORE
00000307: PUSH1 0x04
00000309: ADD
0000030a: PUSH1 0x40
0000030c: MLOAD
0000030d: DUP1
0000030e: SWAP2
0000030f: SUB
00000310: SWAP1
00000311: REVERT
00000312: JUMPDEST
00000313: PUSH1 0x00
00000315: DUP1
00000316: DUP6
00000317: DUP6
00000318: GT
00000319: ISZERO
0000031a: PUSH2 0x0322
0000031d: JUMPI
0000031e: PUSH1 0x00
00000320: DUP1
00000321: REVERT
00000322: JUMPDEST
00000323: DUP4
00000324: DUP7
00000325: GT
00000326: ISZERO
00000327: PUSH2 0x032f
0000032a: JUMPI
0000032b: PUSH1 0x00
0000032d: DUP1
0000032e: REVERT
0000032f: JUMPDEST
00000330: POP
00000331: POP
00000332: DUP3
00000333: ADD
00000334: SWAP4
00000335: SWAP2
00000336: SWAP1
00000337: SWAP3
00000338: SUB
00000339: SWAP2
0000033a: POP
0000033b: JUMP
0000033c: JUMPDEST
0000033d: PUSH4 0x4e487b71
00000342: PUSH1 0xe0
00000344: SHL
00000345: PUSH1 0x00
00000347: MSTORE
00000348: PUSH1 0x41
0000034a: PUSH1 0x04
0000034c: MSTORE
0000034d: PUSH1 0x24
0000034f: PUSH1 0x00
00000351: REVERT
00000352: JUMPDEST
00000353: PUSH1 0x00
00000355: DUP1
00000356: PUSH1 0x40
00000358: DUP4
00000359: DUP6
0000035a: SUB
0000035b: SLT
0000035c: ISZERO
0000035d: PUSH2 0x0365
00000360: JUMPI
00000361: PUSH1 0x00
00000363: DUP1
00000364: REVERT
00000365: JUMPDEST
00000366: DUP3
00000367: CALLDATALOAD
00000368: PUSH1 0x01
0000036a: PUSH1 0x01
0000036c: PUSH1 0xa0
0000036e: SHL
0000036f: SUB
00000370: DUP2
00000371: AND
00000372: DUP2
00000373: EQ
00000374: PUSH2 0x037c
00000377: JUMPI
00000378: PUSH1 0x00
0000037a: DUP1
0000037b: REVERT
0000037c: JUMPDEST
0000037d: SWAP2
0000037e: POP
0000037f: PUSH1 0x20
00000381: DUP4
00000382: ADD
00000383: CALLDATALOAD
00000384: PUSH8 0xffffffffffffffff
0000038d: DUP1
0000038e: DUP3
0000038f: GT
00000390: ISZERO
00000391: PUSH2 0x0399
00000394: JUMPI
00000395: PUSH1 0x00
00000397: DUP1
00000398: REVERT
00000399: JUMPDEST
0000039a: DUP2
0000039b: DUP6
0000039c: ADD
0000039d: SWAP2
0000039e: POP
0000039f: DUP6
000003a0: PUSH1 0x1f
000003a2: DUP4
000003a3: ADD
000003a4: SLT
000003a5: PUSH2 0x03ad
000003a8: JUMPI
000003a9: PUSH1 0x00
000003ab: DUP1
000003ac: REVERT
000003ad: JUMPDEST
000003ae: DUP2
000003af: CALLDATALOAD
000003b0: DUP2
000003b1: DUP2
000003b2: GT
000003b3: ISZERO
000003b4: PUSH2 0x03bf
000003b7: JUMPI
000003b8: PUSH2 0x03bf
000003bb: PUSH2 0x033c
000003be: JUMP
000003bf: JUMPDEST
000003c0: PUSH1 0x40
000003c2: MLOAD
000003c3: PUSH1 0x1f
000003c5: DUP3
000003c6: ADD
000003c7: PUSH1 0x1f
000003c9: NOT
000003ca: SWAP1
000003cb: DUP2
000003cc: AND
000003cd: PUSH1 0x3f
000003cf: ADD
000003d0: AND
000003d1: DUP2
000003d2: ADD
000003d3: SWAP1
000003d4: DUP4
000003d5: DUP3
000003d6: GT
000003d7: DUP2
000003d8: DUP4
000003d9: LT
000003da: OR
000003db: ISZERO
000003dc: PUSH2 0x03e7
000003df: JUMPI
000003e0: PUSH2 0x03e7
000003e3: PUSH2 0x033c
000003e6: JUMP
000003e7: JUMPDEST
000003e8: DUP2
000003e9: PUSH1 0x40
000003eb: MSTORE
000003ec: DUP3
000003ed: DUP2
000003ee: MSTORE
000003ef: DUP9
000003f0: PUSH1 0x20
000003f2: DUP5
000003f3: DUP8
000003f4: ADD
000003f5: ADD
000003f6: GT
000003f7: ISZERO
000003f8: PUSH2 0x0400
000003fb: JUMPI
000003fc: PUSH1 0x00
000003fe: DUP1
000003ff: REVERT
00000400: JUMPDEST
00000401: DUP3
00000402: PUSH1 0x20
00000404: DUP7
00000405: ADD
00000406: PUSH1 0x20
00000408: DUP4
00000409: ADD
0000040a: CALLDATACOPY
0000040b: PUSH1 0x00
0000040d: PUSH1 0x20
0000040f: DUP5
00000410: DUP4
00000411: ADD
00000412: ADD
00000413: MSTORE
00000414: DUP1
00000415: SWAP6
00000416: POP
00000417: POP
00000418: POP
00000419: POP
0000041a: POP
0000041b: POP
0000041c: SWAP3
0000041d: POP
0000041e: SWAP3
0000041f: SWAP1
00000420: POP
00000421: JUMP
00000422: JUMPDEST
00000423: PUSH1 0x00
00000425: DUP3
00000426: MLOAD
00000427: PUSH1 0x00
00000429: JUMPDEST
0000042a: DUP2
0000042b: DUP2
0000042c: LT
0000042d: ISZERO
0000042e: PUSH2 0x0443
00000431: JUMPI
00000432: PUSH1 0x20
00000434: DUP2
00000435: DUP7
00000436: ADD
00000437: DUP2
00000438: ADD
00000439: MLOAD
0000043a: DUP6
0000043b: DUP4
0000043c: ADD
0000043d: MSTORE
0000043e: ADD
0000043f: PUSH2 0x0429
00000442: JUMP
00000443: JUMPDEST
00000444: POP
00000445: PUSH1 0x00
00000447: SWAP3
00000448: ADD
00000449: SWAP2
0000044a: DUP3
0000044b: MSTORE
0000044c: POP
0000044d: SWAP2
0000044e: SWAP1
0000044f: POP
00000450: JUMP
00000451: INVALID
00000452: LOG2
00000453: PUSH5 0x6970667358
00000459: UNKNOWN(0x22)
0000045a: SLT
0000045b: KECCAK256
0000045c: UNKNOWN(0x27)
0000045d: UNKNOWN(0xF8)
0000045e: ADD
0000045f: PUSH23 0x059c85af4ee8751015dd9e5db167736b2eedc4be4d13ff
00000477: UNKNOWN(0xC8)
00000478: PUSH16

Findings

validated = confirmed for call sinks; sink observed for others
Severity Category Tool Title Confidence Validated Confirmed Details
critical upgradeability detector Untrusted DELEGATECALL target reachable 70 no no
view
trace_id: 0x75bc2b5d15ab733d800efe7a141bce1d96b7097e2d94474a6d71b6dedb6598ac
call_targets: 0x3203e813930bd710043c1d899fe38dd359307352
target_varies: no
classification: constant_target
validation_json
{
    "sink": "DELEGATECALL",
    "errors": 0,
    "status": "sink_reached",
    "attempts": 1,
    "trace_id": "0x75bc2b5d15ab733d800efe7a141bce1d96b7097e2d94474a6d71b6dedb6598ac",
    "confirmed": false,
    "trace_mode": "callTracer",
    "call_targets": [
        "0x3203e813930bd710043c1d899fe38dd359307352"
    ],
    "matched_probe": null,
    "target_varies": false,
    "classification": "constant_target"
}
witness_json
{
    "notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
    "constraints": [],
    "entrypoints": [
        {
            "calldata": "0x278f7943",
            "selector": "0x278f7943",
            "calldata_variants": [
                "0x278f7943",
                "0x278f79430000000000000000000000000000000000000000000000000000000000000000"
            ]
        }
    ]
}
evidence_json
{
    "examples": [
        {
            "pc": 355,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        },
        {
            "pc": 542,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        }
    ],
    "guards_detected": [],
    "delegatecall_count": 2,
    "untrusted_delegatecall_count": 2
}
high upgradeability codex Unrestricted initialize allows proxy takeover if not initialized atomically 85 no
view
evidence_json
{
    "evidence": "`initialize` is `external payable` and only guarded by `reentrancyGuardInitializer`, with no caller restriction or admin check.",
    "locations": [
        "contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol:82"
    ],
    "recommendation": "Require an access control check on `initialize` (e.g., onlyProxyAdmin/onlyOwner) or ensure the proxy is deployed with initialization calldata in the same transaction so no external caller can front-run the first initialization.",
    "exploit_scenario": "If the proxy is deployed without atomically calling `initialize`, any account can call it first and set `l2Bridge`, `l2TokenBeacon`, and `l2TokenProxyBytecodeHash` to attacker-controlled values. The attacker can then deploy or point `l2Bridge` to a malicious L2 contract that emits withdrawal messages to drain L1 token balances or permanently disrupt deposits/withdrawals."
}
medium upgradeability cast DELEGATECALL present 80 no
view
evidence_json
{
    "notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
    "delegatecall_count": 2
}
low other codex Refund recipient aliasing not enforced for explicit L1 contract addresses 65 no
view
evidence_json
{
    "evidence": "Aliasing is only applied when `_refundRecipient == address(0)`; there is no contract-detection branch for explicit `_refundRecipient` values despite the comment implying it.",
    "locations": [
        "contracts/zksync/l1-contracts/bridge/L1ERC20Bridge.sol:264"
    ],
    "recommendation": "If `_refundRecipient` is a contract on L1, automatically apply `AddressAliasHelper.applyL1ToL2Alias`, or explicitly require callers to pass an already-aliased L2 address and update the documentation accordingly.",
    "exploit_scenario": "A user (or UI) may pass an L1 contract address as `_refundRecipient` expecting automatic aliasing. The code only aliases when `_refundRecipient == address(0)`, so the refund will be sent to the same address on L2, which the L1 contract does not control. If the L2 transaction fails, the refund can be lost or claimed by whoever controls that L2 address."
}