ETH Price: $2,969.39 (-4.05%)
Gas: 2 Gwei

Contract Diff Checker

Contract Name:
GNBU

Contract Source Code:

File 1 of 1 : GNBU

pragma solidity =0.8.0;

// ----------------------------------------------------------------------------
// GNBU token main contract (2021)
//
// Symbol       : GNBU
// Name         : Nimbus Governance Token
// Total supply : 100.000.000 (burnable)
// Decimals     : 18
// ----------------------------------------------------------------------------
// SPDX-License-Identifier: MIT
// ----------------------------------------------------------------------------

interface IERC20 {
    function totalSupply() external view returns (uint);
    function balanceOf(address tokenOwner) external view returns (uint balance);
    function allowance(address tokenOwner, address spender) external view returns (uint remaining);
    function transfer(address to, uint tokens) external returns (bool success);
    function approve(address spender, uint tokens) external returns (bool success);
    function transferFrom(address from, address to, uint tokens) external returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

contract Ownable {
    address public owner;
    address public newOwner;

    event OwnershipTransferred(address indexed from, address indexed to);

    constructor() {
        owner = msg.sender;
        emit OwnershipTransferred(address(0), owner);
    }

    modifier onlyOwner {
        require(msg.sender == owner, "Ownable: Caller is not the owner");
        _;
    }

    function transferOwnership(address transferOwner) public onlyOwner {
        require(transferOwner != newOwner);
        newOwner = transferOwner;
    }

    function acceptOwnership() virtual public {
        require(msg.sender == newOwner);
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
        newOwner = address(0);
    }
}

contract Pausable is Ownable {
    event Pause();
    event Unpause();

    bool public paused = false;


    modifier whenNotPaused() {
        require(!paused);
        _;
    }

    modifier whenPaused() {
        require(paused);
        _;
    }

    function pause() onlyOwner whenNotPaused public {
        paused = true;
        Pause();
    }

    function unpause() onlyOwner whenPaused public {
        paused = false;
        Unpause();
    }
}

contract GNBU is Ownable, Pausable {
    string public constant name = "Nimbus Governance Token";
    string public constant symbol = "GNBU";
    uint8 public constant decimals = 18;
    uint96 public totalSupply = 100_000_000e18; // 100 million GNBU
    mapping (address => mapping (address => uint96)) internal allowances;

    mapping (address => uint96) private _unfrozenBalances;
    mapping (address => uint32) private _vestingNonces;
    mapping (address => mapping (uint32 => uint96)) private _vestingAmounts;
    mapping (address => mapping (uint32 => uint96)) private _unvestedAmounts;
    mapping (address => mapping (uint32 => uint)) private _vestingReleaseStartDates;
    mapping (address => bool) public vesters;

    uint96 private vestingFirstPeriod = 60 days;
    uint96 private vestingSecondPeriod = 152 days;

    address[] public supportUnits;
    uint public supportUnitsCnt;

    mapping (address => address) public delegates;
    struct Checkpoint {
        uint32 fromBlock;
        uint96 votes;
    }
    mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
    mapping (address => uint32) public numCheckpoints;

    bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
    bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
    bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    mapping (address => uint) public nonces;
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
    event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
    event Unvest(address user, uint amount);

    constructor() {
        _unfrozenBalances[owner] = uint96(totalSupply);
        emit Transfer(address(0), owner, totalSupply);
    }

    function freeCirculation() external view returns (uint) {
        uint96 systemAmount = _unfrozenBalances[owner];
        for (uint i; i < supportUnits.length; i++) {
            systemAmount = add96(systemAmount, _unfrozenBalances[supportUnits[i]], "GNBU::freeCirculation: adding overflow");
        }
        return sub96(totalSupply, systemAmount, "GNBU::freeCirculation: amount exceed totalSupply");
    }
    
    function allowance(address account, address spender) external view returns (uint) {
        return allowances[account][spender];
    }

    function approve(address spender, uint rawAmount) external whenNotPaused returns (bool) {
        uint96 amount;
        if (rawAmount == uint(2 ** 256 - 1)) {
            amount = uint96(2 ** 96 - 1);
        } else {
            amount = safe96(rawAmount, "GNBU::approve: amount exceeds 96 bits");
        }

        allowances[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);
        return true;
    }
    
    function permit(address owner, address spender, uint rawAmount, uint deadline, uint8 v, bytes32 r, bytes32 s) external whenNotPaused {
        uint96 amount;
        if (rawAmount == uint(2 ** 256 - 1)) {
            amount = uint96(2 ** 96 - 1);
        } else {
            amount = safe96(rawAmount, "GNBU::permit: amount exceeds 96 bits");
        }

        bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)));
        bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, rawAmount, nonces[owner]++, deadline));
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), "GNBU::permit: invalid signature");
        require(signatory == owner, "GNBU::permit: unauthorized");
        require(block.timestamp <= deadline, "GNBU::permit: signature expired");

        allowances[owner][spender] = amount;

        emit Approval(owner, spender, amount);
    }
       
    function balanceOf(address account) public view returns (uint) {
        uint96 amount = _unfrozenBalances[account];
        if (_vestingNonces[account] == 0) return amount;
        for (uint32 i = 1; i <= _vestingNonces[account]; i++) {
            uint96 unvested = sub96(_vestingAmounts[account][i], _unvestedAmounts[account][i], "GNBU::balanceOf: unvested exceed vested amount");
            amount = add96(amount, unvested, "GNBU::balanceOf: overflow");
        }
        return amount;
    }

    function availableForUnvesting(address user) external view returns (uint unvestAmount) {
        if (_vestingNonces[user] == 0) return 0;
        for (uint32 i = 1; i <= _vestingNonces[user]; i++) {
            if (_vestingAmounts[user][i] == _unvestedAmounts[user][i]) continue;
            if (_vestingReleaseStartDates[user][i] > block.timestamp) break;
            uint toUnvest = mul96((block.timestamp - _vestingReleaseStartDates[user][i]), (_vestingAmounts[user][i])) / vestingSecondPeriod;
            if (toUnvest > _vestingAmounts[user][i]) {
                toUnvest = _vestingAmounts[user][i];
            } 
            toUnvest -= _unvestedAmounts[user][i];
            unvestAmount += toUnvest;
        }
    }

    function availableForTransfer(address account) external view returns (uint) {
        return _unfrozenBalances[account];
    }

    function vestingInfo(address user, uint32 nonce) external view returns (uint vestingAmount, uint unvestedAmount, uint vestingReleaseStartDate) {
        vestingAmount = _vestingAmounts[user][nonce];
        unvestedAmount = _unvestedAmounts[user][nonce];
        vestingReleaseStartDate = _vestingReleaseStartDates[user][nonce];
    }

    function vestingNonces(address user) external view returns (uint lastNonce) {
        return _vestingNonces[user];
    }
    
    function transfer(address dst, uint rawAmount) external whenNotPaused returns (bool) {
        uint96 amount = safe96(rawAmount, "GNBU::transfer: amount exceeds 96 bits");
        _transferTokens(msg.sender, dst, amount);
        return true;
    }
    
    function transferFrom(address src, address dst, uint rawAmount) external whenNotPaused returns (bool) {
        address spender = msg.sender;
        uint96 spenderAllowance = allowances[src][spender];
        uint96 amount = safe96(rawAmount, "GNBU::approve: amount exceeds 96 bits");

        if (spender != src && spenderAllowance != uint96(2 ** 96 - 1)) {
            uint96 newAllowance = sub96(spenderAllowance, amount, "GNBU::transferFrom: transfer amount exceeds spender allowance");
            allowances[src][spender] = newAllowance;

            emit Approval(src, spender, newAllowance);
        }

        _transferTokens(src, dst, amount);
        return true;
    }
    
    function delegate(address delegatee) public whenNotPaused {
        return _delegate(msg.sender, delegatee);
    }
    
    function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) public whenNotPaused {
        bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)));
        bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), "GNBU::delegateBySig: invalid signature");
        require(nonce == nonces[signatory]++, "GNBU::delegateBySig: invalid nonce");
        require(block.timestamp <= expiry, "GNBU::delegateBySig: signature expired");
        return _delegate(signatory, delegatee);
    }

    function unvest() external whenNotPaused returns (uint96 unvested) {
        require (_vestingNonces[msg.sender] > 0, "GNBU::unvest:No vested amount");
        for (uint32 i = 1; i <= _vestingNonces[msg.sender]; i++) {
            if (_vestingAmounts[msg.sender][i] == _unvestedAmounts[msg.sender][i]) continue;
            if (_vestingReleaseStartDates[msg.sender][i] > block.timestamp) break;
            uint96 toUnvest = mul96((block.timestamp - _vestingReleaseStartDates[msg.sender][i]), _vestingAmounts[msg.sender][i]) / vestingSecondPeriod;
            if (toUnvest > _vestingAmounts[msg.sender][i]) {
                toUnvest = _vestingAmounts[msg.sender][i];
            } 
            uint96 totalUnvestedForNonce = toUnvest;
            toUnvest = sub96(toUnvest, _unvestedAmounts[msg.sender][i], "GNBU::unvest: already unvested amount exceeds toUnvest");
            unvested = add96(unvested, toUnvest, "GNBU::unvest: adding overflow");
            _unvestedAmounts[msg.sender][i] = totalUnvestedForNonce;
        }
        _unfrozenBalances[msg.sender] = add96(_unfrozenBalances[msg.sender], unvested, "GNBU::unvest: adding overflow");
        emit Unvest(msg.sender, unvested);
    }
    
    function getCurrentVotes(address account) external view returns (uint96) {
        uint32 nCheckpoints = numCheckpoints[account];
        return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
    }
    
    function getPriorVotes(address account, uint blockNumber) public view returns (uint96) {
        require(blockNumber < block.number, "GNBU::getPriorVotes: not yet determined");

        uint32 nCheckpoints = numCheckpoints[account];
        if (nCheckpoints == 0) {
            return 0;
        }

        // First check most recent balance
        if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
            return checkpoints[account][nCheckpoints - 1].votes;
        }

        // Next check implicit zero balance
        if (checkpoints[account][0].fromBlock > blockNumber) {
            return 0;
        }

        uint32 lower = 0;
        uint32 upper = nCheckpoints - 1;
        while (upper > lower) {
            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            Checkpoint memory cp = checkpoints[account][center];
            if (cp.fromBlock == blockNumber) {
                return cp.votes;
            } else if (cp.fromBlock < blockNumber) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return checkpoints[account][lower].votes;
    }
    
    function _delegate(address delegator, address delegatee) internal {
        address currentDelegate = delegates[delegator];
        uint96 delegatorBalance = _unfrozenBalances[delegator];
        delegates[delegator] = delegatee;

        emit DelegateChanged(delegator, currentDelegate, delegatee);

        _moveDelegates(currentDelegate, delegatee, delegatorBalance);
    }

    function _transferTokens(address src, address dst, uint96 amount) internal {
        require(src != address(0), "GNBU::_transferTokens: cannot transfer from the zero address");
        require(dst != address(0), "GNBU::_transferTokens: cannot transfer to the zero address");

        _unfrozenBalances[src] = sub96(_unfrozenBalances[src], amount, "GNBU::_transferTokens: transfer amount exceeds balance");
        _unfrozenBalances[dst] = add96(_unfrozenBalances[dst], amount, "GNBU::_transferTokens: transfer amount overflows");
        emit Transfer(src, dst, amount);

        _moveDelegates(delegates[src], delegates[dst], amount);
    }
    
    function _moveDelegates(address srcRep, address dstRep, uint96 amount) internal {
        if (srcRep != dstRep && amount > 0) {
            if (srcRep != address(0)) {
                uint32 srcRepNum = numCheckpoints[srcRep];
                uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
                uint96 srcRepNew = sub96(srcRepOld, amount, "GNBU::_moveVotes: vote amount underflows");
                _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
            }

            if (dstRep != address(0)) {
                uint32 dstRepNum = numCheckpoints[dstRep];
                uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
                uint96 dstRepNew = add96(dstRepOld, amount, "GNBU::_moveVotes: vote amount overflows");
                _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
            }
        }
    }
    
    function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes) internal {
      uint32 blockNumber = safe32(block.number, "GNBU::_writeCheckpoint: block number exceeds 32 bits");

      if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
          checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
      } else {
          checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
          numCheckpoints[delegatee] = nCheckpoints + 1;
      }

      emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
    }

    function _vest(address user, uint96 amount) private {
        uint32 nonce = ++_vestingNonces[user];
        _vestingAmounts[user][nonce] = amount;
        _vestingReleaseStartDates[user][nonce] = block.timestamp + vestingFirstPeriod;
        _unfrozenBalances[owner] = sub96(_unfrozenBalances[owner], amount, "GNBU::_vest: exceeds owner balance");
        emit Transfer(owner, user, amount);
    }


    
    function burnTokens(uint rawAmount) public onlyOwner returns (bool success) {
        uint96 amount = safe96(rawAmount, "GNBU::burnTokens: amount exceeds 96 bits");
        require(amount <= _unfrozenBalances[owner]);
        _unfrozenBalances[owner] = sub96(_unfrozenBalances[owner], amount, "GNBU::burnTokens: transfer amount exceeds balance");
        totalSupply = sub96(totalSupply, amount, "GNBU::burnTokens: transfer amount exceeds total supply");
        emit Transfer(owner, address(0), amount);
        return true;
    }

    function vest(address user, uint rawAmount) external {
        require (vesters[msg.sender], "GNBU::vest: not vester");
        uint96 amount = safe96(rawAmount, "GNBU::vest: amount exceeds 96 bits");
        _vest(user, amount);
    }
    
   
    function multisend(address[] memory to, uint[] memory values) public onlyOwner returns (uint) {
        require(to.length == values.length);
        require(to.length < 100);
        uint sum;
        for (uint j; j < values.length; j++) {
            sum += values[j];
        }
        uint96 _sum = safe96(sum, "GNBU::transfer: amount exceeds 96 bits");
        _unfrozenBalances[owner] = sub96(_unfrozenBalances[owner], _sum, "GNBU::_transferTokens: transfer amount exceeds balance");
        for (uint i; i < to.length; i++) {
            _unfrozenBalances[to[i]] = add96(_unfrozenBalances[to[i]], uint96(values[i]), "GNBU::_transferTokens: transfer amount exceeds balance");
            emit Transfer(owner, to[i], values[i]);
        }
        return(to.length);
    }

    function multivest(address[] memory to, uint[] memory values) external onlyOwner returns (uint) {
        require(to.length == values.length);
        require(to.length < 100);
        uint sum;
        for (uint j; j < values.length; j++) {
            sum += values[j];
        }
        uint96 _sum = safe96(sum, "GNBU::multivest: amount exceeds 96 bits");
        _unfrozenBalances[owner] = sub96(_unfrozenBalances[owner], _sum, "GNBU::multivest: transfer amount exceeds balance");
        for (uint i; i < to.length; i++) {
            uint32 nonce = ++_vestingNonces[to[i]];
            _vestingAmounts[to[i]][nonce] = uint96(values[i]);
            _vestingReleaseStartDates[to[i]][nonce] = block.timestamp + vestingFirstPeriod;
            emit Transfer(owner, to[i], values[i]);
        }
        return(to.length);
    }
    
    function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {
        return IERC20(tokenAddress).transfer(owner, tokens);
    }

    function updateVesters(address vester, bool isActive) external onlyOwner { 
        vesters[vester] = isActive;
    }

    function acceptOwnership() public override {
        require(msg.sender == newOwner);
        uint96 amount = _unfrozenBalances[owner];
        _transferTokens(owner, newOwner, amount);
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
        newOwner = address(0);
    }

    function updateSupportUnitAdd(address newSupportUnit) external onlyOwner {
        for (uint i; i < supportUnits.length; i++) {
            require (supportUnits[i] != newSupportUnit, "GNBU::updateSupportUnitAdd: support unit exists");
        }
        supportUnits.push(newSupportUnit);
        supportUnitsCnt++;
    }

    function updateSupportUnitRemove(uint supportUnitIndex) external onlyOwner {
        supportUnits[supportUnitIndex] = supportUnits[supportUnits.length - 1];
        supportUnits.pop();
        supportUnitsCnt--;
    }
    



    function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
        require(n < 2**32, errorMessage);
        return uint32(n);
    }

    function safe96(uint n, string memory errorMessage) internal pure returns (uint96) {
        require(n < 2**96, errorMessage);
        return uint96(n);
    }

    function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {
        uint96 c = a + b;
        require(c >= a, errorMessage);
        return c;
    }

    function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {
        require(b <= a, errorMessage);
        return a - b;
    }

    function getChainId() internal view returns (uint) {
        return block.chainid;
    }

        
    function mul96(uint96 a, uint96 b) internal pure returns (uint96) {
        if (a == 0) {
            return 0;
        }
        uint96 c = a * b;
        require(c / a == b, "GNBU:mul96: multiplication overflow");
        return c;
    }

    function mul96(uint256 a, uint96 b) internal pure returns (uint96) {
        uint96 _a = safe96(a, "GNBU:mul96: amount exceeds uint96");
        if (_a == 0) {
            return 0;
        }
        uint96 c = _a * b;
        require(c / _a == b, "GNBU:mul96: multiplication overflow");
        return c;
    }
}

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

Context size (optional):