ONVestingMain

Description:

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

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// Sources flattened with hardhat v2.26.3 https://hardhat.org

// SPDX-License-Identifier: Apache-2.0 AND MIT

// File @openzeppelin/contracts/utils/Context.sol@v5.4.0

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// File @openzeppelin/contracts/access/Ownable.sol@v5.4.0

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v5.4.0

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(
        address owner,
        address spender
    ) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool);
}

// File @openzeppelin/contracts/interfaces/IERC20.sol@v5.4.0

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)

pragma solidity >=0.4.16;

// File @openzeppelin/contracts/utils/Errors.sol@v5.4.0

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}

// File @openzeppelin/contracts/utils/Create2.sol@v5.4.0

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol)

pragma solidity ^0.8.20;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev There's no code to deploy.
     */
    error Create2EmptyBytecode();

    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(
        uint256 amount,
        bytes32 salt,
        bytes memory bytecode
    ) internal returns (address addr) {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }
        if (bytecode.length == 0) {
            revert Create2EmptyBytecode();
        }
        assembly ("memory-safe") {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
            // if no address was created, and returndata is not empty, bubble revert
            if and(iszero(addr), not(iszero(returndatasize()))) {
                let p := mload(0x40)
                returndatacopy(p, 0, returndatasize())
                revert(p, returndatasize())
            }
        }
        if (addr == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(
        bytes32 salt,
        bytes32 bytecodeHash
    ) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(
        bytes32 salt,
        bytes32 bytecodeHash,
        address deployer
    ) internal pure returns (address addr) {
        assembly ("memory-safe") {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := and(
                keccak256(start, 85),
                0xffffffffffffffffffffffffffffffffffffffff
            )
        }
    }
}

// File @openzeppelin/contracts/proxy/Clones.sol@v5.4.0

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (proxy/Clones.sol)

