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();
}
}
Submitted on: 2025-10-17 11:24:01
Comments
Log in to comment.
No comments yet.