ETH Price: $2,296.36 (-4.79%)

Transaction Decoder

Block:
20223250 at Jul-03-2024 03:18:35 AM +UTC
Transaction Fee:
0.000415136428101603 ETH $0.95
Gas Used:
70,863 Gas / 5.858295981 Gwei

Emitted Events:

488 GovernorBeta.VoteCast( voter=[Sender] 0xf71e9c766cdf169edfbe2749490943c1dc6b8a55, proposalId=23, support=True, votes=260738826671600440187125 )

Account State Difference:

  Address   Before After State Difference Code
0x874C5D59...79F196d5b
(beaverbuild)
9.936300549745488786 Eth9.936371412745488786 Eth0.000070863
0xf71E9C76...1DC6b8A55
0.850571212552316939 Eth
Nonce: 518
0.850156076124215336 Eth
Nonce: 519
0.000415136428101603

Execution Trace

GovernorBeta.castVote( proposalId=23, support=True )
  • Ctx.getPriorVotes( account=0xf71E9C766Cdf169eDFbE2749490943C1DC6b8A55, blockNumber=20210575 ) => ( 260738826671600440187125 )
    File 1 of 2: GovernorBeta
    // SPDX-License-Identifier: MIT
    pragma solidity 0.7.5;
    pragma experimental ABIEncoderV2;
    contract GovernorBeta {
      /// @notice The name of this contract
      string public constant name = "Cryptex Governor Beta";
      /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed
      function quorumVotes() public pure returns (uint256) {
        return 400_000e18;
      } // 4% of Ctx
      /// @notice The number of votes required in order for a voter to become a proposer
      function proposalThreshold() public pure returns (uint256) {
        return 100_000e18;
      } // 1% of Ctx
      /// @notice The maximum number of actions that can be included in a proposal
      function proposalMaxOperations() public pure returns (uint256) {
        return 10;
      } // 10 actions
      /// @notice The delay before voting on a proposal may take place, once proposed
      function votingDelay() public pure returns (uint256) {
        return 1;
      } // 1 block
      /// @notice The duration of voting on a proposal, in blocks
      function votingPeriod() public pure returns (uint256) {
        return 17_280;
      } // ~3 days in blocks (assuming 15s blocks)
      /// @notice The address of the Ctx Protocol Timelock
      TimelockInterface public timelock;
      /// @notice The address of the Ctx governance token
      CtxInterface public ctx;
      /// @notice The total number of proposals
      uint256 public proposalCount;
      /// @notice Guardian of the governor
      address public guardian;
      /// @param id Unique id for looking up a proposal
      /// @param proposer Creator of the proposal
      /// @param eta The timestamp that the proposal will be available for execution, set once the vote succeeds
      /// @param targets the ordered list of target addresses for calls to be made
      /// @param values The ordered list of values (i.e. msg.value) to be passed to the calls to be made
      /// @param signatures The ordered list of function signatures to be called
      /// @param calldatas The ordered list of calldata to be passed to each call
      /// @param startBlock The block at which voting begins: holders must delegate their votes prior to this block
      /// @param endBlock The block at which voting ends: votes must be cast prior to this block
      /// @param forVotes Current number of votes in favor of this proposal
      /// @param againstVotes Current number of votes in opposition to this proposal
      /// @param canceled Flag marking whether the proposal has been canceled
      /// @param executed Flag marking whether the proposal has been executed
      struct Proposal {
        uint256 id;
        address proposer;
        uint256 eta;
        address[] targets;
        uint256[] values;
        string[] signatures;
        bytes[] calldatas;
        uint256 startBlock;
        uint256 endBlock;
        uint256 forVotes;
        uint256 againstVotes;
        bool canceled;
        bool executed;
      }
      /// @notice Receipts of ballots for the entire set of voters
      mapping(uint256 => mapping(address => Receipt)) public receipts;
      /// @notice Ballot receipt record for a voter
      /// @param hasVoted or not a vote has been cast
      /// @param support or not the voter supports the proposal
      /// @param votes number of votes the voter had, which were cast
      struct Receipt {
        bool hasVoted;
        bool support;
        uint96 votes;
      }
      /// @notice Possible states that a proposal may be in
      enum ProposalState {
        Pending,
        Active,
        Canceled,
        Defeated,
        Succeeded,
        Queued,
        Expired,
        Executed
      }
      /// @notice The official record of all proposals ever proposed
      mapping(uint256 => Proposal) public proposals;
      /// @notice The latest proposal for each proposer
      mapping(address => uint256) public latestProposalIds;
      /// @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 ballot struct used by the contract
      bytes32 public constant BALLOT_TYPEHASH =
        keccak256("Ballot(uint256 proposalId,bool support)");
      /// @notice An event emitted when a new proposal is created
      event ProposalCreated(
        uint256 id,
        address proposer,
        address[] targets,
        uint256[] values,
        string[] signatures,
        bytes[] calldatas,
        uint256 startBlock,
        uint256 endBlock,
        string description
      );
      /// @notice An event emitted when a vote has been cast on a proposal
      event VoteCast(
        address voter,
        uint256 proposalId,
        bool support,
        uint256 votes
      );
      /// @notice An event emitted when a proposal has been canceled
      event ProposalCanceled(uint256 id);
      /// @notice An event emitted when a proposal has been queued in the Timelock
      event ProposalQueued(uint256 id, uint256 eta);
      /// @notice An event emitted when a proposal has been executed in the Timelock
      event ProposalExecuted(uint256 id);
      constructor(
        address timelock_,
        address ctx_,
        address guardian_
      ) {
        timelock = TimelockInterface(timelock_);
        ctx = CtxInterface(ctx_);
        guardian = guardian_;
      }
      function propose(
        address[] memory targets,
        uint256[] memory values,
        string[] memory signatures,
        bytes[] memory calldatas,
        string memory description
      ) public returns (uint256) {
        require(
          ctx.getPriorVotes(msg.sender, sub256(block.number, 1)) >
            proposalThreshold(),
          "GovernorBeta::propose: proposer votes below proposal threshold"
        );
        require(
          targets.length == values.length &&
            targets.length == signatures.length &&
            targets.length == calldatas.length,
          "GovernorBeta::propose: proposal function information arity mismatch"
        );
        require(targets.length != 0, "GovernorBeta::propose: must provide actions");
        require(
          targets.length <= proposalMaxOperations(),
          "GovernorBeta::propose: too many actions"
        );
        uint256 latestProposalId = latestProposalIds[msg.sender];
        if (latestProposalId != 0) {
          ProposalState proposersLatestProposalState = state(latestProposalId);
          require(
            proposersLatestProposalState != ProposalState.Active,
            "GovernorBeta::propose: one live proposal per proposer, found an already active proposal"
          );
          require(
            proposersLatestProposalState != ProposalState.Pending,
            "GovernorBeta::propose: one live proposal per proposer, found an already pending proposal"
          );
        }
        uint256 startBlock = add256(block.number, votingDelay());
        uint256 endBlock = add256(startBlock, votingPeriod());
        proposalCount++;
        Proposal memory newProposal =
          Proposal({
            id: proposalCount,
            proposer: msg.sender,
            eta: 0,
            targets: targets,
            values: values,
            signatures: signatures,
            calldatas: calldatas,
            startBlock: startBlock,
            endBlock: endBlock,
            forVotes: 0,
            againstVotes: 0,
            canceled: false,
            executed: false
          });
        proposals[newProposal.id] = newProposal;
        latestProposalIds[newProposal.proposer] = newProposal.id;
        emit ProposalCreated(
          newProposal.id,
          msg.sender,
          targets,
          values,
          signatures,
          calldatas,
          startBlock,
          endBlock,
          description
        );
        return newProposal.id;
      }
      function queue(uint256 proposalId) public {
        require(
          state(proposalId) == ProposalState.Succeeded,
          "GovernorBeta::queue: proposal can only be queued if it is succeeded"
        );
        Proposal storage proposal = proposals[proposalId];
        uint256 eta = add256(block.timestamp, timelock.delay());
        for (uint256 i = 0; i < proposal.targets.length; i++) {
          _queueOrRevert(
            proposal.targets[i],
            proposal.values[i],
            proposal.signatures[i],
            proposal.calldatas[i],
            eta
          );
        }
        proposal.eta = eta;
        emit ProposalQueued(proposalId, eta);
      }
      function _queueOrRevert(
        address target,
        uint256 value,
        string memory signature,
        bytes memory data,
        uint256 eta
      ) internal {
        require(
          !timelock.queuedTransactions(
            keccak256(abi.encode(target, value, signature, data, eta))
          ),
          "GovernorBeta::_queueOrRevert: proposal action already queued at eta"
        );
        timelock.queueTransaction(target, value, signature, data, eta);
      }
      /// @notice executes the transaction, but uses the msg.value from the eth stored in the timelock
      function execute(uint256 proposalId) public {
        require(
          state(proposalId) == ProposalState.Queued,
          "GovernorBeta::execute: proposal can only be executed if it is queued"
        );
        Proposal storage proposal = proposals[proposalId];
        proposal.executed = true;
        for (uint256 i = 0; i < proposal.targets.length; i++) {
          timelock.executeTransaction{value: 0}(
            proposal.targets[i],
            proposal.values[i],
            proposal.signatures[i],
            proposal.calldatas[i],
            proposal.eta
          );
        }
        emit ProposalExecuted(proposalId);
      }
      function cancel(uint256 proposalId) public {
        ProposalState currentState = state(proposalId);
        require(
          currentState != ProposalState.Executed,
          "GovernorBeta::cancel: cannot cancel executed proposal"
        );
        Proposal storage proposal = proposals[proposalId];
        require(
          ctx.getPriorVotes(proposal.proposer, sub256(block.number, 1)) <
            proposalThreshold(),
          "GovernorBeta::cancel: proposer above threshold"
        );
        proposal.canceled = true;
        for (uint256 i = 0; i < proposal.targets.length; i++) {
          timelock.cancelTransaction(
            proposal.targets[i],
            proposal.values[i],
            proposal.signatures[i],
            proposal.calldatas[i],
            proposal.eta
          );
        }
        emit ProposalCanceled(proposalId);
      }
      function getActions(uint256 proposalId)
        public
        view
        returns (
          address[] memory targets,
          uint256[] memory values,
          string[] memory signatures,
          bytes[] memory calldatas
        )
      {
        Proposal storage p = proposals[proposalId];
        return (p.targets, p.values, p.signatures, p.calldatas);
      }
      function getReceipt(uint256 proposalId, address voter)
        public
        view
        returns (Receipt memory)
      {
        require(
          proposalCount >= proposalId && proposalId > 0,
          "GovernorBeta::getReceipt: invalid proposal id"
        );
        return receipts[proposalId][voter];
      }
      function state(uint256 proposalId) public view returns (ProposalState) {
        require(
          proposalCount >= proposalId && proposalId > 0,
          "GovernorBeta::state: invalid proposal id"
        );
        Proposal storage proposal = proposals[proposalId];
        if (proposal.canceled) {
          return ProposalState.Canceled;
        } else if (block.number <= proposal.startBlock) {
          return ProposalState.Pending;
        } else if (block.number <= proposal.endBlock) {
          return ProposalState.Active;
        } else if (
          proposal.forVotes <= proposal.againstVotes ||
          proposal.forVotes < quorumVotes()
        ) {
          return ProposalState.Defeated;
        } else if (proposal.eta == 0) {
          return ProposalState.Succeeded;
        } else if (proposal.executed) {
          return ProposalState.Executed;
        } else if (
          block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())
        ) {
          return ProposalState.Expired;
        } else {
          return ProposalState.Queued;
        }
      }
      function castVote(uint256 proposalId, bool support) public {
        return _castVote(msg.sender, proposalId, support);
      }
      function castVoteBySig(
        uint256 proposalId,
        bool support,
        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(BALLOT_TYPEHASH, proposalId, support));
        bytes32 digest =
          keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(
          signatory != address(0),
          "GovernorBeta::castVoteBySig: invalid signature"
        );
        return _castVote(signatory, proposalId, support);
      }
      function _castVote(
        address voter,
        uint256 proposalId,
        bool support
      ) internal {
        require(
          state(proposalId) == ProposalState.Active,
          "GovernorBeta::_castVote: voting is closed"
        );
        Proposal storage proposal = proposals[proposalId];
        Receipt storage receipt = receipts[proposalId][voter];
        require(
          receipt.hasVoted == false,
          "GovernorBeta::_castVote: voter already voted"
        );
        uint96 votes = ctx.getPriorVotes(voter, proposal.startBlock);
        if (support) {
          proposal.forVotes = add256(proposal.forVotes, votes);
        } else {
          proposal.againstVotes = add256(proposal.againstVotes, votes);
        }
        receipt.hasVoted = true;
        receipt.support = support;
        receipt.votes = votes;
        emit VoteCast(voter, proposalId, support, votes);
      }
      function acceptTimelockAdmin() external {
        require(
          msg.sender == guardian,
          "GovernorBeta::acceptTimelockAdmin: only guardian can call this function"
        );
        timelock.acceptAdmin();
      }
      function add256(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "addition overflow");
        return c;
      }
      function sub256(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "subtraction underflow");
        return a - b;
      }
      function getChainId() internal pure returns (uint256) {
        uint256 chainId;
        assembly {
          chainId := chainid()
        }
        return chainId;
      }
    }
    interface TimelockInterface {
      function delay() external view returns (uint256);
      function GRACE_PERIOD() external view returns (uint256);
      function acceptAdmin() external;
      function queuedTransactions(bytes32 hash) external view returns (bool);
      function queueTransaction(
        address target,
        uint256 value,
        string calldata signature,
        bytes calldata data,
        uint256 eta
      ) external returns (bytes32);
      function cancelTransaction(
        address target,
        uint256 value,
        string calldata signature,
        bytes calldata data,
        uint256 eta
      ) external;
      function executeTransaction(
        address target,
        uint256 value,
        string calldata signature,
        bytes calldata data,
        uint256 eta
      ) external payable returns (bytes memory);
    }
    interface CtxInterface {
      function getPriorVotes(address account, uint256 blockNumber)
        external
        view
        returns (uint96);
    }
    

    File 2 of 2: Ctx
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    /**
     * @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 subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return sub(a, b, "SafeMath: subtraction overflow");
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        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 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) {
            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;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.7.5;
    pragma experimental ABIEncoderV2;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    contract Ctx {
      /// @notice EIP-20 token name for this token
      string public constant name = "Cryptex";
      /// @notice EIP-20 token symbol for this token
      string public constant symbol = "CTX";
      /// @notice EIP-20 token decimals for this token
      uint8 public constant decimals = 18;
      /// @notice Total number of tokens in circulation
      uint256 public totalSupply = 10000000e18; // 10 million CTX
      /// @notice Address which may mint new tokens
      address public minter;
      /// @notice The timestamp after which minting may occur
      uint256 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;
      /// @dev Allowance amounts on behalf of others
      mapping(address => mapping(address => uint96)) internal allowances;
      /// @dev 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 => uint256) 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,
        uint256 previousBalance,
        uint256 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 Ctx 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_,
        uint256 mintingAllowedAfter_
      ) {
        require(
          mintingAllowedAfter_ >= block.timestamp,
          "Ctx::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,
          "Ctx::setMinter: 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, uint256 rawAmount) external {
        require(msg.sender == minter, "Ctx::mint: only the minter can mint");
        require(
          block.timestamp >= mintingAllowedAfter,
          "Ctx::mint: minting not allowed yet"
        );
        require(
          dst != address(0),
          "Ctx::mint: cannot transfer to the zero address"
        );
        require(
          dst != address(this),
          "Ctx::mint: cannot transfer to the Ctx address"
        );
        // record the mint
        mintingAllowedAfter = SafeMath.add(
          block.timestamp,
          minimumTimeBetweenMints
        );
        // mint the amount
        uint96 amount = safe96(rawAmount, "Ctx::mint: amount exceeds 96 bits");
        require(
          amount <= SafeMath.div(SafeMath.mul(totalSupply, mintCap), 100),
          "Ctx::mint: exceeded mint cap"
        );
        totalSupply = safe96(
          SafeMath.add(totalSupply, amount),
          "Ctx::mint: totalSupply exceeds 96 bits"
        );
        // transfer the amount to the recipient
        balances[dst] = add96(
          balances[dst],
          amount,
          "Ctx::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 (uint256)
      {
        return allowances[account][spender];
      }
      /**
       * @notice Sets `amount` as the allowance of `spender` over the `owner` s tokens.
       * @param owner The address of the caller.
       * @param spender The address of the account which may transfer tokens
       * @param amount The number of tokens that are approved
       * @dev This internal function is equivalent to `approve`, and can be used to
       * e.g. set automatic allowances for certain subsystems, etc.
       * @dev Emits an Approval event.
       * @dev owner cannot be the zero address.
       * @dev spender cannot be the zero address.
       */
      function _approve(
        address owner,
        address spender,
        uint96 amount
      ) internal virtual {
        require(
          owner != address(0),
          "Ctx::_approve: approve from the zero address"
        );
        require(
          spender != address(0),
          "Ctx::_approve: approve to the zero address"
        );
        allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
      }
      /**
       * @notice Approve `spender` to transfer up to `amount` from `src`
       * @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)
       * @dev This will overwrite the approval amount for `spender`
       *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
       * @return Whether or not the approval succeeded
       */
      function approve(address spender, uint256 rawAmount) external returns (bool) {
        uint96 amount;
        if (rawAmount == uint256(-1)) {
          amount = uint96(-1);
        } else {
          amount = safe96(rawAmount, "Ctx::approve: amount exceeds 96 bits");
        }
        _approve(msg.sender, spender, amount);
        return true;
      }
      /**
       * @notice Atomically increases the allowance granted to `spender` by the caller.
       * @param spender address
       * @param addedValue uint256 raw
       * @dev This is an alternative to {approve} that can be used as a mitigation for
       * problems of Allowance Double-Spend Exploit.
       * @dev Emits Approval event indicating the updated allowance.
       * @dev spender cannot be the zero address.
       */
      function increaseAllowance(address spender, uint256 addedValue)
        public
        virtual
        returns (bool)
      {
        uint96 amount;
        if (addedValue == uint256(-1)) {
          amount = uint96(-1);
        } else {
          amount = safe96(
            addedValue,
            "Ctx::increaseAllowance: amount exceeds 96 bits"
          );
        }
        _approve(
          msg.sender,
          spender,
          add96(
            allowances[msg.sender][spender],
            amount,
            "Ctx::increaseAllowance: transfer amount overflows"
          )
        );
        return true;
      }
      /**
       * @notice Atomically decreases the allowance granted to `spender` by the caller.
       * @param spender address
       * @param subtractedValue uint256 raw
       * @dev This is an alternative to {approve} that can be used as a mitigation for
       * problems of Allowance Double-Spend Exploit.
       * @dev Emits an Approval event indicating the updated allowance.
       * @dev spender cannot be the sero address
       * @dev spender must have allowance for the caller of at least subtractedValue
       */
      function decreaseAllowance(address spender, uint256 subtractedValue)
        public
        virtual
        returns (bool)
      {
        uint96 amount;
        if (subtractedValue == uint256(-1)) {
          amount = uint96(-1);
        } else {
          amount = safe96(
            subtractedValue,
            "Ctx::decreaseAllowance: amount exceeds 96 bits"
          );
        }
        _approve(
          msg.sender,
          spender,
          sub96(
            allowances[msg.sender][spender],
            amount,
            "Ctx::decreaseAllowance: decreased allowance below zero"
          )
        );
        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,
        uint256 rawAmount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
      ) external {
        uint96 amount;
        if (rawAmount == uint256(-1)) {
          amount = uint96(-1);
        } else {
          amount = safe96(rawAmount, "Ctx::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), "Ctx::permit: invalid signature");
        require(signatory == owner, "Ctx::permit: unauthorized");
        require(block.timestamp <= deadline, "Ctx::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 (uint256) {
        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, uint256 rawAmount) external returns (bool) {
        uint96 amount = safe96(rawAmount, "Ctx::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,
        uint256 rawAmount
      ) external returns (bool) {
        address spender = msg.sender;
        uint96 spenderAllowance = allowances[src][spender];
        uint96 amount = safe96(rawAmount, "Ctx::approve: amount exceeds 96 bits");
        if (spender != src && spenderAllowance != uint96(-1)) {
          uint96 newAllowance =
            sub96(
              spenderAllowance,
              amount,
              "Ctx::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) 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,
        uint256 nonce,
        uint256 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), "Ctx::delegateBySig: invalid signature");
        require(nonce == nonces[signatory]++, "Ctx::delegateBySig: invalid nonce");
        require(block.timestamp <= expiry, "Ctx::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, uint256 blockNumber)
        public
        view
        returns (uint96)
      {
        require(
          blockNumber < block.number,
          "Ctx::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),
          "Ctx::_transferTokens: cannot transfer from the zero address"
        );
        require(
          dst != address(0),
          "Ctx::_transferTokens: cannot transfer to the zero address"
        );
        require(
          dst != address(this),
          "Ctx::_transferTokens: cannot transfer to the Ctx address"
        );
        balances[src] = sub96(
          balances[src],
          amount,
          "Ctx::_transferTokens: transfer amount exceeds balance"
        );
        balances[dst] = add96(
          balances[dst],
          amount,
          "Ctx::_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, "Ctx::_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, "Ctx::_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,
            "Ctx::_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(uint256 n, string memory errorMessage)
        internal
        pure
        returns (uint32)
      {
        require(n < 2**32, errorMessage);
        return uint32(n);
      }
      function safe96(uint256 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 (uint256) {
        uint256 chainId;
        assembly {
          chainId := chainid()
        }
        return chainId;
      }
    }