Transaction Hash:
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 | ||
---|---|---|---|---|---|
0x874C5D59...79F196d5b | |||||
0x95222290...5CC4BAfe5
Miner
| (beaverbuild) | 9.936300549745488786 Eth | 9.936371412745488786 Eth | 0.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 )
castVote[GovernorBeta (ln:330)]
_castVote[GovernorBeta (ln:331)]
state[GovernorBeta (ln:366)]
quorumVotes[GovernorBeta (ln:315)]
add256[GovernorBeta (ln:323)]
GRACE_PERIOD[GovernorBeta (ln:323)]
getPriorVotes[GovernorBeta (ln:375)]
add256[GovernorBeta (ln:377)]
add256[GovernorBeta (ln:379)]
VoteCast[GovernorBeta (ln:384)]
File 1 of 2: GovernorBeta
File 2 of 2: Ctx
// 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; } }