ETH Price: $2,464.60 (-1.77%)

Contract Diff Checker

Contract Name:
VerseToken

Contract Source Code:

// SPDX-License-Identifier: --BCOM--

pragma solidity =0.8.17;

library MerkleProof {

    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    )
        internal
        pure
        returns (bool)
    {
        bytes32 computedHash = leaf;

        for (uint256 i = 0; i < proof.length; i++) {

            bytes32 proofElement = proof[i];

            computedHash = computedHash <= proofElement
                ? keccak256(abi.encodePacked(computedHash, proofElement))
                : keccak256(abi.encodePacked(proofElement, computedHash));
        }

        return computedHash == root;
    }
}

// SPDX-License-Identifier: --BCOM--

pragma solidity =0.8.17;

import "./MerkleProof.sol";
import "./VerseHelper.sol";

contract VerseClaimer is VerseHelper {

    bytes32 public immutable merkleRoot;
    uint256 public immutable createTime;

    uint256 immutable minimumTimeFrame;

    struct KeeperInfo {
        uint256 keeperRate;
        uint256 keeperTill;
        uint256 keeperInstant;
        uint256 keeperPayouts;
    }

    mapping(address => KeeperInfo) public keeperList;

    constructor(
        bytes32 _merkleRoot,
        uint256 _minimumTimeFrame,
        address _verseTokenAddress
    )
        VerseHelper(_verseTokenAddress)
    {
        require(
            _minimumTimeFrame > 0,
            "VerseClaimer: INVALID_TIMEFRAME"
        );

        require(
            _merkleRoot > 0,
            "VerseClaimer: INVALID_MERKLE_ROOT"
        );

        createTime = getNow();
        merkleRoot = _merkleRoot;
        minimumTimeFrame = _minimumTimeFrame;
    }

    function enrollRecipient(
        uint256 _index,
        address _recipient,
        uint256 _tokensLocked,
        uint256 _tokensOpened,
        uint256 _timeFrame,
        bytes32[] calldata _merkleProof
    )
        external
    {
        _enrollRecipient(
            _index,
            _recipient,
            _tokensLocked,
            _tokensOpened,
            _timeFrame,
            _merkleProof
        );
    }

    function enrollRecipientBulk(
        uint256 _index,
        address[] calldata _recipient,
        uint256[] calldata _tokensLocked,
        uint256[] calldata _tokensOpened,
        uint256[] calldata _timeFrame,
        bytes32[][] calldata _merkleProof
    )
        external
    {
        require(
            _recipient.length < 10,
            "VerseClaimer: TOO_MANY"
        );

        for (uint256 i = 0; i < _recipient.length; i++) {
            _enrollRecipient(
                _index + i,
                _recipient[i],
                _tokensLocked[i],
                _tokensOpened[i],
                _timeFrame[i],
                _merkleProof[i]
            );
        }
    }

    function _enrollRecipient(
        uint256 _index,
        address _recipient,
        uint256 _tokensLocked,
        uint256 _tokensOpened,
        uint256 _timeFrame,
        bytes32[] memory _merkleProof
    )
        private
    {
        require(
            keeperList[_recipient].keeperTill == 0,
            "VerseClaimer: RECIPIENT_ALREADY_ENROLLED"
        );

        bytes32 node = keccak256(
            abi.encodePacked(
                _index,
                _recipient,
                _tokensLocked,
                _tokensOpened,
                _timeFrame
            )
        );

        require(
            MerkleProof.verify(
                _merkleProof,
                merkleRoot,
                node
            ),
            "VerseClaimer: INVALID_PROOF"
        );

        _allocateTokens(
            _recipient,
            _tokensLocked,
            _tokensOpened,
            _timeFrame
        );
    }

    function _allocateTokens(
        address _recipient,
        uint256 _tokensLocked,
        uint256 _tokensOpened,
        uint256 _timeFrame
    )
        private
    {
        require(
            _timeFrame >= minimumTimeFrame,
            "VerseClaimer: INVALID_TIME_FRAME"
        );

        totalRequired = totalRequired
            + _tokensOpened
            + _tokensLocked;

        keeperList[_recipient].keeperTill = createTime
            + _timeFrame;

        keeperList[_recipient].keeperRate = _tokensLocked
            / _timeFrame;

        keeperList[_recipient].keeperInstant = _tokensLocked
            % _timeFrame
            + _tokensOpened;

        _checkVerseBalance(
            totalRequired
        );

        emit recipientEnrolled(
            _recipient,
            _timeFrame,
            _tokensLocked,
            _tokensOpened
        );
    }

    function enrollAndScrape(
        uint256 _index,
        uint256 _tokensLocked,
        uint256 _tokensOpened,
        uint256 _timeFrame,
        bytes32[] calldata _merkleProof
    )
        external
    {
        _enrollRecipient(
            _index,
            msg.sender,
            _tokensLocked,
            _tokensOpened,
            _timeFrame,
            _merkleProof
        );

        _scrapeTokens(
            msg.sender
        );
    }

    function scrapeMyTokens()
        external
    {
        _scrapeTokens(
            msg.sender
        );
    }

    function _scrapeTokens(
        address _recipient
    )
        private
    {
        uint256 scrapeAmount = availableBalance(
            _recipient
        );

        keeperList[_recipient].keeperPayouts += scrapeAmount;

        _safeVerseScrape(
            _recipient,
            scrapeAmount
        );

        emit tokensScraped(
            _recipient,
            scrapeAmount,
            getNow()
        );
    }

    function availableBalance(
        address _recipient
    )
        public
        view
        returns (uint256 balance)
    {
        uint256 timeNow = getNow();
        uint256 timeMax = keeperList[_recipient].keeperTill;

        if (timeMax == 0) return 0;

        uint256 timePassed = timeNow > timeMax
            ? timeMax - createTime
            : timeNow - createTime;

        balance = keeperList[_recipient].keeperRate
            * timePassed
            + keeperList[_recipient].keeperInstant
            - keeperList[_recipient].keeperPayouts;
    }

    function lockedBalance(
        address _recipient
    )
        external
        view
        returns (uint256 balance)
    {
        uint256 timeNow = getNow();

        uint256 timeRemaining =
            keeperList[_recipient].keeperTill > timeNow ?
            keeperList[_recipient].keeperTill - timeNow : 0;

        balance = keeperList[_recipient].keeperRate
            * timeRemaining;
    }
}

