ETH Price: $2,537.39 (+1.67%)

Transaction Decoder

Block:
12320789 at Apr-27-2021 06:34:35 AM +UTC
Transaction Fee:
0.002764152112024938 ETH $7.01
Gas Used:
76,782 Gas / 36.000001459 Gwei

Emitted Events:

Account State Difference:

  Address   Before After State Difference Code
(AntPool 2)
1,248.726190102565927688 Eth1,248.728954254677952626 Eth0.002764152112024938
0xc370D2a5...61434CBDD
0.15530668275527643 Eth
Nonce: 316
0.152542530643251492 Eth
Nonce: 317
0.002764152112024938
0xc7283b66...b0720212B

Execution Trace

Tribe.delegateBySig( delegatee=0xe0ac4559739bD36f0913FB0A3f5bFC19BCBaCD52, nonce=0, expiry=10000000000, v=28, r=B60F402BF1DFC0C207E91F75257EB86AAD29068ADD7F952EEA7EDB36593B86AB, s=0D11715C9B2D48ADA585285792F46648D30247DF6883434D23872530E4198639 )
  • Null: 0x000...001.60712b65( )
    delegateBySig[Tribe (ln:201)]
    pragma solidity ^0.6.0;
    pragma experimental ABIEncoderV2;
    // Forked from Uniswap's UNI
    // Reference: https://etherscan.io/address/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984#code
    contract Tribe {
        /// @notice EIP-20 token name for this token
        // solhint-disable-next-line const-name-snakecase
        string public constant name = "Tribe";
        /// @notice EIP-20 token symbol for this token
        // solhint-disable-next-line const-name-snakecase
        string public constant symbol = "TRIBE";
        /// @notice EIP-20 token decimals for this token
        // solhint-disable-next-line const-name-snakecase
        uint8 public constant decimals = 18;
        /// @notice Total number of tokens in circulation
        // solhint-disable-next-line const-name-snakecase
        uint public totalSupply = 1_000_000_000e18; // 1 billion Tribe
        /// @notice Address which may mint new tokens
        address public minter;
        /// @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 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 Tribe token
         * @param account The initial account to grant all the tokens
         * @param minter_ The account with minting ability
         */
        constructor(address account, address minter_) public {
            balances[account] = uint96(totalSupply);
            emit Transfer(address(0), account, totalSupply);
            minter = minter_;
            emit MinterChanged(address(0), minter);
        }
        /**
         * @notice Change the minter address
         * @param minter_ The address of the new minter
         */
        function setMinter(address minter_) external {
            require(msg.sender == minter, "Tribe: only the minter can change the minter address");
            emit MinterChanged(minter, minter_);
            minter = minter_;
        }
        /**
         * @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, "Tribe: only the minter can mint");
            require(dst != address(0), "Tribe: cannot transfer to the zero address");
            // mint the amount
            uint96 amount = safe96(rawAmount, "Tribe: amount exceeds 96 bits");
            uint96 safeSupply = safe96(totalSupply, "Tribe: totalSupply exceeds 96 bits");
            totalSupply = add96(safeSupply, amount, "Tribe: totalSupply exceeds 96 bits");
            // transfer the amount to the recipient
            balances[dst] = add96(balances[dst], amount, "Tribe: 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, "Tribe: 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, "Tribe: 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), "Tribe: invalid signature");
            require(signatory == owner, "Tribe: unauthorized");
            // solhint-disable-next-line not-rely-on-time
            require(block.timestamp <= deadline, "Tribe: 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, "Tribe: 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, "Tribe: amount exceeds 96 bits");
            if (spender != src && spenderAllowance != uint96(-1)) {
                uint96 newAllowance = sub96(spenderAllowance, amount, "Tribe: 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) public {
            return _delegate(msg.sender, 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) public {
            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), "Tribe: invalid signature");
            require(nonce == nonces[signatory]++, "Tribe: invalid nonce");
            // solhint-disable-next-line not-rely-on-time
            require(block.timestamp <= expiry, "Tribe: 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) public view returns (uint96) {
            require(blockNumber < block.number, "Tribe: 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), "Tribe: cannot transfer from the zero address");
            require(dst != address(0), "Tribe: cannot transfer to the zero address");
            balances[src] = sub96(balances[src], amount, "Tribe: transfer amount exceeds balance");
            balances[dst] = add96(balances[dst], amount, "Tribe: 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, "Tribe: 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, "Tribe: vote amount overflows");
                    _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
                }
            }
        }
        function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes) internal {
          uint32 blockNumber = safe32(block.number, "Tribe: 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;
            // solhint-disable-next-line no-inline-assembly
            assembly { chainId := chainid() }
            return chainId;
        }
    }