Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xa59BaaBFa67482ed463F6291582A714C258700EC
Balance 0 ETH
Nonce 1
Code Size 5933 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

5933 bytes
0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c8063863e76db11610104578063cd87a96c116100a2578063d547741f11610071578063d547741f1461047d578063ea9a011b14610490578063f21f537d146104b6578063fedda89c146104be57600080fd5b8063cd87a96c146103f2578063cf07626714610405578063d248923114610444578063d26c3f311461045757600080fd5b8063a111bab9116100de578063a111bab914610382578063a217fddf1461038a578063a3a5065214610392578063bf7024ea146103ea57600080fd5b8063863e76db1461035257806391d148541461035c57806395805dad1461036f57600080fd5b80633ea2dfb9116101715780635dd39c191161014b5780635dd39c19146102cd5780636a9e1cea146102d55780636d96bfa21461032a57806375b238fc1461033d57600080fd5b80633ea2dfb91461028c5780634530c3e7146102b25780635193f9e0146102ba57600080fd5b806320988cef116101ad57806320988cef14610237578063248a9ca3146102415780632f2ff15d1461026457806336568abe1461027957600080fd5b806301ffc9a7146101d45780631cd6a375146101fc5780631f2698ab1461022c575b600080fd5b6101e76101e2366004611408565b6104c6565b60405190151581526020015b60405180910390f35b7f00000000000000000000000000000000000000000000000000b1a2bc2ec500005b6040519081526020016101f3565b60015460ff166101e7565b61021e62278d0081565b61021e61024f366004611432565b60009081526020819052604090206001015490565b610277610272366004611467565b6104fd565b005b610277610287366004611467565b610528565b7f000000000000000000000000000000000000000000000000000000000000001461021e565b61021e610560565b6101e76102c8366004611493565b6106ac565b61021e6108a6565b6102e86102e33660046114bd565b6108da565b6040516101f39190600060a082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015292915050565b6101e7610338366004611524565b610964565b61021e6000805160206116d883398151915281565b61021e6201518081565b6101e761036a366004611467565b610a3f565b61021e61037d366004611432565b610a68565b60035461021e565b61021e600081565b61039a610bd8565b6040516101f3919081516001600160a01b031681526020808301519082015260408083015190820152606080830151908201526080808301519082015260a0918201519181019190915260c00190565b61021e610c60565b61021e6104003660046114bd565b610cc0565b61042c7f000000000000000000000000b40725714fe8c547c5b0c1472cba3554efa8171881565b6040516001600160a01b0390911681526020016101f3565b6101e7610452366004611493565b610d5b565b7f000000000000000000000000000000000000000000000000000000000076a70061021e565b61027761048b366004611467565b610dae565b7f00000000000000000000000000000000000000000000000003782dace9d9000061021e565b60025461021e565b61021e610dd3565b60006001600160e01b03198216637965db0b60e01b14806104f757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008281526020819052604090206001015461051881610f58565b6105228383610f65565b50505050565b6001600160a01b03811633146105515760405163334bd91960e11b815260040160405180910390fd5b61055b8282610ff7565b505050565b60006000805160206116d883398151915261057a81610f58565b60015460ff1661059d57604051636f312cbd60e01b815260040160405180910390fd5b6005546006546007546008546009546004946105be94909390929091611062565b925082156106a7578281600501546105d691906115ab565b6005820155805460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018590527f000000000000000000000000b40725714fe8c547c5b0c1472cba3554efa817189091169063a9059cbb906044016020604051808303816000875af115801561064e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061067291906115be565b506040518381527f1ece5b313ac5f9c73586967e102cfc3a37021b956dd4d2d7cc70128dbbe7c0dd9060200160405180910390a15b505090565b60006000805160206116d88339815191526106c681610f58565b60015460ff16156106ea57604051631fbde44560e01b815260040160405180910390fd5b836001600160a01b0381166107125760405163e6c4247b60e01b815260040160405180910390fd5b83806000036107345760405163162908e360e11b815260040160405180910390fd5b600554600490156107585760405163782ffe2f60e01b815260040160405180910390fd5b80546001600160a01b0319166001600160a01b03881617815560018101869055600080610784886111ee565b6002850182905590925090507f000000000000000000000000000000000000000000000000000000000076a7006107be62015180846115e0565b6107c8919061160d565b6003840155670de0b6b3a76400006108007f00000000000000000000000000000000000000000000000000b1a2bc2ec50000836115e0565b61080a919061160d565b8360040181905550876003600082825461082491906115ab565b90915550506040805184546001600160a01b03168152600185015460208201526002850154818301526003850154606082015260048501546080820152600585015460a082015290517f7723bdb0a63cf787f80dc359e4b1be62708b873a887dda0ff4a433ef9200094e9181900360c00190a150600198975050505050505050565b60006108d562278d007f00000000000000000000000000000000000000000000000000000000000000146115e0565b905090565b61090c6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b506001600160a01b03166000908152600a6020908152604091829020825160a0810184528154815260018201549281019290925260028101549282019290925260038201546060820152600490910154608082015290565b60015460009060ff161561098b57604051631fbde44560e01b815260040160405180910390fd5b6000805160206116d88339815191526109a381610f58565b8480848082146109d457604051630c55779960e31b8152600481019290925260248201526044015b60405180910390fd5b505060005b81811015610a3157610a298888838181106109f6576109f6611621565b9050602002016020810190610a0b91906114bd565b878784818110610a1d57610a1d611621565b90506020020135611241565b6001016109d9565b506001979650505050505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006000805160206116d8833981519152610a8281610f58565b60015460ff1615610aa657604051631fbde44560e01b815260040160405180910390fd5b6003546000819003610acb5760405163abd3bb5560e01b815260040160405180910390fd5b42841015610aec576040516307f5e89560e51b815260040160405180910390fd5b6001805460ff19168117905560028490556040516323b872dd60e01b8152336004820152306024820152604481018290528493507f000000000000000000000000b40725714fe8c547c5b0c1472cba3554efa817186001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610b73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b9791906115be565b5060408051828152602081018590527f02149ee86341dfe4a75ef32004c5df9f57f55651c72faeee9a9e2c8985c7957b910160405180910390a15050919050565b610c1a6040518060c0016040528060006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b506040805160c0810182526004546001600160a01b031681526005546020820152600654918101919091526007546060820152600854608082015260095460a082015290565b6040805160c0810182526004546001600160a01b0316815260055460208201819052600654928201839052600754606083018190526008546080840181905260095460a08501819052600095610cba949390929091611062565b91505090565b6001600160a01b0381166000908152600a60209081526040808320815160a081018352815480825260018301549482019490945260028201549281019290925260038101546060830152600401546080820152908203610d335760405163f43d198960e01b815260040160405180910390fd5b610d5481600001518260200151836040015184606001518560800151611062565b9392505050565b60015460009060ff1615610d8257604051631fbde44560e01b815260040160405180910390fd5b6000805160206116d8833981519152610d9a81610f58565b610da48484611241565b5060019392505050565b600082815260208190526040902060010154610dc981610f58565b6105228383610ff7565b60015460009060ff16610df957604051636f312cbd60e01b815260040160405180910390fd5b336000818152600a602052604081208054909103610e2a5760405163f43d198960e01b815260040160405180910390fd5b610e4b81600001548260010154836002015484600301548560040154611062565b925082156106a757828160040154610e6391906115ab565b60048083019190915560405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000b40725714fe8c547c5b0c1472cba3554efa81718169163a9059cbb91610ecc9186918891016001600160a01b03929092168252602082015260400190565b6020604051808303816000875af1158015610eeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0f91906115be565b50816001600160a01b03167f9fe9b7be9d151c7a8b6de49a1312ff27a15096d0d1d12999af85fe4310e0b12584604051610f4b91815260200190565b60405180910390a2505090565b610f6281336113cb565b50565b6000610f718383610a3f565b610fef576000838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055610fa73390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016104f7565b5060006104f7565b60006110038383610a3f565b15610fef576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016104f7565b6002546000904290826110957f000000000000000000000000000000000000000000000000000000000076a700836115ab565b905060006110c67f000000000000000000000000000000000000000000000000000000000000001462278d006115e0565b6110d090836115ab565b9050828410156110e75760009450505050506111e5565b808410611103576110f8868b611637565b9450505050506111e5565b818410156111395760006201518061111b8587611637565b611125919061160d565b9050611131818a6115e0565b9550506111c4565b88945060006111488386611637565b9050600061115962278d008361160d565b905060006201518061116e62278d008561164a565b611178919061160d565b9050611184828b6115e0565b61118e90896115ab565b97506111a06201518062278d0061160d565b6111aa828c6115e0565b6111b4919061160d565b6111be90896115ab565b97505050505b858511156111db576111d68686611637565b6111de565b60005b9450505050505b95945050505050565b600080670de0b6b3a76400006112247f00000000000000000000000000000000000000000000000003782dace9d90000856115e0565b61122e919061160d565b915061123a8284611637565b9050915091565b816001600160a01b0381166112695760405163e6c4247b60e01b815260040160405180910390fd5b818060000361128b5760405163162908e360e11b815260040160405180910390fd5b6001600160a01b0384166000908152600a60205260409020805485908290156112c957604051633dd3c8cf60e01b81526004016109cb92919061165e565b50508381556000806112da866111ee565b6001850182905590925090507f000000000000000000000000000000000000000000000000000000000076a70061131462015180846115e0565b61131e919061160d565b6002840155670de0b6b3a76400006113567f00000000000000000000000000000000000000000000000000b1a2bc2ec50000836115e0565b611360919061160d565b8360030181905550856003600082825461137a91906115ab565b92505081905550866001600160a01b03167f3e725d909beab7acd4e618b71089faf5b68e026ca0e7059ceec1bb643d268152846040516113ba91906116a2565b60405180910390a250505050505050565b6113d58282610a3f565b6114045760405163e2517d3f60e01b81526001600160a01b0382166004820152602481018390526044016109cb565b5050565b60006020828403121561141a57600080fd5b81356001600160e01b031981168114610d5457600080fd5b60006020828403121561144457600080fd5b5035919050565b80356001600160a01b038116811461146257600080fd5b919050565b6000806040838503121561147a57600080fd5b8235915061148a6020840161144b565b90509250929050565b600080604083850312156114a657600080fd5b6114af8361144b565b946020939093013593505050565b6000602082840312156114cf57600080fd5b610d548261144b565b60008083601f8401126114ea57600080fd5b50813567ffffffffffffffff81111561150257600080fd5b6020830191508360208260051b850101111561151d57600080fd5b9250929050565b6000806000806040858703121561153a57600080fd5b843567ffffffffffffffff81111561155157600080fd5b61155d878288016114d8565b909550935050602085013567ffffffffffffffff81111561157d57600080fd5b611589878288016114d8565b95989497509550505050565b634e487b7160e01b600052601160045260246000fd5b808201808211156104f7576104f7611595565b6000602082840312156115d057600080fd5b81518015158114610d5457600080fd5b80820281158282048414176104f7576104f7611595565b634e487b7160e01b600052601260045260246000fd5b60008261161c5761161c6115f7565b500490565b634e487b7160e01b600052603260045260246000fd5b818103818111156104f7576104f7611595565b600082611659576116596115f7565b500690565b6001600160a01b038316815281546020820152600182015460408201526002820154606082015260038201546080820152600482015460a082015260c08101610d54565b815481526001820154602082015260028201546040820152600382015460608201526004820154608082015260a081016104f756fea49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775a264697066735822122098af2369406d852234a7613c05663633e82ecd87cdfdf7c35f243f14749c0f6d64736f6c634300081b0033