// SPDX-License-Identifier: --BCOM--

pragma solidity =0.8.17;

contract VerseHelper {

    uint256 public totalRequired;
    address public immutable verseToken;

    event recipientEnrolled(
        address indexed recipient,
        uint256 timeFrame,
        uint256 tokensLocked,
        uint256 tokensOpened
    );

    event tokensScraped(
        address indexed scraper,
        uint256 scrapedAmount,
        uint256 timestamp
    );

    constructor(
        address _verseTokenAddress
    ) {
        if (_verseTokenAddress == address(0x0)) {
            revert("VerseHelper: INVALID_VERSE_TOKEN");
        }

        verseToken = _verseTokenAddress;
    }

    bytes4 private constant TRANSFER = bytes4(
        keccak256(
            bytes(
                "transfer(address,uint256)"
            )
        )
    );

    bytes4 private constant BALANCEOF = bytes4(
        keccak256(
            bytes(
                "balanceOf(address)"
            )
        )
    );

    function _safeVerseScrape(
        address _to,
        uint256 _scrapeAmount
    )
        internal
    {
        totalRequired -= _scrapeAmount;

        (bool success, bytes memory data) = verseToken.call(
            abi.encodeWithSelector(
                TRANSFER,
                _to,
                _scrapeAmount
            )
        );

        require(
            success && (
                abi.decode(
                    data, (bool)
                )
            ),
            "VerseHelper: TRANSFER_FAILED"
        );
    }

    function _checkVerseBalance(
        uint256 _required
    )
        internal
    {
        (bool success, bytes memory data) = verseToken.call(
            abi.encodeWithSelector(
                BALANCEOF,
                address(this)
            )
        );

        require(
            success && abi.decode(
                data, (uint256)
            ) >= _required,
            "VerseHelper: BALANCE_CHECK_FAILED"
        );
    }

    function getNow()
        public
        view
        returns (uint256 time)
    {
        time = block.timestamp;
    }
}

// SPDX-License-Identifier: --BCOM--

pragma solidity =0.8.17;