pragma solidity ^0.8.20;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 */
library Clones {
    error CloneArgumentsTooLong();

    /**
     * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     *
     * WARNING: This function does not check if `implementation` has code. A clone that points to an address
     * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
     * have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
     */
    function clone(address implementation) internal returns (address instance) {
        return clone(implementation, 0);
    }

    /**
     * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
     * to the new contract.
     *
     * WARNING: This function does not check if `implementation` has code. A clone that points to an address
     * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
     * have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function clone(
        address implementation,
        uint256 value
    ) internal returns (address instance) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        assembly ("memory-safe") {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(
                0x00,
                or(
                    shr(0xe8, shl(0x60, implementation)),
                    0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000
                )
            )
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(
                0x20,
                or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)
            )
            instance := create(value, 0x09, 0x37)
        }
        if (instance == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple times will revert, since
     * the clones cannot be deployed twice at the same address.
     *
     * WARNING: This function does not check if `implementation` has code. A clone that points to an address
     * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
     * have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
     */
    function cloneDeterministic(
        address implementation,
        bytes32 salt
    ) internal returns (address instance) {
        return cloneDeterministic(implementation, salt, 0);
    }

    /**
     * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
     * a `value` parameter to send native currency to the new contract.
     *
     * WARNING: This function does not check if `implementation` has code. A clone that points to an address
     * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
     * have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function cloneDeterministic(
        address implementation,
        bytes32 salt,
        uint256 value
    ) internal returns (address instance) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        assembly ("memory-safe") {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(
                0x00,
                or(
                    shr(0xe8, shl(0x60, implementation)),
                    0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000
                )
            )
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(
                0x20,
                or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)
            )
            instance := create2(value, 0x09, 0x37, salt)
        }
        if (instance == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := and(
                keccak256(add(ptr, 0x43), 0x55),
                0xffffffffffffffffffffffffffffffffffffffff
            )
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
     * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
     * access the arguments within the implementation, use {fetchCloneArgs}.
     *
     * This function uses the create opcode, which should never revert.
     *
     * WARNING: This function does not check if `implementation` has code. A clone that points to an address
     * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
     * have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
     */
    function cloneWithImmutableArgs(
        address implementation,
        bytes memory args
    ) internal returns (address instance) {
        return cloneWithImmutableArgs(implementation, args, 0);
    }

    /**
     * @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
     * parameter to send native currency to the new contract.
     *
     * WARNING: This function does not check if `implementation` has code. A clone that points to an address
     * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
     * have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function cloneWithImmutableArgs(
        address implementation,
        bytes memory args,
        uint256 value
    ) internal returns (address instance) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        bytes memory bytecode = _cloneCodeWithImmutableArgs(
            implementation,
            args
        );
        assembly ("memory-safe") {
            instance := create(value, add(bytecode, 0x20), mload(bytecode))
        }
        if (instance == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
     * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
     * access the arguments within the implementation, use {fetchCloneArgs}.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
     * `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice
     * at the same address.
     *
     * WARNING: This function does not check if `implementation` has code. A clone that points to an address
     * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
     * have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
     */
    function cloneDeterministicWithImmutableArgs(
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal returns (address instance) {
        return
            cloneDeterministicWithImmutableArgs(implementation, args, salt, 0);
    }

    /**
     * @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
     * but with a `value` parameter to send native currency to the new contract.
     *
     * WARNING: This function does not check if `implementation` has code. A clone that points to an address
     * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
     * have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function cloneDeterministicWithImmutableArgs(
        address implementation,
        bytes memory args,
        bytes32 salt,
        uint256 value
    ) internal returns (address instance) {
        bytes memory bytecode = _cloneCodeWithImmutableArgs(
            implementation,
            args
        );
        return Create2.deploy(value, salt, bytecode);
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
     */
    function predictDeterministicAddressWithImmutableArgs(
        address implementation,
        bytes memory args,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes memory bytecode = _cloneCodeWithImmutableArgs(
            implementation,
            args
        );
        return Create2.computeAddress(salt, keccak256(bytecode), deployer);
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
     */
    function predictDeterministicAddressWithImmutableArgs(
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal view returns (address predicted) {
        return
            predictDeterministicAddressWithImmutableArgs(
                implementation,
                args,
                salt,
                address(this)
            );
    }

    /**
     * @dev Get the immutable args attached to a clone.
     *
     * - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
     *   function will return an empty array.
     * - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
     *   `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
     *   creation.
     * - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
     *   function should only be used to check addresses that are known to be clones.
     */
    function fetchCloneArgs(
        address instance
    ) internal view returns (bytes memory) {
        bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
        assembly ("memory-safe") {
            extcodecopy(instance, add(result, 32), 45, mload(result))
        }
        return result;
    }

    /**
     * @dev Helper that prepares the initcode of the proxy with immutable args.
     *
     * An assembly variant of this function requires copying the `args` array, which can be efficiently done using
     * `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
     * abi.encodePacked is more expensive but also more portable and easier to review.
     *
     * NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
     * With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
     */
    function _cloneCodeWithImmutableArgs(
        address implementation,
        bytes memory args
    ) private pure returns (bytes memory) {
        if (args.length > 24531) revert CloneArgumentsTooLong();
        return
            abi.encodePacked(
                hex"61",
                uint16(args.length + 45),
                hex"3d81600a3d39f3363d3d373d3d3d363d73",
                implementation,
                hex"5af43d82803e903d91602b57fd5bf3",
                args
            );
    }
}

// File contracts/interfaces/ONCommon.sol

// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity 0.8.26;

error TGENotStarted();
error TGEAlreadyStarted();
error InvalidAddress();

/**
 * @dev Struct to define vesting term
 */
struct VestingTerm {
    address beneficiary;
    uint64 cliff;
    uint64 vestingDuration;
    uint64 milestoneDuration;
    uint256 unlockedAtTGE;
    uint256 total;
}

/**
 * @dev Struct to store vesting schedule
 */
struct VestingSchedule {
    uint64 cliff;
    uint64 vestingDuration;
    uint64 milestoneDuration;
    uint64 milestoneClaimed;
    uint256 milestoneReleaseAmount;
    uint256 unlockedAtTGE;
    uint256 totalClaimed;
}

/**
 * @dev Struct to store vesting schedule
 */
struct VestingDetail {
    address contractAddress;
    address beneficiary;
    uint64 start;
    uint64 end;
    uint64 milestoneDuration;
    uint64 milestoneClaimed;
    uint256 milestoneReleaseAmount;
    uint256 unlockedAtTGE;
    uint256 totalClaimed;
    uint256 balanceClaimable;
    uint256 balanceRemain;
}

/**
 * @title Orochi Network Token Interface
 */
interface ONTokenInterface is IERC20 {
    // Custom public/external functions

    function mint() external returns (bool);

    // Ownership (from Ownable)

    function owner() external view returns (address);

    function transferOwnership(address newOwner) external;

    function renounceOwnership() external;
}

/**
 * @title Orochi Network Vesting Sub Interface
 */
interface ONVestingSubInterface {
    // Init
    function init(
        address onVestingMainAddress,
        VestingTerm calldata vestingTerm
    ) external returns (bool);

    // Beneficiary actions
    function claim() external;

    function emergency() external;

    function transferVestingContract(address beneficiaryNew) external;

    // Views
    function getBeneficiary() external view returns (address);

    function getTimeStart() external view returns (uint64);

    function getTimeEnd() external view returns (uint64);

    function getClaimableBalance() external view returns (uint256);

    function getRemainingBalance() external view returns (uint256);

    function getVestingDetail() external view returns (VestingDetail memory);
}

/**
 * @title Orochi Network Vesting Main Interface
 */
interface ONVestingMainInterface {
    // External functions
    function transfer(address to, uint256 value) external;

    function setTokenAddress(address tokenAddress) external;

    function setImplementation(address onVestingSubImpl) external;

    function setTimeTGE(uint256 timestampTGE) external;

    function mint() external;

    function addVestingTerm(VestingTerm calldata vestingTerm) external;

    // View functions
    function getImplementation() external view returns (address);

    function getTokenAddress() external view returns (address);

    function getTimeTGE() external view returns (uint256);

    function getVestingDetailList(
        uint256 offset,
        uint256 limit
    ) external view returns (VestingDetail[] memory);

    function getVestingContractAddress(
        uint256 index
    ) external view returns (address);

    function getVestingContractTotal() external view returns (uint256);

    function isTGE() external view returns (bool);
}

// File @openzeppelin/contracts/utils/ReentrancyGuard.sol@v5.4.0

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @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 EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * 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].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

// File contracts/interfaces/ONVestingMainBaseInterface.sol

// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity 0.8.26;

/**
 * @title Orochi Network Vesting Main Base Interface
 */
interface ONVestingMainBaseInterface {
    // Errors
    error UnableToAddNewVestingContract(address beneficiary);
    error TGETimeMustBeInTheFuture(uint256 timestamp);
    error InvalidOffsetOrLimit(uint256 offset, uint256 limit);
    error UnableToTransfer(address to, uint256 value);

    // Events
    event TransferToken(address indexed to, uint256 value);
    event AddNewVestingContract(
        uint256 indexed index,
        address vestingContract,
        address indexed beneficiary
    );
    event SetImplementation(address indexed implementation);
    event SetTokenAddress(address indexed tokenAddress);
    event SetTimeTGE(uint256 indexed timestampTGE);
}

// File contracts/ONVestingMainBase.sol

// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity 0.8.26;

/**
 * @title Orochi Network Vesting Main Base
 */
contract ONVestingMainBase is ONVestingMainBaseInterface {
    // Allow main to clone sub contract
    using Clones for address;

    // Token contract address
    ONTokenInterface private token;

    // TGE time
    uint256 private timeTGE;

    // Based implementation of ON Vesting Sub
    address private onVestingSub;

    // Total number of vesting contract
    uint256 private vestingContractTotal;

    // Mapping from index to vesting contract address
    mapping(uint256 => address) private vestingContractMap;

    /**
     * @dev Modifier to make sure that the TGE is not started yet
     */
    modifier onlyPreTGE() {
        // It's require isTGE to be false to move one
        if (_isTGE()) {
            revert TGEAlreadyStarted();
        }
        _;
    }

    /*******************************************************
     * Constructor
     ********************************************************/

    /**
     * Constructor
     * @param tokenAddress The address of the token contract
     */
    constructor(
        address tokenAddress,
        uint256 timestampTGE,
        address onVestingSubImpl
    ) {
        _setTokenAddress(tokenAddress);
        _setImplementation(onVestingSubImpl);
        _setTimeTGE(timestampTGE);
    }

    /*******************************************************
     * Internal
     ********************************************************/

    /**
     * Transfer token to the given address
     * @param to Address to transfer token to
     * @param value Amount of token to transfer
     */
    function _transfer(address to, uint256 value) internal {
        if (token.transfer(to, value)) {
            emit TransferToken(to, value);
            return;
        }
        revert UnableToTransfer(to, value);
    }

    /**
     * Mint maximum supply to this contract
     */
    function _mint() internal {
        token.mint();
    }

    /**
     * Add a vesting term to the contract
     * @param vestingTerm VestingTerm struct
     */
    function _addVestingTerm(VestingTerm calldata vestingTerm) internal {
        // Clone ONVestingSub
        address newVestingContract = onVestingSub.cloneDeterministic(
            bytes32(vestingContractTotal)
        );
        vestingContractMap[vestingContractTotal] = newVestingContract;
        // Init ONVestingSub with its vesting term
        if (
            ONVestingSubInterface(newVestingContract).init(
                address(this),
                vestingTerm
            ) && token.transfer(newVestingContract, vestingTerm.total)
        ) {
            emit AddNewVestingContract(
                vestingContractTotal,
                newVestingContract,
                vestingTerm.beneficiary
            );
            vestingContractTotal += 1;
            return;
        }
        revert UnableToAddNewVestingContract(vestingTerm.beneficiary);
    }

    /*******************************************************
     * Internal
     ********************************************************/

    /**
     * Set ONVestingSub implementation address
     * @param onVestingSubImpl Address of ONVestingSub implementation
     */
    function _setImplementation(address onVestingSubImpl) internal {
        if (onVestingSubImpl == address(0)) {
            revert InvalidAddress();
        }
        onVestingSub = onVestingSubImpl;
        emit SetImplementation(onVestingSubImpl);
    }

    /**
     * Set ONToken address
     * @param onTokenAddress On token addresss
     */
    function _setTokenAddress(address onTokenAddress) internal {
        if (onTokenAddress == address(0)) {
            revert InvalidAddress();
        }
        token = ONTokenInterface(onTokenAddress);
        emit SetTokenAddress(onTokenAddress);
    }

    /**
     * Set the TGE time
     * @param timestampTGE Timestamp of the TGE
     */
    function _setTimeTGE(uint256 timestampTGE) internal {
        if (timestampTGE <= block.timestamp) {
            revert TGETimeMustBeInTheFuture(timestampTGE);
        }
        timeTGE = timestampTGE;
        emit SetTimeTGE(timestampTGE);
    }

    /*******************************************************
     * Internal view View
     ********************************************************/

    /**
     * Get ONVestingSub implementation address
     */
    function _getImplementation() internal view returns (address) {
        return address(onVestingSub);
    }

    /**
     * Get token address
     */
    function _getTokenAddress() internal view returns (address) {
        return address(token);
    }

    /**
     * Get TGE time
     */
    function _getTimeTGE() internal view returns (uint256) {
        return timeTGE;
    }

    /**
     * Get all vesting detail
     * @param offset Offset in the list
     * @param limit Number of record
     */
    function _getVestingDetailList(
        uint256 offset,
        uint256 limit
    ) internal view returns (VestingDetail[] memory) {
        uint256 end = offset + limit;
        if (end > vestingContractTotal) {
            end = vestingContractTotal;
        }
        uint256 recordCount = end - offset;
        if (recordCount == 0) {
            revert InvalidOffsetOrLimit(offset, limit);
        }
        VestingDetail[] memory vestingDetailList = new VestingDetail[](
            recordCount
        );
        for (uint i = offset; i < end; i += 1) {
            vestingDetailList[i - offset] = ONVestingSubInterface(
                vestingContractMap[i]
            ).getVestingDetail();
        }
        return vestingDetailList;
    }

    /**
     * Get vesting contract addresss at given index
     * @param index Index in contract map
     */
    function _getVestingContractAddress(
        uint256 index
    ) internal view returns (address) {
        return vestingContractMap[index];
    }

    /**
     * Get total number of vesting contract
     */
    function _getVestingContractTotal() internal view returns (uint256) {
        return vestingContractTotal;
    }

    /**
     * Check if TGE is happend or not
     */
    function _isTGE() internal view returns (bool) {
        return block.timestamp >= timeTGE;
    }
}

// File contracts/ONVestingMain.sol

// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity 0.8.26;

/**
 * @title Orochi Network Vesting Main
 */
contract ONVestingMain is
    ONVestingMainInterface,
    ONVestingMainBase,
    ReentrancyGuard,
    Ownable
{
    /*******************************************************
     * Constructor
     ********************************************************/

    /**
     * Constructor
     * @param tokenAddress The address of the token contract
     */
    constructor(
        address tokenAddress,
        uint256 timestampTGE,
        address onVestingSubImpl
    )
        Ownable(msg.sender)
        ONVestingMainBase(tokenAddress, timestampTGE, onVestingSubImpl)
    {}

    /*******************************************************
     * External Owner
     ********************************************************/

    /**
     * Transfer token to the given address
     * @param to Address to transfer token to
     * @param value Amount of token to transfer
     * @dev Only callable by the owner
     */
    function transfer(
        address to,
        uint256 value
    ) external onlyOwner nonReentrant {
        _transfer(to, value);
    }

    /*******************************************************
     * External Owner, before TGE
     ********************************************************/

    /**
     * Set ONToken address
     * @param tokenAddress Address of the ONToken contract
     * @dev Only callable by the owner before TGE
     */
    function setTokenAddress(
        address tokenAddress
    ) external onlyOwner nonReentrant onlyPreTGE {
        _setTokenAddress(tokenAddress);
    }

    /**
     * Set ONVestingSub implementation
     * @param onVestingSubImpl Address of the ONVestingSub implementation
     * @dev Only callable by the owner before TGE
     */
    function setImplementation(
        address onVestingSubImpl
    ) external onlyOwner nonReentrant onlyPreTGE {
        _setImplementation(onVestingSubImpl);
    }

    /**
     * Set TGE time
     * @param timestampTGE Timestamp of the TGE
     * @dev Only callable by the owner before TGE
     */
    function setTimeTGE(
        uint256 timestampTGE
    ) external onlyOwner nonReentrant onlyPreTGE {
        _setTimeTGE(timestampTGE);
    }

    /**
     * Mint maxium supply to this contract
     */
    function mint() external onlyOwner nonReentrant onlyPreTGE {
        _mint();
    }

    /**
     * Add a vesting term to the contract
     * @param vestingTerm VestingTerm struct
     * @dev Only callable by the owner before TGE
     */
    function addVestingTerm(
        VestingTerm calldata vestingTerm
    ) external onlyOwner nonReentrant onlyPreTGE {
        _addVestingTerm(vestingTerm);
    }

    /*******************************************************
     * External View
     ********************************************************/

    /**
     * Get ONVestingSub implementation address
     */
    function getImplementation() external view returns (address) {
        return _getImplementation();
    }

    /**
     * Get token address
     */
    function getTokenAddress() external view returns (address) {
        return _getTokenAddress();
    }

    /**
     * Get TGE time
     */
    function getTimeTGE() external view returns (uint256) {
        return _getTimeTGE();
    }

    /**
     * Get all vesting detail
     * @param offset Offset in the list
     * @param limit Number of record
     */
    function getVestingDetailList(
        uint256 offset,
        uint256 limit
    ) external view returns (VestingDetail[] memory) {
        return _getVestingDetailList(offset, limit);
    }

    /**
     * Get vesting contract addresss at given index
     * @param index Index in contract map
     */
    function getVestingContractAddress(
        uint256 index
    ) external view returns (address) {
        return _getVestingContractAddress(index);
    }

    /**
     * Get total number of vesting contract
     */
    function getVestingContractTotal() external view returns (uint256) {
        return _getVestingContractTotal();
    }

    /**
     * Is TGE started or not
     */
    function isTGE() external view returns (bool) {
        return _isTGE();
    }
}

Tags:
ERC20, Multisig, Mintable, Upgradeable, Multi-Signature, Factory|addr:0x059ae3e163aba361e6c4a0beec4891bd36eb19e3|verified:true|block:23595507|tx:0x8524ec7b6a109c83d3a2c3d15d4fbe26202400ef43cb0f9fe89110209c4e9711|first_check:1760693040

Submitted on: 2025-10-17 11:24:01

Comments

Log in to comment.

No comments yet.