BaseDepositProcessorL1

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/l1/bridging/BaseDepositProcessorL1.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {DefaultDepositProcessorL1, IToken} from "./DefaultDepositProcessorL1.sol";

interface IBridge {
    // Source: https://github.com/ethereum-optimism/optimism/blob/65ec61dde94ffa93342728d324fecf474d228e1f/packages/contracts-bedrock/contracts/L1/L1StandardBridge.sol#L188
    // Doc: https://docs.optimism.io/builders/app-developers/bridging/standard-bridge#architecture
    /// @custom:legacy
    /// @notice Deposits some amount of ERC20 tokens into a target account on L2.
    ///
    /// @param _l1Token     Address of the L1 token being deposited.
    /// @param _l2Token     Address of the corresponding token on L2.
    /// @param _to          Address of the recipient on L2.
    /// @param _amount      Amount of the ERC20 to deposit.
    /// @param _minGasLimit Minimum gas limit for the deposit message on L2.
    /// @param _extraData   Optional data to forward to L2. Data supplied here will not be used to
    ///                     execute any code on L2 and is only emitted as extra data for the
    ///                     convenience of off-chain tooling.
    function depositERC20To(
        address _l1Token,
        address _l2Token,
        address _to,
        uint256 _amount,
        uint32 _minGasLimit,
        bytes calldata _extraData
    ) external;

    // Source: https://github.com/ethereum-optimism/optimism/blob/65ec61dde94ffa93342728d324fecf474d228e1f/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L259
    // Doc: https://docs.optimism.io/builders/app-developers/bridging/messaging
    /// @notice Sends a message to some target address on the other chain. Note that if the call
    ///         always reverts, then the message will be unrelayable, and any ETH sent will be
    ///         permanently locked. The same will occur if the target on the other chain is
    ///         considered unsafe (see the _isUnsafeTarget() function).
    ///
    /// @param _target      Target contract or wallet address.
    /// @param _message     Message to trigger the target address with.
    /// @param _minGasLimit Minimum gas limit that the message can be executed with.
    function sendMessage(address _target, bytes calldata _message, uint32 _minGasLimit) external payable;
}

/// @title BaseDepositProcessorL1 - Smart contract for sending tokens and data via Base bridge from L1 to L2 and processing data received from L2.
contract BaseDepositProcessorL1 is DefaultDepositProcessorL1 {
    // Bridge payload length
    uint256 public constant BRIDGE_PAYLOAD_LENGTH = 32;
    // OLAS address on L2
    address public immutable olasL2;

    /// @dev BaseDepositProcessorL1 constructor.
    /// @param _olas OLAS token address.
    /// @param _l1Dispenser L1 tokenomics dispenser address.
    /// @param _l1TokenRelayer L1 token relayer bridging contract address (OmniBridge).
    /// @param _l1MessageRelayer L1 message relayer bridging contract address (AMB Proxy Foreign).
    /// @param _olasL2 OLAS token address on L2.
    constructor(
        address _olas,
        address _l1Dispenser,
        address _l1TokenRelayer,
        address _l1MessageRelayer,
        address _olasL2
    ) DefaultDepositProcessorL1(_olas, _l1Dispenser, _l1TokenRelayer, _l1MessageRelayer) {
        // Check for zero address
        if (_olasL2 == address(0)) {
            revert ZeroAddress();
        }

        olasL2 = _olasL2;
    }

    /// @inheritdoc DefaultDepositProcessorL1
    function _sendMessage(
        address target,
        uint256 amount,
        bytes memory bridgePayload,
        bytes32 batchHash,
        bytes32 operation
    ) internal override returns (uint256 sequence, uint256 leftovers) {
        // Transfer OLAS tokens
        if (operation == STAKE) {
            // Deposit OLAS
            // Approve tokens for the predicate bridge contract
            // Source: https://github.com/maticnetwork/pos-portal/blob/5fbd35ba9cdc8a07bf32d81d6d1f4ce745feabd6/flat/RootChainManager.sol#L2218
            IToken(olas).approve(l1TokenRelayer, amount);

            // Transfer OLAS to L2 staking dispenser contract across the bridge
            IBridge(l1TokenRelayer).depositERC20To(
                olas, olasL2, l2StakingProcessor, amount, uint32(TOKEN_GAS_LIMIT), ""
            );
        }

        uint256 gasLimitMessage;
        // Check for the bridge payload length
        if (bridgePayload.length == BRIDGE_PAYLOAD_LENGTH) {
            // Decode bridge payload
            gasLimitMessage = abi.decode(bridgePayload, (uint256));
        }

        // Check for the recommended message gas limit
        if (gasLimitMessage < MESSAGE_GAS_LIMIT) {
            gasLimitMessage = MESSAGE_GAS_LIMIT;
        }

        // Assemble data payload
        bytes memory data = abi.encodeWithSelector(RECEIVE_MESSAGE, abi.encode(target, amount, batchHash, operation));

        // Send message to L2
        // Reference: https://docs.optimism.io/builders/app-developers/bridging/messaging#for-l1-to-l2-transactions-1
        IBridge(l1MessageRelayer).sendMessage(l2StakingProcessor, data, uint32(gasLimitMessage));

        // Since there is no returned message sequence, use the batch hash
        sequence = uint256(batchHash);

        // Return msg.value, if provided by mistake
        leftovers = msg.value;
    }
}
"
    },
    "contracts/l1/bridging/DefaultDepositProcessorL1.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {IBridgeErrors} from "../../interfaces/IBridgeErrors.sol";

