ETH Price: $2,524.50 (-0.54%)

Transaction Decoder

Block:
17788995 at Jul-28-2023 03:53:11 AM +UTC
Transaction Fee:
0.001414234591113232 ETH $3.57
Gas Used:
79,856 Gas / 17.709810047 Gwei

Emitted Events:

258 GovernorBravoDelegator.VoteCast( voter=[Sender] 0xc4fb550fa8f2a6e5178711e56d5b48dedf897e5e, proposalId=42, support=1, votes=400000000000000000, reason= )

Account State Difference:

  Address   Before After State Difference Code
0x408ED635...Cbd8724C3
(beaverbuild)
14.520646782499108125 Eth14.520650775299108125 Eth0.0000039928
0xc4fb550F...EdF897e5e
0.011314407519611707 Eth
Nonce: 1415
0.009900172928498475 Eth
Nonce: 1416
0.001414234591113232

Execution Trace

GovernorBravoDelegator.56781388( )
  • GovernorBravoDelegate.castVote( proposalId=42, support=1 )
    • Uni.getPriorVotes( account=0xc4fb550FA8F2A6e5178711e56D5b48DEdF897e5e, blockNumber=17787452 ) => ( 400000000000000000 )
      File 1 of 3: GovernorBravoDelegator
      pragma solidity ^0.5.16;
      pragma experimental ABIEncoderV2;
      import "./GovernorBravoInterfaces.sol";
      contract GovernorBravoDelegator is GovernorBravoDelegatorStorage, GovernorBravoEvents {
      \tconstructor(
      \t\t\taddress timelock_,
      \t\t\taddress uni_,
      \t\t\taddress admin_,
      \t        address implementation_,
      \t        uint votingPeriod_,
      \t        uint votingDelay_,
                  uint proposalThreshold_) public {
              // Admin set to msg.sender for initialization
              admin = msg.sender;
              delegateTo(implementation_, abi.encodeWithSignature("initialize(address,address,uint256,uint256,uint256)",
                                                                  timelock_,
                                                                  uni_,
                                                                  votingPeriod_,
                                                                  votingDelay_,
                                                                  proposalThreshold_));
              _setImplementation(implementation_);
      \t\tadmin = admin_;
      \t}
      \t/**
           * @notice Called by the admin to update the implementation of the delegator
           * @param implementation_ The address of the new implementation for delegation
           */
          function _setImplementation(address implementation_) public {
              require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
              require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address");
              address oldImplementation = implementation;
              implementation = implementation_;
              emit NewImplementation(oldImplementation, implementation);
          }
          /**
           * @notice Internal method to delegate execution to another contract
           * @dev It returns to the external caller whatever the implementation returns or forwards reverts
           * @param callee The contract to delegatecall
           * @param data The raw data to delegatecall
           */
          function delegateTo(address callee, bytes memory data) internal {
              (bool success, bytes memory returnData) = callee.delegatecall(data);
              assembly {
                  if eq(success, 0) {
                      revert(add(returnData, 0x20), returndatasize)
                  }
              }
          }
      \t/**
           * @dev Delegates execution to an implementation contract.
           * It returns to the external caller whatever the implementation returns
           * or forwards reverts.
           */
          function () external payable {
              // delegate all other functions to current implementation
              (bool success, ) = implementation.delegatecall(msg.data);
              assembly {
                    let free_mem_ptr := mload(0x40)
                    returndatacopy(free_mem_ptr, 0, returndatasize)
                    switch success
                    case 0 { revert(free_mem_ptr, returndatasize) }
                    default { return(free_mem_ptr, returndatasize) }
              }
          }
      }pragma solidity ^0.5.16;
      pragma experimental ABIEncoderV2;
      contract GovernorBravoEvents {
          /// @notice An event emitted when a new proposal is created
          event ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint startBlock, uint endBlock, string description);
          /// @notice An event emitted when a vote has been cast on a proposal
          /// @param voter The address which casted a vote
          /// @param proposalId The proposal id which was voted on
          /// @param support Support value for the vote. 0=against, 1=for, 2=abstain
          /// @param votes Number of votes which were cast by the voter
          /// @param reason The reason given for the vote by the voter
          event VoteCast(address indexed voter, uint proposalId, uint8 support, uint votes, string reason);
          /// @notice An event emitted when a proposal has been canceled
          event ProposalCanceled(uint id);
          /// @notice An event emitted when a proposal has been queued in the Timelock
          event ProposalQueued(uint id, uint eta);
          /// @notice An event emitted when a proposal has been executed in the Timelock
          event ProposalExecuted(uint id);
          /// @notice An event emitted when the voting delay is set
          event VotingDelaySet(uint oldVotingDelay, uint newVotingDelay);
          /// @notice An event emitted when the voting period is set
          event VotingPeriodSet(uint oldVotingPeriod, uint newVotingPeriod);
          /// @notice Emitted when implementation is changed
          event NewImplementation(address oldImplementation, address newImplementation);
          /// @notice Emitted when proposal threshold is set
          event ProposalThresholdSet(uint oldProposalThreshold, uint newProposalThreshold);
          /// @notice Emitted when pendingAdmin is changed
          event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
          /// @notice Emitted when pendingAdmin is accepted, which means admin is updated
          event NewAdmin(address oldAdmin, address newAdmin);
      }
      contract GovernorBravoDelegatorStorage {
          /// @notice Administrator for this contract
          address public admin;
          /// @notice Pending administrator for this contract
          address public pendingAdmin;
          /// @notice Active brains of Governor
          address public implementation;
      }
      /**
       * @title Storage for Governor Bravo Delegate
       * @notice For future upgrades, do not change GovernorBravoDelegateStorageV1. Create a new
       * contract which implements GovernorBravoDelegateStorageV1 and following the naming convention
       * GovernorBravoDelegateStorageVX.
       */
      contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage {
          /// @notice The delay before voting on a proposal may take place, once proposed, in blocks
          uint public votingDelay;
          /// @notice The duration of voting on a proposal, in blocks
          uint public votingPeriod;
          /// @notice The number of votes required in order for a voter to become a proposer
          uint public proposalThreshold;
          /// @notice Initial proposal id set at become
          uint public initialProposalId;
          /// @notice The total number of proposals
          uint public proposalCount;
          /// @notice The address of the Uniswap Protocol Timelock
          TimelockInterface public timelock;
          /// @notice The address of the Uniswap governance token
          UniInterface public uni;
          /// @notice The official record of all proposals ever proposed
          mapping (uint => Proposal) public proposals;
          /// @notice The latest proposal for each proposer
          mapping (address => uint) public latestProposalIds;
          struct Proposal {
              /// @notice Unique id for looking up a proposal
              uint id;
              /// @notice Creator of the proposal
              address proposer;
              /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds
              uint eta;
              /// @notice the ordered list of target addresses for calls to be made
              address[] targets;
              /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made
              uint[] values;
              /// @notice The ordered list of function signatures to be called
              string[] signatures;
              /// @notice The ordered list of calldata to be passed to each call
              bytes[] calldatas;
              /// @notice The block at which voting begins: holders must delegate their votes prior to this block
              uint startBlock;
              /// @notice The block at which voting ends: votes must be cast prior to this block
              uint endBlock;
              /// @notice Current number of votes in favor of this proposal
              uint forVotes;
              /// @notice Current number of votes in opposition to this proposal
              uint againstVotes;
              /// @notice Current number of votes for abstaining for this proposal
              uint abstainVotes;
              /// @notice Flag marking whether the proposal has been canceled
              bool canceled;
              /// @notice Flag marking whether the proposal has been executed
              bool executed;
              /// @notice Receipts of ballots for the entire set of voters
              mapping (address => Receipt) receipts;
          }
          /// @notice Ballot receipt record for a voter
          struct Receipt {
              /// @notice Whether or not a vote has been cast
              bool hasVoted;
              /// @notice Whether or not the voter supports the proposal or abstains
              uint8 support;
              /// @notice The number of votes the voter had, which were cast
              uint96 votes;
          }
          /// @notice Possible states that a proposal may be in
          enum ProposalState {
              Pending,
              Active,
              Canceled,
              Defeated,
              Succeeded,
              Queued,
              Expired,
              Executed
          }
      }
      interface TimelockInterface {
          function delay() external view returns (uint);
          function GRACE_PERIOD() external view returns (uint);
          function acceptAdmin() external;
          function queuedTransactions(bytes32 hash) external view returns (bool);
          function queueTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external returns (bytes32);
          function cancelTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external;
          function executeTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external payable returns (bytes memory);
      }
      interface UniInterface {
          function getPriorVotes(address account, uint blockNumber) external view returns (uint96);
      }
      interface GovernorAlpha {
          /// @notice The total number of proposals
          function proposalCount() external returns (uint);
      }

      File 2 of 3: GovernorBravoDelegate
      pragma solidity ^0.5.16;
      pragma experimental ABIEncoderV2;
      import "./GovernorBravoInterfaces.sol";
      contract GovernorBravoDelegate is GovernorBravoDelegateStorageV1, GovernorBravoEvents {
          /// @notice The name of this contract
          string public constant name = "Uniswap Governor Bravo";
          /// @notice The minimum setable proposal threshold
          uint public constant MIN_PROPOSAL_THRESHOLD = 1000000e18; // 1,000,000 Uni
          /// @notice The maximum setable proposal threshold
          uint public constant MAX_PROPOSAL_THRESHOLD = 10000000e18; //10,000,000 Uni
          /// @notice The minimum setable voting period
          uint public constant MIN_VOTING_PERIOD = 5760; // About 24 hours
          /// @notice The max setable voting period
          uint public constant MAX_VOTING_PERIOD = 80640; // About 2 weeks
          /// @notice The min setable voting delay
          uint public constant MIN_VOTING_DELAY = 1;
          /// @notice The max setable voting delay
          uint public constant MAX_VOTING_DELAY = 40320; // About 1 week
          /// @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
          uint public constant quorumVotes = 40000000e18; // 40,000,000 = 4% of Uni
          /// @notice The maximum number of actions that can be included in a proposal
          uint public constant proposalMaxOperations = 10; // 10 actions
          /// @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,uint8 support)");
          /**
            * @notice Used to initialize the contract during delegator contructor
            * @param timelock_ The address of the Timelock
            * @param uni_ The address of the Uni token
            * @param votingPeriod_ The initial voting period
            * @param votingDelay_ The initial voting delay
            * @param proposalThreshold_ The initial proposal threshold
            */
          function initialize(address timelock_, address uni_, uint votingPeriod_, uint votingDelay_, uint proposalThreshold_) public {
              require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once");
              require(msg.sender == admin, "GovernorBravo::initialize: admin only");
              require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address");
              require(uni_ != address(0), "GovernorBravo::initialize: invalid uni address");
              require(votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, "GovernorBravo::initialize: invalid voting period");
              require(votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, "GovernorBravo::initialize: invalid voting delay");
              require(proposalThreshold_ >= MIN_PROPOSAL_THRESHOLD && proposalThreshold_ <= MAX_PROPOSAL_THRESHOLD, "GovernorBravo::initialize: invalid proposal threshold");
              timelock = TimelockInterface(timelock_);
              uni = UniInterface(uni_);
              votingPeriod = votingPeriod_;
              votingDelay = votingDelay_;
              proposalThreshold = proposalThreshold_;
          }
          /**
            * @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold
            * @param targets Target addresses for proposal calls
            * @param values Eth values for proposal calls
            * @param signatures Function signatures for proposal calls
            * @param calldatas Calldatas for proposal calls
            * @param description String description of the proposal
            * @return Proposal id of new proposal
            */
          function propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description) public returns (uint) {
              // Reject proposals before initiating as Governor
              require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");
              require(uni.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold, "GovernorBravo::propose: proposer votes below proposal threshold");
              require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorBravo::propose: proposal function information arity mismatch");
              require(targets.length != 0, "GovernorBravo::propose: must provide actions");
              require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
              uint latestProposalId = latestProposalIds[msg.sender];
              if (latestProposalId != 0) {
                ProposalState proposersLatestProposalState = state(latestProposalId);
                require(proposersLatestProposalState != ProposalState.Active, "GovernorBravo::propose: one live proposal per proposer, found an already active proposal");
                require(proposersLatestProposalState != ProposalState.Pending, "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal");
              }
              uint startBlock = add256(block.number, votingDelay);
              uint 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,
                  abstainVotes: 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;
          }
          /**
            * @notice Queues a proposal of state succeeded
            * @param proposalId The id of the proposal to queue
            */
          function queue(uint proposalId) external {
              require(state(proposalId) == ProposalState.Succeeded, "GovernorBravo::queue: proposal can only be queued if it is succeeded");
              Proposal storage proposal = proposals[proposalId];
              uint eta = add256(block.timestamp, timelock.delay());
              for (uint i = 0; i < proposal.targets.length; i++) {
                  queueOrRevertInternal(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
              }
              proposal.eta = eta;
              emit ProposalQueued(proposalId, eta);
          }
          function queueOrRevertInternal(address target, uint value, string memory signature, bytes memory data, uint eta) internal {
              require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta");
              timelock.queueTransaction(target, value, signature, data, eta);
          }
          /**
            * @notice Executes a queued proposal if eta has passed
            * @param proposalId The id of the proposal to execute
            */
          function execute(uint proposalId) external payable {
              require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued");
              Proposal storage proposal = proposals[proposalId];
              proposal.executed = true;
              for (uint i = 0; i < proposal.targets.length; i++) {
                  timelock.executeTransaction.value(proposal.values[i])(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);
              }
              emit ProposalExecuted(proposalId);
          }
          /**
            * @notice Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold
            * @param proposalId The id of the proposal to cancel
            */
          function cancel(uint proposalId) external {
              require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");
              Proposal storage proposal = proposals[proposalId];
              require(msg.sender == proposal.proposer || uni.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold, "GovernorBravo::cancel: proposer above threshold");
              proposal.canceled = true;
              for (uint 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);
          }
          /**
            * @notice Gets actions of a proposal
            * @param proposalId the id of the proposal
            * @return Targets, values, signatures, and calldatas of the proposal actions
            */
          function getActions(uint proposalId) external view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) {
              Proposal storage p = proposals[proposalId];
              return (p.targets, p.values, p.signatures, p.calldatas);
          }
          /**
            * @notice Gets the receipt for a voter on a given proposal
            * @param proposalId the id of proposal
            * @param voter The address of the voter
            * @return The voting receipt
            */
          function getReceipt(uint proposalId, address voter) external view returns (Receipt memory) {
              return proposals[proposalId].receipts[voter];
          }
          /**
            * @notice Gets the state of a proposal
            * @param proposalId The id of the proposal
            * @return Proposal state
            */
          function state(uint proposalId) public view returns (ProposalState) {
              require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::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;
              }
          }
          /**
            * @notice Cast a vote for a proposal
            * @param proposalId The id of the proposal to vote on
            * @param support The support value for the vote. 0=against, 1=for, 2=abstain
            */
          function castVote(uint proposalId, uint8 support) external {
              emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), "");
          }
          /**
            * @notice Cast a vote for a proposal with a reason
            * @param proposalId The id of the proposal to vote on
            * @param support The support value for the vote. 0=against, 1=for, 2=abstain
            * @param reason The reason given for the vote by the voter
            */
          function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external {
              emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason);
          }
          /**
            * @notice Cast a vote for a proposal by signature
            * @dev External function that accepts EIP-712 signatures for voting on proposals.
            */
          function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
              bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), 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), "GovernorBravo::castVoteBySig: invalid signature");
              emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
          }
          /**
            * @notice Internal function that caries out voting logic
            * @param voter The voter that is casting their vote
            * @param proposalId The id of the proposal to vote on
            * @param support The support value for the vote. 0=against, 1=for, 2=abstain
            * @return The number of votes cast
            */
          function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) {
              require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");
              require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
              Proposal storage proposal = proposals[proposalId];
              Receipt storage receipt = proposal.receipts[voter];
              require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
              uint96 votes = uni.getPriorVotes(voter, proposal.startBlock);
              if (support == 0) {
                  proposal.againstVotes = add256(proposal.againstVotes, votes);
              } else if (support == 1) {
                  proposal.forVotes = add256(proposal.forVotes, votes);
              } else if (support == 2) {
                  proposal.abstainVotes = add256(proposal.abstainVotes, votes);
              }
              receipt.hasVoted = true;
              receipt.support = support;
              receipt.votes = votes;
              return votes;
          }
          /**
            * @notice Admin function for setting the voting delay
            * @param newVotingDelay new voting delay, in blocks
            */
          function _setVotingDelay(uint newVotingDelay) external {
              require(msg.sender == admin, "GovernorBravo::_setVotingDelay: admin only");
              require(newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, "GovernorBravo::_setVotingDelay: invalid voting delay");
              uint oldVotingDelay = votingDelay;
              votingDelay = newVotingDelay;
              emit VotingDelaySet(oldVotingDelay,votingDelay);
          }
          /**
            * @notice Admin function for setting the voting period
            * @param newVotingPeriod new voting period, in blocks
            */
          function _setVotingPeriod(uint newVotingPeriod) external {
              require(msg.sender == admin, "GovernorBravo::_setVotingPeriod: admin only");
              require(newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, "GovernorBravo::_setVotingPeriod: invalid voting period");
              uint oldVotingPeriod = votingPeriod;
              votingPeriod = newVotingPeriod;
              emit VotingPeriodSet(oldVotingPeriod, votingPeriod);
          }
          /**
            * @notice Admin function for setting the proposal threshold
            * @dev newProposalThreshold must be greater than the hardcoded min
            * @param newProposalThreshold new proposal threshold
            */
          function _setProposalThreshold(uint newProposalThreshold) external {
              require(msg.sender == admin, "GovernorBravo::_setProposalThreshold: admin only");
              require(newProposalThreshold >= MIN_PROPOSAL_THRESHOLD && newProposalThreshold <= MAX_PROPOSAL_THRESHOLD, "GovernorBravo::_setProposalThreshold: invalid proposal threshold");
              uint oldProposalThreshold = proposalThreshold;
              proposalThreshold = newProposalThreshold;
              emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold);
          }
          /**
            * @notice Initiate the GovernorBravo contract
            * @dev Admin only. Sets initial proposal id which initiates the contract, ensuring a continuous proposal id count
            * @param proposalCount proposal id to initialize from
            */
          function _initiate(uint proposalCount) external {
              require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
              require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
              initialProposalId = proposalCount;
              timelock.acceptAdmin();
          }
          /**
            * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
            * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
            * @param newPendingAdmin New pending admin.
            */
          function _setPendingAdmin(address newPendingAdmin) external {
              // Check caller = admin
              require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only");
              // Save current value, if any, for inclusion in log
              address oldPendingAdmin = pendingAdmin;
              // Store pendingAdmin with value newPendingAdmin
              pendingAdmin = newPendingAdmin;
              // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
              emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
          }
          /**
            * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
            * @dev Admin function for pending admin to accept role and update admin
            */
          function _acceptAdmin() external {
              // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
              require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only");
              // Save current values for inclusion in log
              address oldAdmin = admin;
              address oldPendingAdmin = pendingAdmin;
              // Store admin with value pendingAdmin
              admin = pendingAdmin;
              // Clear the pending value
              pendingAdmin = address(0);
              emit NewAdmin(oldAdmin, admin);
              emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
          }
          function add256(uint256 a, uint256 b) internal pure returns (uint) {
              uint c = a + b;
              require(c >= a, "addition overflow");
              return c;
          }
          function sub256(uint256 a, uint256 b) internal pure returns (uint) {
              require(b <= a, "subtraction underflow");
              return a - b;
          }
          function getChainIdInternal() internal pure returns (uint) {
              uint chainId;
              assembly { chainId := chainid() }
              return chainId;
          }
      }pragma solidity ^0.5.16;
      pragma experimental ABIEncoderV2;
      contract GovernorBravoEvents {
          /// @notice An event emitted when a new proposal is created
          event ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint startBlock, uint endBlock, string description);
          /// @notice An event emitted when a vote has been cast on a proposal
          /// @param voter The address which casted a vote
          /// @param proposalId The proposal id which was voted on
          /// @param support Support value for the vote. 0=against, 1=for, 2=abstain
          /// @param votes Number of votes which were cast by the voter
          /// @param reason The reason given for the vote by the voter
          event VoteCast(address indexed voter, uint proposalId, uint8 support, uint votes, string reason);
          /// @notice An event emitted when a proposal has been canceled
          event ProposalCanceled(uint id);
          /// @notice An event emitted when a proposal has been queued in the Timelock
          event ProposalQueued(uint id, uint eta);
          /// @notice An event emitted when a proposal has been executed in the Timelock
          event ProposalExecuted(uint id);
          /// @notice An event emitted when the voting delay is set
          event VotingDelaySet(uint oldVotingDelay, uint newVotingDelay);
          /// @notice An event emitted when the voting period is set
          event VotingPeriodSet(uint oldVotingPeriod, uint newVotingPeriod);
          /// @notice Emitted when implementation is changed
          event NewImplementation(address oldImplementation, address newImplementation);
          /// @notice Emitted when proposal threshold is set
          event ProposalThresholdSet(uint oldProposalThreshold, uint newProposalThreshold);
          /// @notice Emitted when pendingAdmin is changed
          event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
          /// @notice Emitted when pendingAdmin is accepted, which means admin is updated
          event NewAdmin(address oldAdmin, address newAdmin);
      }
      contract GovernorBravoDelegatorStorage {
          /// @notice Administrator for this contract
          address public admin;
          /// @notice Pending administrator for this contract
          address public pendingAdmin;
          /// @notice Active brains of Governor
          address public implementation;
      }
      /**
       * @title Storage for Governor Bravo Delegate
       * @notice For future upgrades, do not change GovernorBravoDelegateStorageV1. Create a new
       * contract which implements GovernorBravoDelegateStorageV1 and following the naming convention
       * GovernorBravoDelegateStorageVX.
       */
      contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage {
          /// @notice The delay before voting on a proposal may take place, once proposed, in blocks
          uint public votingDelay;
          /// @notice The duration of voting on a proposal, in blocks
          uint public votingPeriod;
          /// @notice The number of votes required in order for a voter to become a proposer
          uint public proposalThreshold;
          /// @notice Initial proposal id set at become
          uint public initialProposalId;
          /// @notice The total number of proposals
          uint public proposalCount;
          /// @notice The address of the Uniswap Protocol Timelock
          TimelockInterface public timelock;
          /// @notice The address of the Uniswap governance token
          UniInterface public uni;
          /// @notice The official record of all proposals ever proposed
          mapping (uint => Proposal) public proposals;
          /// @notice The latest proposal for each proposer
          mapping (address => uint) public latestProposalIds;
          struct Proposal {
              /// @notice Unique id for looking up a proposal
              uint id;
              /// @notice Creator of the proposal
              address proposer;
              /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds
              uint eta;
              /// @notice the ordered list of target addresses for calls to be made
              address[] targets;
              /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made
              uint[] values;
              /// @notice The ordered list of function signatures to be called
              string[] signatures;
              /// @notice The ordered list of calldata to be passed to each call
              bytes[] calldatas;
              /// @notice The block at which voting begins: holders must delegate their votes prior to this block
              uint startBlock;
              /// @notice The block at which voting ends: votes must be cast prior to this block
              uint endBlock;
              /// @notice Current number of votes in favor of this proposal
              uint forVotes;
              /// @notice Current number of votes in opposition to this proposal
              uint againstVotes;
              /// @notice Current number of votes for abstaining for this proposal
              uint abstainVotes;
              /// @notice Flag marking whether the proposal has been canceled
              bool canceled;
              /// @notice Flag marking whether the proposal has been executed
              bool executed;
              /// @notice Receipts of ballots for the entire set of voters
              mapping (address => Receipt) receipts;
          }
          /// @notice Ballot receipt record for a voter
          struct Receipt {
              /// @notice Whether or not a vote has been cast
              bool hasVoted;
              /// @notice Whether or not the voter supports the proposal or abstains
              uint8 support;
              /// @notice The number of votes the voter had, which were cast
              uint96 votes;
          }
          /// @notice Possible states that a proposal may be in
          enum ProposalState {
              Pending,
              Active,
              Canceled,
              Defeated,
              Succeeded,
              Queued,
              Expired,
              Executed
          }
      }
      interface TimelockInterface {
          function delay() external view returns (uint);
          function GRACE_PERIOD() external view returns (uint);
          function acceptAdmin() external;
          function queuedTransactions(bytes32 hash) external view returns (bool);
          function queueTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external returns (bytes32);
          function cancelTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external;
          function executeTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external payable returns (bytes memory);
      }
      interface UniInterface {
          function getPriorVotes(address account, uint blockNumber) external view returns (uint96);
      }
      interface GovernorAlpha {
          /// @notice The total number of proposals
          function proposalCount() external returns (uint);
      }

      File 3 of 3: Uni
      /**
       *Submitted for verification at Etherscan.io on 2020-09-15
      */
      
      pragma solidity ^0.5.16;
      pragma experimental ABIEncoderV2;
      
      // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol
      // 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;
          }
      }
      
      contract Uni {
          /// @notice EIP-20 token name for this token
          string public constant name = "Uniswap";
      
          /// @notice EIP-20 token symbol for this token
          string public constant symbol = "UNI";
      
          /// @notice EIP-20 token decimals for this token
          uint8 public constant decimals = 18;
      
          /// @notice Total number of tokens in circulation
          uint public totalSupply = 1_000_000_000e18; // 1 billion Uni
      
          /// @notice Address which may mint new tokens
          address public minter;
      
          /// @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 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 Uni 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, "Uni::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, "Uni::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, uint rawAmount) external {
              require(msg.sender == minter, "Uni::mint: only the minter can mint");
              require(block.timestamp >= mintingAllowedAfter, "Uni::mint: minting not allowed yet");
              require(dst != address(0), "Uni::mint: cannot transfer to the zero address");
      
              // record the mint
              mintingAllowedAfter = SafeMath.add(block.timestamp, minimumTimeBetweenMints);
      
              // mint the amount
              uint96 amount = safe96(rawAmount, "Uni::mint: amount exceeds 96 bits");
              require(amount <= SafeMath.div(SafeMath.mul(totalSupply, mintCap), 100), "Uni::mint: exceeded mint cap");
              totalSupply = safe96(SafeMath.add(totalSupply, amount), "Uni::mint: totalSupply exceeds 96 bits");
      
              // transfer the amount to the recipient
              balances[dst] = add96(balances[dst], amount, "Uni::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, "Uni::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, "Uni::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), "Uni::permit: invalid signature");
              require(signatory == owner, "Uni::permit: unauthorized");
              require(now <= deadline, "Uni::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, "Uni::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, "Uni::approve: amount exceeds 96 bits");
      
              if (spender != src && spenderAllowance != uint96(-1)) {
                  uint96 newAllowance = sub96(spenderAllowance, amount, "Uni::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, 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), "Uni::delegateBySig: invalid signature");
              require(nonce == nonces[signatory]++, "Uni::delegateBySig: invalid nonce");
              require(now <= expiry, "Uni::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) public view returns (uint96) {
              require(blockNumber < block.number, "Uni::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), "Uni::_transferTokens: cannot transfer from the zero address");
              require(dst != address(0), "Uni::_transferTokens: cannot transfer to the zero address");
      
              balances[src] = sub96(balances[src], amount, "Uni::_transferTokens: transfer amount exceeds balance");
              balances[dst] = add96(balances[dst], amount, "Uni::_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, "Uni::_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, "Uni::_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, "Uni::_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;
          }
      }