ETH Price: $2,558.78 (-4.61%)

Transaction Decoder

Block:
18389219 at Oct-20-2023 04:41:35 AM +UTC
Transaction Fee:
0.000614242663337928 ETH $1.57
Gas Used:
84,246 Gas / 7.291060268 Gwei

Emitted Events:

354 GTC.Approval( owner=[Sender] 0x07f5813f95462da339f57e401e5cc12a7f602e50, spender=[Receiver] IDStaking, amount=9999978000000000000000000 )
355 GTC.Transfer( from=[Sender] 0x07f5813f95462da339f57e401e5cc12a7f602e50, to=[Receiver] IDStaking, amount=11000000000000000000 )
356 IDStaking.selfStake( roundId=4, staker=[Sender] 0x07f5813f95462da339f57e401e5cc12a7f602e50, amount=11000000000000000000, staked=True )

Account State Difference:

  Address   Before After State Difference Code
0x07F5813f...A7F602e50
0.00591216092341227 Eth
Nonce: 67
0.005297918260074342 Eth
Nonce: 68
0.000614242663337928
0x0E3efD5B...7F2A0e95F
(Titan Builder)
14.380594370796135154 Eth14.380602795396135154 Eth0.0000084246
0xDe30da39...1F348163F

Execution Trace

IDStaking.stake( roundId=4, amount=11000000000000000000 )
  • GTC.transferFrom( src=0x07F5813f95462Da339F57e401e5cc12A7F602e50, dst=0x0E3efD5BE54CC0f4C64e0D186b0af4b7F2A0e95F, rawAmount=11000000000000000000 ) => ( True )
    File 1 of 2: IDStaking
    //SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0 <0.9.0;
    import {Staking} from "./Staking.sol";
    import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
    contract IDStaking is Staking, EIP712, AccessControl {
        uint256 public latestRound;
        // 0x7ba1e5E9d013EaE624D274bfbAC886459F291081
        address public trustedSigner;
        struct Round {
            string meta;
            uint256 tvl;
            uint256 start;
            uint256 duration;
        }
        mapping(uint256 => mapping(bytes32 => uint256)) public xStakes;
        mapping(uint256 => Round) rounds;
        mapping(bytes32 => bool) usedDigest;
        event roundCreated(uint256 id);
        event selfStake(
            uint256 roundId,
            address staker,
            uint256 amount,
            bool staked
        );
        event xStake(
            uint256 roundId,
            address staker,
            address user,
            uint256 amount,
            bool staked
        );
        event tokenMigrated(
            address staker,
            uint256 amount,
            uint256 fromRound,
            uint256 toRound
        );
        modifier roundExists(uint256 roundId) {
            require(roundId > 0 && roundId <= latestRound, "Round does not exist");
            _;
        }
        modifier canStakeRound(uint256 roundId) {
            require(roundId > 0 && roundId <= latestRound, "Round does not exist");
            require(
                rounds[roundId].start + rounds[roundId].duration > block.timestamp,
                "Can't stake on this round"
            );
            _;
        }
        modifier canUnstakeRound(uint256 roundId) {
            require(roundId > 0 && roundId <= latestRound, "Round does not exist");
            require(
                rounds[roundId].start + rounds[roundId].duration < block.timestamp,
                "Can't unstake an active round"
            );
            _;
        }
        constructor(IERC20 _token)
            EIP712("IDStaking", "1.0")
        {
            _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
            token = _token;
        }
        function addAdmin(address admin) public {
            grantRole(DEFAULT_ADMIN_ROLE, admin);
        }
        function removeAdmin(address admin) public {
            revokeRole(DEFAULT_ADMIN_ROLE, admin);
        }
        function createRound(
            uint256 start,
            uint256 duration,
            string calldata meta
        ) external onlyRole(DEFAULT_ADMIN_ROLE) {
            if (latestRound > 0) {
                require(
                    start >
                        rounds[latestRound].start + rounds[latestRound].duration,
                    "new rounds have to start after old rounds"
                );
            }
            require(start >= block.timestamp, "new rounds should be in the future");
            latestRound++;
            uint256 currentRound = latestRound;
            rounds[currentRound].start = start;
            rounds[currentRound].duration = duration;
            rounds[currentRound].meta = meta;
            emit roundCreated(currentRound);
        }
        // stake
        function stake(uint256 roundId, uint256 amount)
            external
            canStakeRound(roundId)
        {
            _stake(roundId, amount);
            rounds[roundId].tvl += amount;
            emit selfStake(roundId, msg.sender, amount, true);
        }
        // unstake
        function unstake(uint256 roundId, uint256 amount)
            external
            canUnstakeRound(roundId)
        {
            require(
                stakes[roundId][msg.sender] >= amount,
                "Not enough balance to withdraw"
            );
            rounds[roundId].tvl -= amount;
            _unstake(roundId, amount);
            emit selfStake(roundId, msg.sender, amount, false);
        }
        // stakeUser
        function stakeUsers(
            uint256 roundId,
            address[] calldata users,
            uint256[] calldata amounts
        ) external canStakeRound(roundId) {
            require(users.length == amounts.length, "Unequal users and amount");
            uint256 totalAmount;
            for (uint256 i = 0; i < users.length; i++) {
                address user = users[i];
                uint256 amount = amounts[i];
                require(
                    amount > 0,
                    "You can't stake nothing on a selected address"
                );
                require(address(0) != user, "can't stake the zero address");
                require(user != msg.sender, "You can't stake on your address here");
                xStakes[roundId][getStakeId(msg.sender, user)] += amount;
                totalAmount += amount;
                emit xStake(roundId, msg.sender, user, amount, true);
            }
            require(
                token.transferFrom(msg.sender, address(this), totalAmount),
                "unable to stake users"
            );
            rounds[roundId].tvl += totalAmount;
        }
        // unstakeUser
        function unstakeUsers(uint256 roundId, address[] calldata users)
            external
            canUnstakeRound(roundId)
        {
            uint256 totalAmount = 0;
            for (uint256 i = 0; i < users.length; i++) {
                require(address(0) != users[i], "can't unstake the zero address");
                require(
                    users[i] != msg.sender,
                    "You can't unstake on your address here"
                );
                bytes32 stakeId = getStakeId(msg.sender, users[i]);
                uint256 unstakeBalance = xStakes[roundId][stakeId];
                if (unstakeBalance > 0) {
                    xStakes[roundId][stakeId] -= unstakeBalance;
                    totalAmount += unstakeBalance;
                    emit xStake(
                        roundId,
                        msg.sender,
                        users[i],
                        unstakeBalance,
                        false
                    );
                }
            }
            rounds[roundId].tvl -= totalAmount;
            require(
                token.transfer(msg.sender, totalAmount),
                "unable to unstake users"
            );
        }
        // migrateStake
        function migrateStake(uint256 fromRound)
            external
            canUnstakeRound(fromRound)
        {
            uint256 toRound = latestRound;
            require(fromRound < toRound, "Can't migrate from an active round");
            uint256 balance = stakes[fromRound][msg.sender];
            require(balance > 0, "Not enough balance to migrate");
            rounds[fromRound].tvl -= balance;
            stakes[fromRound][msg.sender] = 0;
            rounds[toRound].tvl += balance;
            stakes[toRound][msg.sender] = balance;
            emit selfStake(fromRound, msg.sender, balance, false);
            emit selfStake(toRound, msg.sender, balance, true);
            emit tokenMigrated(msg.sender, balance, fromRound, toRound);
        }
        // VIEW
        function fetchRoundMeta(uint256 roundId)
            public
            view
            roundExists(roundId)
            returns (
                uint256 start,
                uint256 duration,
                uint256 tvl,
                string memory meta
            )
        {
            return (
                rounds[roundId].start,
                rounds[roundId].duration,
                rounds[roundId].tvl,
                rounds[roundId].meta
            );
        }
        function isActiveRound(uint256 roundId)
            public
            view
            returns (bool isActive)
        {
            (uint256 start, uint256 duration, , ) = fetchRoundMeta(roundId);
            isActive =
                start < block.timestamp &&
                start + duration > block.timestamp;
        }
        function getUserStakeForRound(uint256 roundId, address user)
            public
            view
            roundExists(roundId)
            returns (uint256)
        {
            return _getUserStakeForRound(roundId, user);
        }
        function getUserXStakeForRound(
            uint256 roundId,
            address staker,
            address user
        ) external view returns (uint256) {
            return xStakes[roundId][getStakeId(staker, user)];
        }
        function getStakeId(address staker, address user)
            public
            pure
            returns (bytes32)
        {
            return keccak256(abi.encode(staker, user));
        }
    }
    //SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0 <0.9.0;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    contract Staking {
        IERC20 public token;
        mapping(uint256 => mapping(address => uint256)) public stakes;
        // stake
        function _stake(uint256 roundId, uint256 amount) internal {
            require(
                token.transferFrom(msg.sender, address(this), amount),
                "unable to stake amount"
            );
            stakes[roundId][msg.sender] += amount;
        }
        // unstake
        function _unstake(uint256 roundId, uint256 amount) internal {
            stakes[roundId][msg.sender] -= amount;
            require(token.transfer(msg.sender, amount), "unable to unstake amount");
        }
        function _getUserStakeForRound(uint256 roundId, address user)
            internal
            view
            returns (uint256)
        {
            return stakes[roundId][user];
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)
    pragma solidity ^0.8.0;
    import "./IAccessControl.sol";
    import "../utils/Context.sol";
    import "../utils/Strings.sol";
    import "../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:
     *
     * ```
     * 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}:
     *
     * ```
     * 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.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address => bool) members;
            bytes32 adminRole;
        }
        mapping(bytes32 => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with a standardized message including the required role.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         *
         * _Available since v4.1._
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role, _msgSender());
            _;
        }
        /**
         * @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 override returns (bool) {
            return _roles[role].members[account];
        }
        /**
         * @dev Revert with a standard message if `account` is missing `role`.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         */
        function _checkRole(bytes32 role, address account) internal view {
            if (!hasRole(role, account)) {
                revert(
                    string(
                        abi.encodePacked(
                            "AccessControl: account ",
                            Strings.toHexString(uint160(account), 20),
                            " is missing role ",
                            Strings.toHexString(uint256(role), 32)
                        )
                    )
                );
            }
        }
        /**
         * @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 override 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.
         */
        function grantRole(bytes32 role, address account) public virtual override 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.
         */
        function revokeRole(bytes32 role, address account) public virtual override 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 `account`.
         */
        function renounceRole(bytes32 role, address account) public virtual override {
            require(account == _msgSender(), "AccessControl: can only renounce roles for self");
            _revokeRole(role, account);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event. Note that unlike {grantRole}, this function doesn't perform any
         * checks on the calling account.
         *
         * [WARNING]
         * ====
         * This function should only be called from the constructor when setting
         * up the initial roles for the system.
         *
         * Using this function in any other way is effectively circumventing the admin
         * system imposed by {AccessControl}.
         * ====
         *
         * NOTE: This function is deprecated in favor of {_grantRole}.
         */
        function _setupRole(bytes32 role, address account) internal virtual {
            _grantRole(role, account);
        }
        /**
         * @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 Grants `role` to `account`.
         *
         * Internal function without access restriction.
         */
        function _grantRole(bytes32 role, address account) internal virtual {
            if (!hasRole(role, account)) {
                _roles[role].members[account] = true;
                emit RoleGranted(role, account, _msgSender());
            }
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * Internal function without access restriction.
         */
        function _revokeRole(bytes32 role, address account) internal virtual {
            if (hasRole(role, account)) {
                _roles[role].members[account] = false;
                emit RoleRevoked(role, account, _msgSender());
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) 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 `amount` 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 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
        /**
         * @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);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.0;
    import "../Strings.sol";
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        enum RecoverError {
            NoError,
            InvalidSignature,
            InvalidSignatureLength,
            InvalidSignatureS,
            InvalidSignatureV
        }
        function _throwError(RecoverError error) private pure {
            if (error == RecoverError.NoError) {
                return; // no error: do nothing
            } else if (error == RecoverError.InvalidSignature) {
                revert("ECDSA: invalid signature");
            } else if (error == RecoverError.InvalidSignatureLength) {
                revert("ECDSA: invalid signature length");
            } else if (error == RecoverError.InvalidSignatureS) {
                revert("ECDSA: invalid signature 's' value");
            } else if (error == RecoverError.InvalidSignatureV) {
                revert("ECDSA: invalid signature 'v' value");
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature` or error string. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         *
         * Documentation for signature generation:
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
            // Check the signature length
            // - case 65: r,s,v signature (standard)
            // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
            if (signature.length == 65) {
                bytes32 r;
                bytes32 s;
                uint8 v;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
                return tryRecover(hash, v, r, s);
            } else if (signature.length == 64) {
                bytes32 r;
                bytes32 vs;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    vs := mload(add(signature, 0x40))
                }
                return tryRecover(hash, r, vs);
            } else {
                return (address(0), RecoverError.InvalidSignatureLength);
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, signature);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
         *
         * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address, RecoverError) {
            bytes32 s;
            uint8 v;
            assembly {
                s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                v := add(shr(255, vs), 27)
            }
            return tryRecover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
         *
         * _Available since v4.2._
         */
        function recover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, r, vs);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address, RecoverError) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return (address(0), RecoverError.InvalidSignatureS);
            }
            if (v != 27 && v != 28) {
                return (address(0), RecoverError.InvalidSignatureV);
            }
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            if (signer == address(0)) {
                return (address(0), RecoverError.InvalidSignature);
            }
            return (signer, RecoverError.NoError);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function recover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash));
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from `s`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    ", Strings.toString(s.length), s));
        }
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
    pragma solidity ^0.8.0;
    import "./ECDSA.sol";
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
     *
     * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
     * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
     * they need in their contracts using a combination of `abi.encode` and `keccak256`.
     *
     * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
     * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
     * ({_hashTypedDataV4}).
     *
     * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
     * the chain id to protect against replay attacks on an eventual fork of the chain.
     *
     * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
     * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
     *
     * _Available since v3.4._
     */
    abstract contract EIP712 {
        /* solhint-disable var-name-mixedcase */
        // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
        // invalidate the cached domain separator if the chain id changes.
        bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
        uint256 private immutable _CACHED_CHAIN_ID;
        address private immutable _CACHED_THIS;
        bytes32 private immutable _HASHED_NAME;
        bytes32 private immutable _HASHED_VERSION;
        bytes32 private immutable _TYPE_HASH;
        /* solhint-enable var-name-mixedcase */
        /**
         * @dev Initializes the domain separator and parameter caches.
         *
         * The meaning of `name` and `version` is specified in
         * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
         *
         * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
         * - `version`: the current major version of the signing domain.
         *
         * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
         * contract upgrade].
         */
        constructor(string memory name, string memory version) {
            bytes32 hashedName = keccak256(bytes(name));
            bytes32 hashedVersion = keccak256(bytes(version));
            bytes32 typeHash = keccak256(
                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
            );
            _HASHED_NAME = hashedName;
            _HASHED_VERSION = hashedVersion;
            _CACHED_CHAIN_ID = block.chainid;
            _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
            _CACHED_THIS = address(this);
            _TYPE_HASH = typeHash;
        }
        /**
         * @dev Returns the domain separator for the current chain.
         */
        function _domainSeparatorV4() internal view returns (bytes32) {
            if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                return _CACHED_DOMAIN_SEPARATOR;
            } else {
                return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
            }
        }
        function _buildDomainSeparator(
            bytes32 typeHash,
            bytes32 nameHash,
            bytes32 versionHash
        ) private view returns (bytes32) {
            return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
        }
        /**
         * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
         * function returns the hash of the fully encoded EIP712 message for this domain.
         *
         * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
         *
         * ```solidity
         * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
         *     keccak256("Mail(address to,string contents)"),
         *     mailTo,
         *     keccak256(bytes(mailContents))
         * )));
         * address signer = ECDSA.recover(digest, signature);
         * ```
         */
        function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
            return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @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 signaling this.
         *
         * _Available since v3.1._
         */
        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, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        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 `account`.
         */
        function renounceRole(bytes32 role, address account) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @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;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0x00";
            }
            uint256 temp = value;
            uint256 length = 0;
            while (temp != 0) {
                length++;
                temp >>= 8;
            }
            return toHexString(value, length);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 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);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * 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[EIP 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);
    }
    

    File 2 of 2: GTC
    // SPDX-License-Identifier: NONE
    
    pragma solidity 0.6.12;
    pragma experimental ABIEncoderV2;
    
    
    
    // Part: SafeMath
    
    // Subject to the MIT license.
    
    /**
     * @dev Wrappers over Solidity's arithmetic operations with added overflow
     * checks.
     *
     * Arithmetic operations in Solidity wrap on overflow. This can easily result
     * in bugs, because programmers usually assume that an overflow raises an
     * error, which is the standard behavior in high level programming languages.
     * `SafeMath` restores this intuition by reverting the transaction when an
     * operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, reverting on overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
    
            return c;
        }
    
        /**
         * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, errorMessage);
    
            return c;
        }
    
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         * - Subtraction cannot underflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return sub(a, b, "SafeMath: subtraction underflow");
        }
    
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         * - Subtraction cannot underflow.
         */
        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            uint256 c = a - b;
    
            return c;
        }
    
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
    
            return c;
        }
    
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b, errorMessage);
    
            return c;
        }
    
        /**
         * @dev Returns the integer division of two unsigned integers.
         * Reverts on division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return div(a, b, "SafeMath: division by zero");
        }
    
        /**
         * @dev Returns the integer division of two unsigned integers.
         * Reverts with custom message on division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            // Solidity only automatically asserts when dividing by 0
            require(b > 0, errorMessage);
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
            return c;
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * Reverts when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return mod(a, b, "SafeMath: modulo by zero");
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * Reverts with custom message when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b != 0, errorMessage);
            return a % b;
        }
    }
    
    // File: GTC.sol
    
    contract GTC {
        /// @notice EIP-20 token name for this token
        string public constant name = "Gitcoin";
    
        /// @notice EIP-20 token symbol for this token
        string public constant symbol = "GTC";
    
        /// @notice EIP-20 token decimals for this token
        uint8 public constant decimals = 18;
    
        /// @notice Total number of tokens in circulation
        uint public totalSupply = 100_000_000e18; // 100 million GTC
    
        /// @notice Address which may mint new tokens
        address public minter;
    
        /// @notice Address of the GTCDistribution contract 
        address public GTCDist;
    
        /// @notice The timestamp after which minting may occur
        uint public mintingAllowedAfter;
    
        /// @notice Minimum time between mints
        uint32 public constant minimumTimeBetweenMints = 1 days * 365;
    
        /// @notice Cap on the percentage of totalSupply that can be minted at each mint
        uint8 public constant mintCap = 2;
    
        /// @notice Allowance amounts on behalf of others
        mapping (address => mapping (address => uint96)) internal allowances;
    
        /// @notice Official record of token balances for each account
        mapping (address => uint96) internal balances;
    
        /// @notice A record of each accounts delegate
        mapping (address => address) public delegates;
    
        /// @notice A checkpoint for marking number of votes from a given block
        struct Checkpoint {
            uint32 fromBlock;
            uint96 votes;
        }
    
        /// @notice A record of votes checkpoints for each account, by index
        mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
    
        /// @notice The number of checkpoints for each account
        mapping (address => uint32) public numCheckpoints;
    
        /// @notice The EIP-712 typehash for the contract's domain
        bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
    
        /// @notice The EIP-712 typehash for the delegation struct used by the contract
        bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
    
        /// @notice The EIP-712 typehash for the permit struct used by the contract
        bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    
        /// @notice A record of states for signing / validating signatures
        mapping (address => uint) public nonces;
    
        /// @notice An event thats emitted when the minter address is changed
        event MinterChanged(address minter, address newMinter);
    
        /// @notice An event thats emitted when the minter address is changed
        event GTCDistChanged(address delegator, address delegatee);
    
        /// @notice An event thats emitted when an account changes its delegate
        event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
    
        /// @notice An event thats emitted when a delegate account's vote balance changes
        event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
    
        /// @notice The standard EIP-20 transfer event
        event Transfer(address indexed from, address indexed to, uint256 amount);
    
        /// @notice The standard EIP-20 approval event
        event Approval(address indexed owner, address indexed spender, uint256 amount);
    
        /**
         * @notice Construct a new GTC token
         * @param account The initial account to grant all the tokens
         * @param minter_ The account with minting ability
         * @param mintingAllowedAfter_ The timestamp after which minting may occur
         */
        constructor(address account, address minter_, uint mintingAllowedAfter_) public {
            require(mintingAllowedAfter_ >= block.timestamp, "GTC::constructor: minting can only begin after deployment");
            balances[account] = uint96(totalSupply);
            emit Transfer(address(0), account, totalSupply);
            minter = minter_;
            emit MinterChanged(address(0), minter);
            mintingAllowedAfter = mintingAllowedAfter_;
        }
    
        /**
         * @notice Change the minter address
         * @param minter_ The address of the new minter
         */
        function setMinter(address minter_) external {
            require(msg.sender == minter, "GTC::setMinter: only the minter can change the minter address");
            emit MinterChanged(minter, minter_);
            minter = minter_;
        }
    
        /**
         * @notice Change/set TokenDistribution address, needs to be called after GTCToken contract is deployed 
         * @param GTCDist_ The address of the TokenDistributor contract
         */
        function setGTCDist(address GTCDist_) external {
            require(msg.sender == minter, "GTC::setGTCDist: only the minter can change the GTCDist address");
            emit GTCDistChanged(GTCDist, GTCDist_);
            GTCDist = GTCDist_;
        }
    
        /**
         * @notice Mint new tokens
         * @param dst The address of the destination account
         * @param rawAmount The number of tokens to be minted
         */
        function mint(address dst, uint rawAmount) external {
            require(msg.sender == minter, "GTC::mint: only the minter can mint");
            require(block.timestamp >= mintingAllowedAfter, "GTC::mint: minting not allowed yet");
            require(dst != address(0), "GTC::mint: cannot transfer to the zero address");
    
            // record the mint
            mintingAllowedAfter = SafeMath.add(block.timestamp, minimumTimeBetweenMints);
    
            // mint the amount
            uint96 amount = safe96(rawAmount, "GTC::mint: amount exceeds 96 bits");
            require(amount <= SafeMath.div(SafeMath.mul(totalSupply, mintCap), 100), "GTC::mint: exceeded mint cap");
            totalSupply = safe96(SafeMath.add(totalSupply, amount), "GTC::mint: totalSupply exceeds 96 bits");
    
            // transfer the amount to the recipient
            balances[dst] = add96(balances[dst], amount, "GTC::mint: transfer amount overflows");
            emit Transfer(address(0), dst, amount);
    
            // move delegates
            _moveDelegates(address(0), delegates[dst], amount);
        }
    
        /**
         * @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
         * @param account The address of the account holding the funds
         * @param spender The address of the account spending the funds
         * @return The number of tokens approved
         */
        function allowance(address account, address spender) external view returns (uint) {
            return allowances[account][spender];
        }
    
        /**
         * @notice Approve `spender` to transfer up to `amount` from `src`
         * @dev This will overwrite the approval amount for `spender`
         *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
         * @param spender The address of the account which may transfer tokens
         * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
         * @return Whether or not the approval succeeded
         */
        function approve(address spender, uint rawAmount) external returns (bool) {
            uint96 amount;
            if (rawAmount == uint(-1)) {
                amount = uint96(-1);
            } else {
                amount = safe96(rawAmount, "GTC::approve: amount exceeds 96 bits");
            }
    
            allowances[msg.sender][spender] = amount;
    
            emit Approval(msg.sender, spender, amount);
            return true;
        }
    
        /**
         * @notice Triggers an approval from owner to spends
         * @param owner The address to approve from
         * @param spender The address to be approved
         * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
         * @param deadline The time at which to expire the signature
         * @param v The recovery byte of the signature
         * @param r Half of the ECDSA signature pair
         * @param s Half of the ECDSA signature pair
         */
        function permit(address owner, address spender, uint rawAmount, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
            uint96 amount;
            if (rawAmount == uint(-1)) {
                amount = uint96(-1);
            } else {
                amount = safe96(rawAmount, "GTC::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), "GTC::permit: invalid signature");
            require(signatory == owner, "GTC::permit: unauthorized");
            require(now <= deadline, "GTC::permit: signature expired");
    
            allowances[owner][spender] = amount;
    
            emit Approval(owner, spender, amount);
        }
    
        /**
         * @notice Get the number of tokens held by the `account`
         * @param account The address of the account to get the balance of
         * @return The number of tokens held
         */
        function balanceOf(address account) external view returns (uint) {
            return balances[account];
        }
    
        /**
         * @notice Transfer `amount` tokens from `msg.sender` to `dst`
         * @param dst The address of the destination account
         * @param rawAmount The number of tokens to transfer
         * @return Whether or not the transfer succeeded
         */
        function transfer(address dst, uint rawAmount) external returns (bool) {
            uint96 amount = safe96(rawAmount, "GTC::transfer: amount exceeds 96 bits");
            _transferTokens(msg.sender, dst, amount);
            return true;
        }
    
        /**
         * @notice Transfer `amount` tokens from `src` to `dst`
         * @param src The address of the source account
         * @param dst The address of the destination account
         * @param rawAmount The number of tokens to transfer
         * @return Whether or not the transfer succeeded
         */
        function transferFrom(address src, address dst, uint rawAmount) external returns (bool) {
            address spender = msg.sender;
            uint96 spenderAllowance = allowances[src][spender];
            uint96 amount = safe96(rawAmount, "GTC::approve: amount exceeds 96 bits");
    
            if (spender != src && spenderAllowance != uint96(-1)) {
                uint96 newAllowance = sub96(spenderAllowance, amount, "GTC::transferFrom: transfer amount exceeds spender allowance");
                allowances[src][spender] = newAllowance;
    
                emit Approval(src, spender, newAllowance);
            }
    
            _transferTokens(src, dst, amount);
            return true;
        }
    
        /**
         * @notice Delegate votes from `msg.sender` to `delegatee`
         * @param delegatee The address to delegate votes to
         */
        function delegate(address delegatee) external {
            return _delegate(msg.sender, delegatee);
        }
    
        /**
         * @notice Delegate votes from `delegator` to `delegatee` 
         * @param delegator The address to delegate votes from 
         * @param delegatee The address to delegate votes to
         */
        function delegateOnDist(address delegator, address delegatee) external {
            require(msg.sender == GTCDist, "Sender not authorized");
            return _delegate(delegator, delegatee);
        }
    
        /**
         * @notice Delegates votes from signatory to `delegatee`
         * @param delegatee The address to delegate votes to
         * @param nonce The contract state required to match the signature
         * @param expiry The time at which to expire the signature
         * @param v The recovery byte of the signature
         * @param r Half of the ECDSA signature pair
         * @param s Half of the ECDSA signature pair
         */
        function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) external {
            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), "GTC::delegateBySig: invalid signature");
            require(nonce == nonces[signatory]++, "GTC::delegateBySig: invalid nonce");
            require(now <= expiry, "GTC::delegateBySig: signature expired");
            return _delegate(signatory, delegatee);
        }
    
        /**
         * @notice Gets the current votes balance for `account`
         * @param account The address to get votes balance
         * @return The number of current votes for `account`
         */
        function getCurrentVotes(address account) external view returns (uint96) {
            uint32 nCheckpoints = numCheckpoints[account];
            return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
        }
    
        /**
         * @notice Determine the prior number of votes for an account as of a block number
         * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
         * @param account The address of the account to check
         * @param blockNumber The block number to get the vote balance at
         * @return The number of votes the account had as of the given block
         */
        function getPriorVotes(address account, uint blockNumber) external view returns (uint96) {
            require(blockNumber < block.number, "GTC::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 = balances[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), "GTC::_transferTokens: cannot transfer from the zero address");
            require(dst != address(0), "GTC::_transferTokens: cannot transfer to the zero address");
    
            balances[src] = sub96(balances[src], amount, "GTC::_transferTokens: transfer amount exceeds balance");
            balances[dst] = add96(balances[dst], amount, "GTC::_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, "GTC::_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, "GTC::_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, "GTC::_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 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 pure returns (uint) {
            uint256 chainId;
            assembly { chainId := chainid() }
            return chainId;
        }
    
    }