interface IToken {
    /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
    /// @param spender Account address that will be able to transfer tokens on behalf of the caller.
    /// @param amount Token amount.
    /// @return True if the function execution is successful.
    function approve(address spender, uint256 amount) external returns (bool);

    /// @dev Gets the amount of tokens owned by a specified account.
    /// @param account Account address.
    /// @return Amount of tokens owned.
    function balanceOf(address account) external view returns (uint256);
}

/// @title DefaultDepositProcessorL1 - Smart contract for sending tokens and data via arbitrary bridge from L1 to L2 and processing data received from L2.
abstract contract DefaultDepositProcessorL1 is IBridgeErrors {
    event MessagePosted(uint256 indexed sequence, address indexed target, uint256 amount, bytes32 indexed batchHash);
    event L2StakerUpdated(address indexed l2StakingProcessor);
    event LeftoversRefunded(address indexed sender, uint256 leftovers, bool success);

    // Stake operation
    bytes32 public constant STAKE = 0x1bcc0f4c3fad314e585165815f94ecca9b96690a26d6417d7876448a9a867a69;
    // receiveMessage selector to be executed on L2
    bytes4 public constant RECEIVE_MESSAGE = bytes4(keccak256(bytes("receiveMessage(bytes)")));
    // Maximum chain Id as per EVM specs
    uint256 public constant MAX_CHAIN_ID = type(uint64).max / 2 - 36;
    // Token transfer gas limit for L2
    // This is safe as the value is practically bigger than observed ones on numerous chains
    uint256 public constant TOKEN_GAS_LIMIT = 300_000;
    // Message transfer gas limit for L2
    uint256 public constant MESSAGE_GAS_LIMIT = 2_000_000;
    // OLAS token address
    address public immutable olas;
    // L1 depository address
    address public immutable l1Depository;
    // L1 token relayer bridging contract address
    address public immutable l1TokenRelayer;
    // L1 message relayer bridging contract address
    address public immutable l1MessageRelayer;
    // L2 staker address, set by the deploying owner
    address public l2StakingProcessor;
    // Contract owner until the time when the l2StakingProcessor is set
    address public owner;
    // Nonce for each message batch
    uint256 public messageBatchNonce;

    // Processed batch hashes
    mapping(bytes32 => bool) public processedHashes;

    /// @dev DefaultDepositProcessorL1 constructor.
    /// @param _olas OLAS token address on L1.
    /// @param _l1Depository L1 depository address.
    /// @param _l1TokenRelayer L1 token relayer bridging contract address.
    /// @param _l1MessageRelayer L1 message relayer bridging contract address.
    constructor(address _olas, address _l1Depository, address _l1TokenRelayer, address _l1MessageRelayer) {
        // Check for zero addresses
        if (_l1Depository == address(0) || _l1TokenRelayer == address(0) || _l1MessageRelayer == address(0)) {
            revert ZeroAddress();
        }

        olas = _olas;
        l1Depository = _l1Depository;
        l1TokenRelayer = _l1TokenRelayer;
        l1MessageRelayer = _l1MessageRelayer;
        owner = msg.sender;
    }

    /// @dev Sends message to the L2 side via a corresponding bridge.
    /// @notice Message is sent to the staker contract to reflect transferred OLAS and staking amounts.
    /// @param target Staking target address.
    /// @param amount Corresponding staking amount.
    /// @param bridgePayload Bridge payload necessary (if required) for a specific bridging relayer.
    /// @param batchHash Unique batch hash for each message transfer.
    /// @param operation Funds operation: stake / unstake.
    /// @return sequence Unique message sequence (if applicable) or the batch hash converted to number.
    /// @return leftovers ETH leftovers from unused msg.value.
    function _sendMessage(
        address target,
        uint256 amount,
        bytes memory bridgePayload,
        bytes32 batchHash,
        bytes32 operation
    ) internal virtual returns (uint256 sequence, uint256 leftovers);

    /// @dev Sends a message to the L2 side via a corresponding bridge.
    /// @param target Staking target addresses.
    /// @param amount Corresponding staking amount.
    /// @param bridgePayload Bridge payload necessary (if required) for a specific bridge relayer.
    /// @param operation Funds operation: stake / unstake.
    /// @param sender Sender account.
    function sendMessage(address target, uint256 amount, bytes memory bridgePayload, bytes32 operation, address sender)
        external
        payable
        virtual
    {
        // Check for the dispenser contract to be the msg.sender
        if (msg.sender != l1Depository) {
            revert ManagerOnly(l1Depository, msg.sender);
        }

        // Check for zero value
        if (operation == STAKE && amount == 0) {
            revert ZeroValue();
        }

        // Get the batch hash
        uint256 batchNonce = messageBatchNonce;
        bytes32 batchHash = keccak256(abi.encode(batchNonce, address(this), block.timestamp, block.chainid));

        // Send the message to L2
        (uint256 sequence, uint256 leftovers) = _sendMessage(target, amount, bridgePayload, batchHash, operation);

        // Send leftover amount back to the sender, if any
        if (leftovers > 0) {
            // If the call fails, ignore to avoid the attack that would prevent this function from executing
            // solhint-disable-next-line avoid-low-level-calls
            (bool success,) = sender.call{value: leftovers}("");

            emit LeftoversRefunded(sender, leftovers, success);
        }

        // Increase the staking batch nonce
        messageBatchNonce = batchNonce + 1;

        emit MessagePosted(sequence, target, amount, batchHash);
    }

    /// @dev Updated the batch hash of a failed message, if applicable.
    /// @param batchHash Unique batch hash for each message transfer.
    function updateHashMaintenance(bytes32 batchHash) external {
        // Check for the dispenser contract to be the msg.sender
        if (msg.sender != l1Depository) {
            revert ManagerOnly(l1Depository, msg.sender);
        }

        // Check that the batch hash has not yet being processed
        // Possible scenario: bridge failed to deliver from L2 to L1, then after some time the bridge somehow
        // re-delivers the same message, and the maintenance function is called by the DAO as well,
        // that is not needed already anymore since the message was processed naturally via a recovered bridge
        if (processedHashes[batchHash]) {
            revert AlreadyDelivered(batchHash);
        }
        processedHashes[batchHash] = true;
    }

    /// @dev Sets L2 staking processor address and zero-s the owner.
    /// @param l2Processor L2 staking processor address.
    function _setL2StakingProcessor(address l2Processor) internal {
        // Check the contract ownership
        if (msg.sender != owner) {
            revert OwnerOnly(owner, msg.sender);
        }

        // The L2 staker must have a non zero address
        if (l2Processor == address(0)) {
            revert ZeroAddress();
        }
        l2StakingProcessor = l2Processor;

        // Revoke the owner role making the contract ownerless
        owner = address(0);

        emit L2StakerUpdated(l2Processor);
    }

    /// @dev Sets L2 staking processor address.
    /// @param l2Processor L2 staking processor address.
    function setL2StakingProcessor(address l2Processor) external virtual {
        _setL2StakingProcessor(l2Processor);
    }

    /// @dev Gets the maximum number of token decimals able to be transferred across the bridge.
    /// @return Number of supported decimals.
    function getBridgingDecimals() external pure virtual returns (uint256) {
        return 18;
    }
}
"
    },
    "contracts/interfaces/IBridgeErrors.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

