Lock

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/Lock.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {Implementation, OwnerOnly, ZeroAddress} from "../Implementation.sol";

interface IGovernor {
    /// @dev Create a new proposal to change the protocol / contract parameters.
    /// @param targets The ordered list of target addresses for calls to be made during proposal execution.
    /// @param values The ordered list of values to be passed to the calls made during proposal execution.
    /// @param calldatas The ordered list of data to be passed to each individual function call during proposal execution.
    /// @param description A human readable description of the proposal and the changes it will enact.
    /// @return The Id of the newly created proposal.
    function propose(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        string memory description
    ) external returns (uint256);

    /// @dev Casts a vote
    function castVote(uint256 proposalId, uint8 support) external returns (uint256);
}

interface IToken {
    /// @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);

    /// @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 Transfers the token amount.
    /// @param to Address to transfer to.
    /// @param amount The amount to transfer.
    /// @return True if the function execution is successful.
    function transfer(address to, uint256 amount) external returns (bool);

    /// @dev Transfers the token amount that was previously approved up until the maximum allowance.
    /// @param from Account address to transfer from.
    /// @param to Account address to transfer to.
    /// @param amount Amount to transfer to.
    /// @return True if the function execution is successful.
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

interface IVEOLAS {
    /// @dev Deposits `amount` tokens for `msg.sender` and locks for `unlockTime`.
    /// @param amount Amount to deposit.
    /// @param unlockTime Time when tokens unlock, rounded down to a whole week.
    function createLock(uint256 amount, uint256 unlockTime) external;

    /// @dev Deposits `amount` additional tokens for `msg.sender` without modifying the unlock time.
    /// @param amount Amount of tokens to deposit and add to the lock.
    function increaseAmount(uint256 amount) external;

    /// @dev Extends the unlock time.
    /// @param unlockTime New tokens unlock time.
    function increaseUnlockTime(uint256 unlockTime) external;

    /// @dev Withdraws all tokens for `msg.sender`. Only possible if the lock has expired.
    function withdraw() external;
}

/// @dev Zero value.
error ZeroValue();

/// @dev The contract is already initialized.
error AlreadyInitialized();

/// @dev Caught reentrancy violation.
error ReentrancyGuard();

/// @title Lock - Smart contract for veOLAS related lock and voting functions
contract Lock is Implementation {
    event OlasGovernorUpdated(address indexed olasGovernor);
    event LockTimeIncreaseUpdated(uint256 lockTimeIncrease);
    event Withdraw(address indexed to, uint256 amount);

    // Maximum veOLAS lock time (4 years)
    uint256 public constant MAX_LOCK_TIME = 4 * 365 * 1 days;

    // veOLAS address
    address public immutable ve;
    // OLAS address
    address public immutable olas;

    // OLAS olasGovernor address
    address public olasGovernor;
    // OLAS lock time increase value
    uint256 public lockTimeIncrease;

    // Reentrancy lock
    bool transient _locked;

    /// @dev Lock constructor.
    /// @param _olas OLAS address.
    /// @param _ve veOLAS address.
    constructor(address _olas, address _ve) {
        // Check for zero addresses
        if (_olas == address(0) || _ve == address(0)) {
            revert ZeroAddress();
        }

        ve = _ve;
        olas = _olas;
    }

    /// @dev Lock initializer.
    function initialize() external {
        // Check for already initialized
        if (owner != address(0)) {
            revert AlreadyInitialized();
        }

        lockTimeIncrease = MAX_LOCK_TIME;
        owner = msg.sender;
    }

    /// @dev Changes lock time increase value.
    /// @param newLockTimeIncrease New lock time increase value.
    function changeLockTimeIncrease(uint256 newLockTimeIncrease) external {
        // Check for ownership
        if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }

        lockTimeIncrease = newLockTimeIncrease;
        emit LockTimeIncreaseUpdated(newLockTimeIncrease);
    }

    /// @dev Changes OLAS governor address.
    /// @param newOlasGovernor New OLAS governor address.
    function changeGovernor(address newOlasGovernor) external {
        // Check for ownership
        if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }

        // Check for zero address
        if (newOlasGovernor == address(0)) {
            revert ZeroAddress();
        }

