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/GnosisDepositProcessorL1.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import {DefaultDepositProcessorL1, IToken} from "./DefaultDepositProcessorL1.sol";
interface IBridge {
// Contract: AMB Contract Proxy Foreign
// Source: https://github.com/omni/tokenbridge-contracts/blob/908a48107919d4ab127f9af07d44d47eac91547e/contracts/upgradeable_contracts/arbitrary_message/MessageDelivery.sol#L22
// Doc: https://docs.gnosischain.com/bridges/Token%20Bridge/amb-bridge
/// @dev Requests message relay to the opposite network
/// @param target Executor address on the other side.
/// @param data Calldata passed to the executor on the other side.
/// @param maxGasLimit Gas limit used on the other network for executing a message.
/// @return Message Id.
function requireToPassMessage(address target, bytes memory data, uint256 maxGasLimit) external returns (bytes32);
// Contract: Omnibridge Multi-Token Mediator Proxy
// Source: https://github.com/omni/omnibridge/blob/c814f686487c50462b132b9691fd77cc2de237d3/contracts/upgradeable_contracts/components/common/TokensRelayer.sol#L54
// Doc: https://docs.gnosischain.com/bridges/Token%20Bridge/omnibridge
function relayTokens(address token, address receiver, uint256 amount) external;
}
/// @title GnosisDepositProcessorL1 - Smart contract for sending tokens and data via Gnosis bridge from L1 to L2 and processing data received from L2.
contract GnosisDepositProcessorL1 is DefaultDepositProcessorL1 {
/// @dev GnosisDepositProcessorL1 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).
constructor(address _olas, address _l1Dispenser, address _l1TokenRelayer, address _l1MessageRelayer)
DefaultDepositProcessorL1(_olas, _l1Dispenser, _l1TokenRelayer, _l1MessageRelayer)
{}
/// @inheritdoc DefaultDepositProcessorL1
function _sendMessage(address target, uint256 amount, bytes memory, bytes32 batchHash, bytes32 operation)
internal
override
returns (uint256 sequence, uint256 leftovers)
{
// Transfer OLAS tokens
if (operation == STAKE) {
// Approve tokens for the bridge contract
IToken(olas).approve(l1TokenRelayer, amount);
// Transfer tokens
IBridge(l1TokenRelayer).relayTokens(olas, l2StakingProcessor, amount);
}
// Assemble AMB data payload
bytes memory data = abi.encodeWithSelector(RECEIVE_MESSAGE, abi.encode(target, amount, batchHash, operation));
// Send message to L2
// In the current configuration, maxGasPerTx is set to 4000000 on Ethereum and 2000000 on Gnosis Chain.
// Source: https://docs.gnosischain.com/bridges/Token%20Bridge/amb-bridge#how-to-check-if-amb-is-down-not-relaying-message
bytes32 iMsg = IBridge(l1MessageRelayer).requireToPassMessage(l2StakingProcessor, data, MESSAGE_GAS_LIMIT);
sequence = uint256(iMsg);
// 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:05
Comments
Log in to comment.
No comments yet.