import "./VerseClaimer.sol";

contract VerseToken {

    string public constant name = "Verse";
    string public constant symbol = "VERSE";
    uint8 public constant decimals = 18;

    VerseClaimer public immutable claimer;

    address constant ZERO_ADDRESS = address(0);
    uint256 constant UINT256_MAX = type(uint256).max;

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;
    mapping(address => uint256) public nonces;

    bytes32 public immutable DOMAIN_SEPARATOR;
    bytes32 public constant PERMIT_TYPEHASH = keccak256(
        "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
    );

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );

    event Transfer(
        address indexed from,
        address indexed to,
        uint256 value
    );

    constructor(
        uint256 _initialSupply,
        uint256 _minimumTimeFrame,
        bytes32 _merkleRoot
    ) {
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );

        claimer = new VerseClaimer(
            _merkleRoot,
            _minimumTimeFrame,
            address(this)
        );

        _mint(
            address(claimer),
            _initialSupply
        );
    }

    function _mint(
        address _to,
        uint256 _value
    )
        internal
    {
        totalSupply =
        totalSupply + _value;

        unchecked {
            balanceOf[_to] =
            balanceOf[_to] + _value;
        }

        emit Transfer(
            ZERO_ADDRESS,
            _to,
            _value
        );
    }

    function burn(
        uint256 _value
    )
        external
    {
        _burn(
            msg.sender,
            _value
        );
    }

    function _burn(
        address _from,
        uint256 _value
    )
        internal
    {
        unchecked {
            totalSupply =
            totalSupply - _value;
        }

        balanceOf[_from] =
        balanceOf[_from] - _value;

        emit Transfer(
            _from,
            ZERO_ADDRESS,
            _value
        );
    }

    function _approve(
        address _owner,
        address _spender,
        uint256 _value
    )
        private
    {
        allowance[_owner][_spender] = _value;

        emit Approval(
            _owner,
            _spender,
            _value
        );
    }

    function _transfer(
        address _from,
        address _to,
        uint256 _value
    )
        private
    {
        balanceOf[_from] =
        balanceOf[_from] - _value;

        unchecked {
            balanceOf[_to] =
            balanceOf[_to] + _value;
        }

        emit Transfer(
            _from,
            _to,
            _value
        );
    }

    function approve(
        address _spender,
        uint256 _value
    )
        external
        returns (bool)
    {
        _approve(
            msg.sender,
            _spender,
            _value
        );

        return true;
    }

    function increaseAllowance(
        address _spender,
        uint256 _addedValue
    )
        external
        returns (bool)
    {
        _approve(
            msg.sender,
            _spender,
            allowance[msg.sender][_spender] + _addedValue
        );

        return true;
    }

    function decreaseAllowance(
        address _spender,
        uint256 _subtractedValue
    )
        external
        returns (bool)
    {
        _approve(
            msg.sender,
            _spender,
            allowance[msg.sender][_spender] - _subtractedValue
        );

        return true;
    }

    function transfer(
        address _to,
        uint256 _value
    )
        external
        returns (bool)
    {
        _transfer(
            msg.sender,
            _to,
            _value
        );

        return true;
    }

    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    )
        external
        returns (bool)
    {
        if (allowance[_from][msg.sender] != UINT256_MAX) {
            allowance[_from][msg.sender] -= _value;
        }

        _transfer(
            _from,
            _to,
            _value
        );

        return true;
    }

    function permit(
        address _owner,
        address _spender,
        uint256 _value,
        uint256 _deadline,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    )
        external
    {
        require(
            _deadline >= block.timestamp,
            "VerseToken: PERMIT_CALL_EXPIRED"
        );

        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        PERMIT_TYPEHASH,
                        _owner,
                        _spender,
                        _value,
                        nonces[_owner]++,
                        _deadline
                    )
                )
            )
        );

        if (uint256(_s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            revert("VerseToken: INVALID_SIGNATURE");
        }

        address recoveredAddress = ecrecover(
            digest,
            _v,
            _r,
            _s
        );

        require(
            recoveredAddress != ZERO_ADDRESS &&
            recoveredAddress == _owner,
            "VerseToken: INVALID_SIGNATURE"
        );

        _approve(
            _owner,
            _spender,
            _value
        );
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):