Verified Source Code Full Match

Compiler: v0.8.27+commit.40a35a09 EVM: paris Optimization: Yes (200 runs)
AccessControl.sol 209 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
IAccessControl.sol 98 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted to signal this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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);
}
Context.sol 28 lines
// 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;
    }
}
ERC165.sol 27 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
CoindepoSaleDistribution.sol 418 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ICoindepoSaleDistribution} from "./ICoindepoSaleDistribution.sol";

/**
 * @title CoindepoSaleDistribution
 * @author PixelPlex.inc
 * @notice Manages vesting and distribution of tokens for the Coindepo sale event.
 * @dev Uses a two-phase vesting logic: phase one claims daily for N days, phase two claims daily for M days.
 */
contract CoindepoSaleDistribution is ICoindepoSaleDistribution, AccessControl {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    uint256 public constant ONE_DAY = 1 days;
    uint256 public constant ONE_MONTH = 30 days;

    IERC20 public immutable coinDepoToken;

    bool private _started;
    uint256 private _startedAt;
    uint256 private _totalLockAmount;

    uint256 private immutable _phaseOneTotalX18;
    uint256 private immutable _phaseOneDuration;
    uint256 private immutable _phaseTwoTotalWindows;
    uint256 private immutable _phaseTwoWindowX18;

    CustodyPart private _custodyPart;
    mapping(address user => VestingData) private _userToVestingData;

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function started() external view returns (bool status) {
        return _started;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function startedAt() external view returns (uint256 timestamp) {
        return _startedAt;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function totalLockAmount() external view returns (uint256 amount) {
        return _totalLockAmount;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function phaseOneTotalX18() external view returns (uint256 phaseOneShareX18) {
        return _phaseOneTotalX18;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function phaseOneDuration() external view returns (uint256 duration) {
        return _phaseOneDuration;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function phaseTwoDuration() external view returns (uint256 duration) {
        return _phaseTwoTotalWindows * ONE_MONTH;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function phaseTwoTotalWindows() external view returns (uint256 countOfWindows) {
        return _phaseTwoTotalWindows;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function phaseTwoWindowX18() external view returns (uint256 windowShareX18) {
        return _phaseTwoWindowX18;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function getCustodyPart() external view returns (CustodyPart memory custodyPart) {
        return _custodyPart;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function getVestingDataByUser(address user) external view returns (VestingData memory vestingData) {
        return _userToVestingData[user];
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function getAvailableCustodyPartClaimAmount() external view returns (uint256 amount) {
        CustodyPart memory custodyPart = _custodyPart;
        amount = _availableToClaim(
            custodyPart.amount,
            custodyPart.phaseOneTotalAmount,
            custodyPart.phaseOneDaily,
            custodyPart.phaseTwoWindowAmount,
            custodyPart.withdrawn
        );
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function getAvailableUserClaimAmount(address user) external view returns (uint256 amount) {
        VestingData memory vestingData = _userToVestingData[user];
        require(vestingData.amount != 0, HasNoVesting());
        amount = _availableToClaim(
            vestingData.amount,
            vestingData.phaseOneTotalAmount,
            vestingData.phaseOneDaily,
            vestingData.phaseTwoWindowAmount,
            vestingData.withdrawn
        );
    }

    /**
     * @notice Initializes the contract with the specified parameters.
     * @dev Sets up roles, immutable references and global phase params.
     *
     * Requirements:
     *  - `admin_` must not be zero address.
     *  - `coinDepoToken_` must not be zero address.
     *  - Vesting parameters must be valid:
     *      - `phaseOneTotalX18_` > 0 and <= 1e18.
     *      - `phaseOneDuration_` > 0.
     *      - `phaseTwoTotalWindows_` > 0.
     *      - `phaseTwoWindowX18_` > 0 and <= 1e18.
     *      - (`phaseTwoTotalWindows_` * `phaseTwoWindowX18_`) == 1e18.
     *
     * @param admin_ The address to be granted admin rights.
     * @param coinDepoToken_ The ERC20 token used for distribution.
     * @param phaseOneTotalX18_ The X18 fixed-point ratio (parts per 1e18) of phase one allocation.
     * @param phaseOneDuration_ Duration of phase one in seconds.
     * @param phaseTwoTotalWindows_ Number of vesting windows in phase two.
     * @param phaseTwoWindowX18_ Fraction (X18) of the phase two allocation for each window
     *                           (sum of all windows must be 1e18).
     */
    constructor(
        address admin_,
        address coinDepoToken_,
        uint256 phaseOneTotalX18_,
        uint256 phaseOneDuration_,
        uint256 phaseTwoTotalWindows_,
        uint256 phaseTwoWindowX18_
    )
        onlyValidAddress(admin_)
        onlyValidAddress(coinDepoToken_)
        onlyValidPhasesConfiguration(phaseOneTotalX18_, phaseOneDuration_, phaseTwoTotalWindows_, phaseTwoWindowX18_)
    {
        _grantRole(DEFAULT_ADMIN_ROLE, admin_);
        _grantRole(ADMIN_ROLE, admin_);
        coinDepoToken = IERC20(coinDepoToken_);
        _phaseOneTotalX18 = phaseOneTotalX18_;
        _phaseOneDuration = phaseOneDuration_;
        _phaseTwoTotalWindows = phaseTwoTotalWindows_;
        _phaseTwoWindowX18 = phaseTwoWindowX18_;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function start(uint256 startTimestamp) external onlyRole(ADMIN_ROLE) onlyNotStarted returns (uint256 startedAt_) {
        uint256 totalLockAmount_ = _totalLockAmount;
        require(totalLockAmount_ != 0, NoDistribution());
        require(startTimestamp >= block.timestamp, InvalidStartTimestamp());
        _started = true;
        _startedAt = startedAt_ = startTimestamp;
        coinDepoToken.transferFrom(msg.sender, address(this), totalLockAmount_);
        emit VestingStarted(totalLockAmount_, startedAt_);
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function createCustodyPart(
        address beneficiary,
        uint256 amount
    )
        external
        onlyRole(ADMIN_ROLE)
        onlyNotStarted
        onlyValidAddress(beneficiary)
        onlyValidAmount(amount)
        returns (bool)
    {
        CustodyPart storage custodyPart = _custodyPart;
        require(_custodyPart.amount == 0, CustodyPartAlreadyExists());
        custodyPart.beneficiary = beneficiary;
        custodyPart.amount = amount;
        (uint256 phaseOneTotalAmount, uint256 phaseTwoTotalAmount) = _calculatePhasesAmount(amount);
        custodyPart.phaseOneTotalAmount = phaseOneTotalAmount;
        custodyPart.phaseOneDaily = (phaseOneTotalAmount * ONE_DAY) / _phaseOneDuration;
        custodyPart.phaseTwoWindowAmount = (phaseTwoTotalAmount * _phaseTwoWindowX18) / 1e18;
        _totalLockAmount += amount;
        emit CustodyPartCreated(custodyPart);
        return true;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function createVestingData(
        address user,
        uint256 amount
    ) external onlyNotStarted onlyRole(ADMIN_ROLE) returns (bool) {
        _createVestingData(user, amount);
        return true;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function batchCreateVestingData(
        address[] calldata users,
        uint256[] calldata amounts
    ) external onlyNotStarted onlyRole(ADMIN_ROLE) returns (bool) {
        uint256 length = users.length;
        require(length == amounts.length, InvalidBatchInput(length, amounts.length));
        for (uint256 i = 0; i < length; i++) {
            _createVestingData(users[i], amounts[i]);
        }
        return true;
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function claimCustodyPart() external onlyRole(ADMIN_ROLE) onlyStarted returns (uint256 claimAmount) {
        CustodyPart storage custodyPart = _custodyPart;
        claimAmount = _availableToClaim(
            custodyPart.amount,
            custodyPart.phaseOneTotalAmount,
            custodyPart.phaseOneDaily,
            custodyPart.phaseTwoWindowAmount,
            custodyPart.withdrawn
        );
        if (claimAmount != 0) {
            custodyPart.withdrawn = custodyPart.withdrawn + claimAmount;
            coinDepoToken.transfer(custodyPart.beneficiary, claimAmount);
            emit CustodyPartClaimed(claimAmount);
        }
    }

    /**
     * @inheritdoc ICoindepoSaleDistribution
     */
    function claimVesting() external onlyStarted returns (uint256 claimAmount) {
        address caller = msg.sender;
        VestingData storage vestingData = _userToVestingData[caller];
        require(vestingData.amount != 0, HasNoVesting());
        claimAmount = _availableToClaim(
            vestingData.amount,
            vestingData.phaseOneTotalAmount,
            vestingData.phaseOneDaily,
            vestingData.phaseTwoWindowAmount,
            vestingData.withdrawn
        );
        if (claimAmount != 0) {
            vestingData.withdrawn = vestingData.withdrawn + claimAmount;
            coinDepoToken.transfer(caller, claimAmount);
            emit VestingClaimed(caller, claimAmount);
        }
    }

    /**
     * @notice Creates and stores vesting data for a user.
     * @param user The address of the user.
     * @param amount The total vesting amount for the user.
     */
    function _createVestingData(address user, uint256 amount) private onlyValidAddress(user) onlyValidAmount(amount) {
        VestingData storage vestingData = _userToVestingData[user];
        require(vestingData.amount == 0, VestingDataAlreadyExists(user, vestingData));
        vestingData.amount = amount;
        (uint256 phaseOneTotalAmount, uint256 phaseTwoTotalAmount) = _calculatePhasesAmount(amount);
        vestingData.phaseOneTotalAmount = phaseOneTotalAmount;
        vestingData.phaseOneDaily = (phaseOneTotalAmount * ONE_DAY) / _phaseOneDuration;
        vestingData.phaseTwoWindowAmount = (phaseTwoTotalAmount * _phaseTwoWindowX18) / 1e18;
        _totalLockAmount += amount;
        emit VestingDataCreated(user, vestingData);
    }

    /**
     * @notice Calculates absolute token amounts for phase one and phase two vesting periods.
     * @dev Uses `_phaseOneTotalX18` as share (fixed point, 18 decimals) for phase one.
     * @param amount The total allocation to split into phases.
     * @return phaseOneTotalAmount The token amount for phase one.
     * @return phaseTwoTotalAmount The token amount for phase two.
     */
    function _calculatePhasesAmount(
        uint256 amount
    ) private view returns (uint256 phaseOneTotalAmount, uint256 phaseTwoTotalAmount) {
        phaseOneTotalAmount = (amount * _phaseOneTotalX18) / 1e18;
        phaseTwoTotalAmount = amount - phaseOneTotalAmount;
    }

    /**
     * @dev Internal function to calculate the currently claimable amount for a vesting entry.
     * @param amount Total allocation for the entry.
     * @param phaseOneTotalAmount Allocated amount for phase one.
     * @param phaseOneDaily Per-day claimable amount for phase one.
     * @param phaseTwoWindowAmount Per-window claimable amount for phase two.
     * @param withdrawn Already withdrawn amount.
     * @return claimAmount The currently available amount to claim.
     */
    function _availableToClaim(
        uint256 amount,
        uint256 phaseOneTotalAmount,
        uint256 phaseOneDaily,
        uint256 phaseTwoWindowAmount,
        uint256 withdrawn
    ) private view returns (uint256 claimAmount) {
        uint256 timestamp = block.timestamp;
        uint256 phaseOneStartTimestamp = _startedAt;
        uint256 phaseTwoStartTimestamp = phaseOneStartTimestamp + _phaseOneDuration;
        uint256 phaseTwoEndTimestamp = phaseTwoStartTimestamp + ONE_MONTH * _phaseTwoTotalWindows;
        if (timestamp < phaseOneStartTimestamp) return 0;
        else if (timestamp >= phaseTwoEndTimestamp) return amount - withdrawn;
        else if (timestamp < phaseTwoStartTimestamp) {
            uint256 closedDaysInPhaseOne = (timestamp - phaseOneStartTimestamp) / ONE_DAY;
            claimAmount = phaseOneDaily * closedDaysInPhaseOne;
        } else {
            claimAmount = phaseOneTotalAmount; // phase one amount
            uint256 passedTimeInPhaseTwo = timestamp - phaseTwoStartTimestamp;
            uint256 closedWindowsInPhaseTwo = passedTimeInPhaseTwo / ONE_MONTH; // number of fully elapsed phase two windows
            uint256 closedDaysInCurrentWindow = (passedTimeInPhaseTwo % ONE_MONTH) / ONE_DAY; // number of closed days in current window
            claimAmount += phaseTwoWindowAmount * closedWindowsInPhaseTwo; // amount for closed windows
            claimAmount += (phaseTwoWindowAmount * closedDaysInCurrentWindow) / (ONE_MONTH / ONE_DAY); // amount for closed days in current window
        }
        claimAmount = claimAmount <= withdrawn ? 0 : claimAmount - withdrawn;
    }

    /**
     * @notice Checks the correctness of vesting phase parameters.
     * @dev Ensures that:
     *  - phaseOneTotalX18_ is not zero (non-zero phase one ratio, X18 fixed point).
     *  - phaseOneDuration_ is not zero (non-zero phase one duration).
     *  - phaseTwoTotalWindows_ is not zero (non-zero number of phase two windows).
     *  - phaseTwoWindowX18_ is not zero (non-zero window share for phase two).
     *  - The sum of all phase two windows strictly equals 1e18 (i.e. 100% of phase two allocation is distributed).
     * @param phaseOneTotalX18_ Ratio of phase one allocation, X18 fixed point.
     * @param phaseOneDuration_ Duration of phase one in seconds.
     * @param phaseTwoTotalWindows_ Number of vesting windows in phase two.
     * @param phaseTwoWindowX18_ Fraction of phase two allocation per window, X18 fixed point.
     */
    modifier onlyValidPhasesConfiguration(
        uint256 phaseOneTotalX18_,
        uint256 phaseOneDuration_,
        uint256 phaseTwoTotalWindows_,
        uint256 phaseTwoWindowX18_
    ) {
        require(
            phaseOneTotalX18_ != 0 &&
                phaseOneTotalX18_ <= 1e18 &&
                phaseOneDuration_ != 0 &&
                phaseTwoTotalWindows_ != 0 &&
                phaseTwoWindowX18_ != 0 &&
                phaseTwoWindowX18_ <= 1e18 &&
                phaseTwoTotalWindows_ * phaseTwoWindowX18_ == 1e18,
            InvalidPhasesConfiguration(phaseOneTotalX18_, phaseOneDuration_, phaseTwoTotalWindows_, phaseTwoWindowX18_)
        );
        _;
    }

    /**
     * @notice Modifier that checks that the passed address is not zero address.
     * @dev Checks the `address_` is not zero address.
     * @param address_ Address to check.
     */
    modifier onlyValidAddress(address address_) {
        require(address_ != address(0), InvalidAddress());
        _;
    }

    /**
     * @notice Modifier that checks that the passed amount is not zero.
     * @dev Checks that `amount` is greater than zero.
     * @param amount Amount to check.
     */
    modifier onlyValidAmount(uint256 amount) {
        require(amount != 0, InvalidAmount());
        _;
    }

    /**
     * @notice Modifier that allows functions to execute only if the lockup has started.
     * @dev Checks the `_started` flag to ensure the lockup process is active.
     */
    modifier onlyStarted() {
        require(_started, NotStarted());
        _;
    }

    /**
     * @notice Modifier that allows functions to execute only if the lockup has not started.
     * @dev Checks the `_started` flag to ensure the lockup process is inactive.
     */
    modifier onlyNotStarted() {
        require(!_started, AlreadyStarted());
        _;
    }
}
ICoindepoSaleDistribution.sol 314 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

/**
 * @title ICoindepoSaleDistribution
 * @author PixelPlex.inc
 * @notice Interface for Coindepo sale distribution contract with split vesting logic.
 */
interface ICoindepoSaleDistribution {
    struct CustodyPart {
        address beneficiary; ///< Address receiving claimed tokens for the custody part.
        uint256 amount; ///< Total allocation for the custody part.
        uint256 phaseOneTotalAmount; ///< Total amount for phase one.
        uint256 phaseOneDaily; ///< Amount claimed daily during phase one.
        uint256 phaseTwoWindowAmount; ///< Amount for each window during phase two.
        uint256 withdrawn; ///< Total amount already claimed.
    }

    struct VestingData {
        uint256 amount; ///< Total allocation for the user.
        uint256 phaseOneTotalAmount; ///< Total amount for phase one.
        uint256 phaseOneDaily; ///< Amount claimed daily during phase one.
        uint256 phaseTwoWindowAmount; ///< Amount for each window during phase two.
        uint256 withdrawn; ///< Total amount already claimed.
    }

    /**
     * @notice Emitted when the vesting process is started.
     * @param totalLockAmount Total amount of tokens locked for distribution.
     * @param startedAt Timestamp when vesting starts.
     */
    event VestingStarted(uint256 totalLockAmount, uint256 startedAt);

    /**
     * @notice Emitted when the custody allocation is created.
     * @param custodyPart Struct containing custody allocation details.
     */
    event CustodyPartCreated(CustodyPart custodyPart);

    /**
     * @notice Emitted when user vesting data is created.
     * @param user Address of the user.
     * @param vestingData Struct containing user's vesting allocation details.
     */
    event VestingDataCreated(address indexed user, VestingData vestingData);

    /**
     * @notice Emitted when the custody beneficiary claims tokens.
     * @param amount Amount of tokens claimed.
     */
    event CustodyPartClaimed(uint256 amount);

    /**
     * @notice Emitted when a user claims unlocked tokens.
     * @param user Address of the user claiming tokens.
     * @param amount Amount of tokens claimed.
     */
    event VestingClaimed(address indexed user, uint256 amount);

    /**
     * @notice Indicates that phases configuration was provided with invalid values.
     * @param phaseOneTotalX18 Total amount for phase one in X18 format.
     * @param phaseOneDuration Duration of phase one in seconds.
     * @param phaseTwoTotalWindows Number of vesting windows in phase two.
     * @param phaseTwoWindowX18 Fraction (X18) of the phase two allocation for each window.
     */
    error InvalidPhasesConfiguration(
        uint256 phaseOneTotalX18,
        uint256 phaseOneDuration,
        uint256 phaseTwoTotalWindows,
        uint256 phaseTwoWindowX18
    );

    /**
     * @notice Indicates that a zero address was provided where a valid address is required.
     */
    error InvalidAddress();

    /**
     * @notice Indicates that an invalid (zero) amount was provided where a positive amount is required.
     */
    error InvalidAmount();

    /**
     * @notice Indicates that the batch input lengths for users and amounts do not match.
     * @param usersLength The length of the users array.
     * @param amountLength The length of the amounts array.
     */
    error InvalidBatchInput(uint256 usersLength, uint256 amountLength);

    /**
     * @notice Indicates that the provided start timestamp is invalid.
     */
    error InvalidStartTimestamp();

    /**
     * @notice Indicates that the custody part has already been created and cannot be created again.
     */
    error CustodyPartAlreadyExists();

    /**
     * @notice Indicates that the vesting data for this user already exists.
     * @param user Address of the user for which vesting data already exists.
     * @param vestingData Existing vesting data for the user.
     */
    error VestingDataAlreadyExists(address user, VestingData vestingData);

    /**
     * @notice Indicates that the distribution is not initialized or has no tokens allocated.
     */
    error NoDistribution();

    /**
     * @notice Indicates that the user has no vesting allocation.
     */
    error HasNoVesting();

    /**
     * @notice Indicates that vesting has not started yet.
     */
    error NotStarted();

    /**
     * @notice Indicates that vesting has already started.
     */
    error AlreadyStarted();

    /**
     * @notice Returns whether the vesting process has started.
     * @return status True if vesting is active, false otherwise.
     */
    function started() external view returns (bool status);

    /**
     * @notice Returns the timestamp when the vesting process started.
     * @return timestamp The Unix timestamp of vesting start.
     */
    function startedAt() external view returns (uint256 timestamp);

    /**
     * @notice Returns the total amount of tokens locked for vesting.
     * @return amount The total lock amount in tokens.
     */
    function totalLockAmount() external view returns (uint256 amount);

    /**
     * @notice Returns the total amount of tokens allocated for phase one in X18 format.
     * @return phaseOneShareX18 The total amount of tokens allocated for phase one in X18 format.
     */
    function phaseOneTotalX18() external view returns (uint256 phaseOneShareX18);

    /**
     * @notice Returns the duration of the first phase (in seconds).
     * @return duration The duration of phase one.
     */
    function phaseOneDuration() external view returns (uint256 duration);

    /**
     * @notice Returns the total duration of phase two in seconds.
     * @dev Duration is calculated as phaseTwoTotalWindows × ONE_MONTH (30 days).
     * @return duration Total seconds in phase two.
     */
    function phaseTwoDuration() external view returns (uint256 duration);

    /**
     * @notice Returns the total number of vesting windows in phase two.
     * @return countOfWindows Number of phase two windows.
     */
    function phaseTwoTotalWindows() external view returns (uint256 countOfWindows);

    /**
     * @notice Returns the X18 fraction representing the share of each window in phase two.
     * @dev The sum over all windows must be 1e18.
     * @return windowShareX18 Fraction of phase two allocation for each window (X18).
     */
    function phaseTwoWindowX18() external view returns (uint256 windowShareX18);

    /**
     * @notice Returns the custody part information.
     * @return custodyPart The current custody part data structure.
     */
    function getCustodyPart() external view returns (CustodyPart memory custodyPart);

    /**
     * @notice Returns the vesting data for a specific user.
     * @param user The address of the user.
     * @return vestingData The vesting data structure for the user.
     */
    function getVestingDataByUser(address user) external view returns (VestingData memory vestingData);

    /**
     * @notice Returns the amount available for claim by the custody beneficiary.
     * @return amount The amount currently available to claim.
     */
    function getAvailableCustodyPartClaimAmount() external view returns (uint256 amount);

    /**
     * @notice Returns the amount available for claim by a specific user.
     * @param user The address of the user.
     * @return amount The amount currently available to claim for the user.
     */
    function getAvailableUserClaimAmount(address user) external view returns (uint256 amount);

    /**
     * @notice Starts the vesting process and locks the tokens in the contract.
     * @dev
     *  - Transfers all required tokens from the admin to the contract.
     *
     * Requirements:
     *  - Can only be called by an account with `ADMIN_ROLE`.
     *  - Can only be called before the vesting process has started.
     *  - The total lock amount must be non-zero.
     *  - The specified `startTimestamp` must not be less than the current block timestamp.
     *
     * - Emits a {VestingStarted} event.
     *
     * @param startTimestamp The Unix timestamp at which the vesting should start.
     * @return startedAt_ The actual timestamp when vesting was started.
     */
    function start(uint256 startTimestamp) external returns (uint256 startedAt_);

    /**
     * @notice Registers the custody part (off-chain distribution) for a specific beneficiary.
     * @dev
     *  - Updates the internal custody state.
     *
     * Requirements:
     *  - Can only be called by an account with `ADMIN_ROLE`.
     *  - Can only be called before the vesting process has started.
     *  - `beneficiary` must not be the zero address.
     *  - `amount` must be greater than zero.
     *  - The custody part must not already exist.
     *
     *  - Emits a {CustodyPartCreated} event.
     *
     * @param beneficiary The address to receive the custody part tokens.
     * @param amount The total amount of tokens to allocate.
     * @return True if successfully created.
     */
    function createCustodyPart(address beneficiary, uint256 amount) external returns (bool);

    /**
     * @notice Registers vesting allocation for a user.
     * @dev
     *  - Updates the internal vesting state for the user.
     *
     * Requirements:
     *  - Can only be called by an account with `ADMIN_ROLE`.
     *  - Can only be called before the vesting process has started.
     *  - `user` must not be the zero address.
     *  - `amount` must be greater than zero.
     *  - The vesting data for the user must not already exist.
     *
     *  - Emits a {VestingDataCreated} event.
     *
     * @param user The address of the user.
     * @param amount The total amount of tokens to allocate to the user.
     * @return True if successfully created.
     */
    function createVestingData(address user, uint256 amount) external returns (bool);

    /**
     * @notice Registers vesting allocations for multiple users in a single transaction.
     * @dev
     *  - Updates the internal vesting state for all users.
     *
     * Requirements:
     *  - Can only be called by an account with `ADMIN_ROLE`.
     *  - Can only be called before the vesting process has started.
     *  - `users` and `amounts` arrays must be the same length and not empty.
     *  - Each user address must not be zero.
     *  - Each amount must be greater than zero.
     *  - No user must already have vesting data.
     *
     *  - Emits a {VestingDataCreated} event for each user.
     *
     * @param users Array of user addresses.
     * @param amounts Array of token amounts to allocate to each user.
     * @return True if all allocations were successfully created.
     */
    function batchCreateVestingData(address[] calldata users, uint256[] calldata amounts) external returns (bool);

    /**
     * @notice Claims available tokens for the custody beneficiary, according to the vesting schedule.
     * @dev
     *  - Transfers tokens to the custody beneficiary if available.
     *
     * Requirements:
     *  - Can only be called by an account with `ADMIN_ROLE`.
     *  - Can only be called after the vesting process has started.
     *  - There must be tokens available to claim.
     *
     *  - Emits a {CustodyPartClaimed} event.
     *
     * @return claimAmount The amount of tokens claimed.
     */
    function claimCustodyPart() external returns (uint256 claimAmount);

    /**
     * @notice Claims available tokens for the caller (user) according to the vesting schedule.
     * @dev
     *  - Transfers tokens to the caller if available.
     *
     * Requirements:
     *  - Can only be called after the vesting process has started.
     *  - The caller must have vesting data.
     *  - There must be tokens available to claim.
     *
     *  - Emits a {VestingClaimed} event.
     *
     * @return claimAmount The amount of tokens claimed.
     */
    function claimVesting() external returns (uint256 claimAmount);
}

Read Contract

ADMIN_ROLE 0x75b238fc → bytes32
DEFAULT_ADMIN_ROLE 0xa217fddf → bytes32
ONE_DAY 0x863e76db → uint256
ONE_MONTH 0x20988cef → uint256
coinDepoToken 0xcf076267 → address
getAvailableCustodyPartClaimAmount 0xbf7024ea → uint256
getAvailableUserClaimAmount 0xcd87a96c → uint256
getCustodyPart 0xa3a50652 → tuple
getRoleAdmin 0x248a9ca3 → bytes32
getVestingDataByUser 0x6a9e1cea → tuple
hasRole 0x91d14854 → bool
phaseOneDuration 0xd26c3f31 → uint256
phaseOneTotalX18 0xea9a011b → uint256
phaseTwoDuration 0x5dd39c19 → uint256
phaseTwoTotalWindows 0x3ea2dfb9 → uint256
phaseTwoWindowX18 0x1cd6a375 → uint256
started 0x1f2698ab → bool
startedAt 0xf21f537d → uint256
supportsInterface 0x01ffc9a7 → bool
totalLockAmount 0xa111bab9 → uint256

Write Contract 9 functions

These functions modify contract state and require a wallet transaction to execute.

batchCreateVestingData 0x6d96bfa2
address[] users
uint256[] amounts
returns: bool
claimCustodyPart 0x4530c3e7
No parameters
returns: uint256
claimVesting 0xfedda89c
No parameters
returns: uint256
createCustodyPart 0x5193f9e0
address beneficiary
uint256 amount
returns: bool
createVestingData 0xd2489231
address user
uint256 amount
returns: bool
grantRole 0x2f2ff15d
bytes32 role
address account
renounceRole 0x36568abe
bytes32 role
address callerConfirmation
revokeRole 0xd547741f
bytes32 role
address account
start 0x95805dad
uint256 startTimestamp
returns: uint256

Recent Transactions

No transactions found for this address