interface IBridgeErrors {
    /// @dev Only `manager` has a privilege, but the `sender` was provided.
    /// @param sender Sender address.
    /// @param manager Required sender address as a manager.
    error ManagerOnly(address sender, address manager);

    /// @dev Only `owner` has a privilege, but the `sender` was provided.
    /// @param sender Sender address.
    /// @param owner Required sender address as an owner.
    error OwnerOnly(address sender, address owner);

    /// @dev Provided zero address.
    error ZeroAddress();

    /// @dev Zero value when it has to be different from zero.
    error ZeroValue();

    /// @dev Provided incorrect data length.
    /// @param expected Expected minimum data length.
    /// @param provided Provided data length.
    error IncorrectDataLength(uint256 expected, uint256 provided);

    /// @dev Received lower value than the expected one.
    /// @param provided Provided value is lower.
    /// @param expected Expected value.
    error LowerThan(uint256 provided, uint256 expected);

    /// @dev Value overflow.
    /// @param provided Overflow value.
    /// @param max Maximum possible value.
    error Overflow(uint256 provided, uint256 max);

    /// @dev Target bridge relayer is incorrect.
    /// @param provided Provided relayer address.
    /// @param expected Expected relayer address.
    error TargetRelayerOnly(address provided, address expected);

    /// @dev Message sender from another chain is incorrect.
    /// @param provided Provided message sender address.
    /// @param expected Expected message sender address.
    error WrongMessageSender(address provided, address expected);

