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
}
}}
Submitted on: 2025-10-24 09:55:31
Comments
Log in to comment.
No comments yet.