        olasGovernor = newOlasGovernor;
        emit OlasGovernorUpdated(newOlasGovernor);
    }

    /// @dev Sets OLAS governor address and creates first veOLAS lock.
    /// @param _olasGovernor OLAS governor address.
    function setGovernorAndCreateFirstLock(address _olasGovernor) external {
        // Reentrancy guard
        if (_locked) {
            revert ReentrancyGuard();
        }
        _locked = true;

        // Check for ownership
        if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }

        // Check for zero address
        if (_olasGovernor == address(0)) {
            revert ZeroAddress();
        }

        // Deposit starting OLAS amount
        uint256 olasAmount = IToken(olas).balanceOf(address(this));
        // Check for zero value
        if (olasAmount == 0) {
            revert ZeroValue();
        }

        // Approve OLAS for veOLAS
        IToken(olas).approve(ve, olasAmount);
        // Create lock
        IVEOLAS(ve).createLock(olasAmount, MAX_LOCK_TIME);

        olasGovernor = _olasGovernor;
        emit OlasGovernorUpdated(_olasGovernor);
    }

    /// @dev Increases lock amount and time.
    /// @param olasAmount OLAS amount.
    /// @return unlockTimeIncreased True, if the unlock time has increased.
    function increaseLock(uint256 olasAmount) external returns (bool unlockTimeIncreased) {
        // Reentrancy guard
        if (_locked) {
            revert ReentrancyGuard();
        }
        _locked = true;

        // Get OLAS from sender
        IToken(olas).transferFrom(msg.sender, address(this), olasAmount);

        // Approve OLAS for veOLAS
        IToken(olas).approve(ve, olasAmount);

        // Increase lock amount
        IVEOLAS(ve).increaseAmount(olasAmount);

        uint256 curLockTimeIncrease = lockTimeIncrease;
        if (curLockTimeIncrease > 0) {
            // Increase unlock time to a maximum, if possible
            bytes memory increaseUnlockTimeData = abi.encodeCall(IVEOLAS.increaseUnlockTime, (curLockTimeIncrease));
            // Note: both success and failure are acceptable
            (unlockTimeIncreased,) = ve.call(increaseUnlockTimeData);
        }
    }

    /// @dev Withdraws locked balance.
    /// @param to Address to send funds to.
    function withdraw(address to) external {
        // Reentrancy guard
        if (_locked) {
            revert ReentrancyGuard();
        }
        _locked = true;

        // Check for ownership
        if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }

        // veOLAS withdraw
        IVEOLAS(ve).withdraw();

        // Get current balance amount
        uint256 amount = IToken(olas).balanceOf(address(this));

        // Check for non-zero amount
        if (amount > 0) {
            IToken(olas).transfer(to, amount);
        }

        emit Withdraw(to, amount);
    }

    /// @dev Create a new proposal to change the protocol / contract parameters.
    /// @param targets The ordered list of target addresses for calls to be made during proposal execution.
    /// @param values The ordered list of values to be passed to the calls made during proposal execution.
    /// @param calldatas The ordered list of data to be passed to each individual function call during proposal execution.
    /// @param description A human readable description of the proposal and the changes it will enact.
    /// @return The Id of the newly created proposal.
    function propose(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        string memory description
    ) external returns (uint256) {
        // Reentrancy guard
        if (_locked) {
            revert ReentrancyGuard();
        }
        _locked = true;

        // Check for ownership
        if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }

        return IGovernor(olasGovernor).propose(targets, values, calldatas, description);
    }

    /// @dev Casts a vote.
    /// @param proposalId Proposal Id.
    /// @param support Support value: against, for, abstain.
    /// @return Vote weight.
    function castVote(uint256 proposalId, uint8 support) external returns (uint256) {
        // Reentrancy guard
        if (_locked) {
            revert ReentrancyGuard();
        }
        _locked = true;

        // Check for ownership
        if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }

        return IGovernor(olasGovernor).castVote(proposalId, support);
    }
}
"
    },
    "contracts/Implementation.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

/// @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 Zero address.
error ZeroAddress();

/// @title Implementation - Smart contract for default minimal implementation
contract Implementation {
    event OwnerUpdated(address indexed owner);
    event ImplementationUpdated(address indexed implementation);

    // Code position in storage is bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"
    bytes32 public constant PROXY_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    // Contract owner address
    address public owner;

    /// @dev Changes contract owner address.
    /// @param newOwner Address of a new owner.
    function changeOwner(address newOwner) external {
        // Check for ownership
        if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }

        // Check for zero address
        if (newOwner == address(0)) {
            revert ZeroAddress();
        }

        owner = newOwner;
        emit OwnerUpdated(newOwner);
    }

    /// @dev Changes depository implementation contract address.
    /// @param newImplementation New implementation contract address.
    function changeImplementation(address newImplementation) external {
        // Check for ownership
        if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }

        // Check for zero address
        if (newImplementation == address(0)) {
            revert ZeroAddress();
        }

        // Store depository implementation address
        assembly {
            sstore(PROXY_SLOT, newImplementation)
        }

        emit ImplementationUpdated(newImplementation);
    }
}
"
    }
  },
  "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, Voting, Upgradeable, Multi-Signature, Factory|addr:0x62a7033dcb94486f3da962bfc10631c9f5ef311e|verified:true|block:23634929|tx:0x875b0bccae627fd186b04e0f529a2795b53fbb095a562f2f470e9824684e7fb1|first_check:1761292316

Submitted on: 2025-10-24 09:51:59

Comments

Log in to comment.

No comments yet.