    /// @dev Chain Id originating the call is incorrect.
    /// @param provided Provided chain Id.
    /// @param expected Expected chain Id.
    error WrongChainId(uint256 provided, uint256 expected);

    /// @dev Request with specified params is not found in the queue.
    /// @param target Target address.
    /// @param amount Token amount.
    /// @param batchHash Reference batch hash.
    /// @param operation Funds operation: stake / unstake.
    error RequestNotQueued(address target, uint256 amount, bytes32 batchHash, bytes32 operation);

    /// @dev Insufficient token balance.
    /// @param provided Provided balance.
    /// @param expected Expected available amount.
    error InsufficientBalance(uint256 provided, uint256 expected);

    /// @dev Failure of a transfer.
    /// @param token Address of a token.
    /// @param from Address `from`.
    /// @param to Address `to`.
    /// @param amount Token amount.
    error TransferFailed(address token, address from, address to, uint256 amount);

    /// @dev Delivery hash has been already processed.
    /// @param deliveryHash Delivery hash.
    error AlreadyDelivered(bytes32 deliveryHash);

    /// @dev Wrong amount received / provided.
    /// @param provided Provided amount.
    /// @param expected Expected amount.
    error WrongAmount(uint256 provided, uint256 expected);

    /// @dev Provided token address is incorrect.
    /// @param provided Provided token address.
    /// @param expected Expected token address.
    error WrongTokenAddress(address provided, address expected);

    /// @dev The contract is paused.
    error Paused();

    /// @dev The contract is unpaused.
    error Unpaused();

    // @dev Reentrancy guard.
    error ReentrancyGuard();

    /// @dev Account address is incorrect.
    /// @param account Account address.
    error WrongAccount(address account);

    /// @dev Operation not found.
    /// @param batchHash Batch hash.
    /// @param operation Funds operation type.
    error OperationNotFound(bytes32 batchHash, bytes32 operation);

    /// @dev Redeem request failed.
    /// @param batchHash Batch hash.
    /// @param target Staking target address.
    /// @param amount Staking amount.
    /// @param operation Funds operation: stake / unstake.
    /// @param status Request status.
    error RequestFailed(bytes32 batchHash, address target, uint256 amount, bytes32 operation, uint256 status);
}
"
    }
  },
  "settings": {
    "remappings": [
      "@gnosis.pm/=node_modules/@gnosis.pm/",
      "@layerzerolabs/oapp-evm/=lib/devtools/packages/oapp-evm/",
      "@layerzerolabs/lz-evm-protocol-v2/=lib/layerzero-v2/packages/layerzero-v2/evm/protocol/",
      "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
      "@registries/=lib/autonolas-registries/",
      "@solmate/=lib/solmate/",
      "autonolas-registries/=lib/autonolas-registries/",
      "devtools/=lib/devtools/packages/toolbox-foundry/src/",
      "ds-test/=lib/autonolas-registries/lib/forge-std/lib/ds-test/src/",
      "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
      "forge-std/=lib/autonolas-registries/lib/forge-std/src/",
      "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
      "layerzero-v2/=lib/layerzero-v2/",
      "openzeppelin-contracts/=lib/openzeppelin-contracts/",
      "solmate/=lib/solmate/src/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 1000000
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "prague",
    "viaIR": true
  }
}}

Tags:
Multisig, Upgradeable, Multi-Signature, Factory|addr:0x40abf47b926181148000dbcc7c8de76a3a61a66f|verified:true|block:23635041|tx:0x3f628f0645d6ce3bb6e0868f0cad3bdf93dd54402d433cc85ebbe6088cfe4966|first_check:1761292528

Submitted on: 2025-10-24 09:55:31

Comments

Log in to comment.

No comments yet.