// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC20 } from "./interfaces/IERC20.sol";
import { IERC20Extended } from "./interfaces/IERC20Extended.sol";
import { ERC3009 } from "./ERC3009.sol";
/**
* @title An ERC20 token extended with EIP-2612 permits for signed approvals (via EIP-712 and with EIP-1271
* and EIP-5267 compatibility), and extended with EIP-3009 transfer with authorization (via EIP-712).
* @author M^0 Labs
*/
abstract contract ERC20Extended is IERC20Extended, ERC3009 {
/* ============ Variables ============ */
/**
* @inheritdoc IERC20Extended
* @dev Keeping this constant, despite `permit` parameter name differences, to ensure max EIP-2612 compatibility.
* keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
*/
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
/// @inheritdoc IERC20
uint8 public immutable decimals;
/// @inheritdoc IERC20
string public symbol;
/// @inheritdoc IERC20
mapping(address account => mapping(address spender => uint256 allowance)) public allowance;
/* ============ Constructor ============ */
/**
* @notice Constructs the ERC20Extended contract.
* @param name_ The name of the token.
* @param symbol_ The symbol of the token.
* @param decimals_ The number of decimals the token uses.
*/
constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC3009(name_) {
symbol = symbol_;
decimals = decimals_;
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IERC20
function approve(address spender_, uint256 amount_) external returns (bool success_) {
_approve(msg.sender, spender_, amount_);
return true;
}
/// @inheritdoc IERC20Extended
function permit(
address owner_,
address spender_,
uint256 value_,
uint256 deadline_,
uint8 v_,
bytes32 r_,
bytes32 s_
) external {
_revertIfInvalidSignature(owner_, _permitAndGetDigest(owner_, spender_, value_, deadline_), v_, r_, s_);
}
/// @inheritdoc IERC20Extended
function permit(
address owner_,
address spender_,
uint256 value_,
uint256 deadline_,
bytes memory signature_
) external {
_revertIfInvalidSignature(owner_, _permitAndGetDigest(owner_, spender_, value_, deadline_), signature_);
}
/// @inheritdoc IERC20
function transfer(address recipient_, uint256 amount_) external returns (bool success_) {
_transfer(msg.sender, recipient_, amount_);
return true;
}
/// @inheritdoc IERC20
function transferFrom(address sender_, address recipient_, uint256 amount_) external returns (bool success_) {
uint256 spenderAllowance_ = allowance[sender_][msg.sender]; // Cache `spenderAllowance_` to stack.
if (spenderAllowance_ != type(uint256).max) {
if (spenderAllowance_ < amount_) revert InsufficientAllowance(msg.sender, spenderAllowance_, amount_);
unchecked {
_setAllowance(sender_, msg.sender, spenderAllowance_ - amount_);
}
}
_transfer(sender_, recipient_, amount_);
return true;
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IERC20
function name() external view returns (string memory name_) {
return _name;
}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Approve `spender_` to spend `amount_` of tokens from `account_`.
* @param account_ The address approving the allowance.
* @param spender_ The address approved to spend the tokens.
* @param amount_ The amount of tokens being approved for spending.
*/
function _approve(address account_, address spender_, uint256 amount_) internal virtual {
_setAllowance(account_, spender_, amount_);
emit Approval(account_, spender_, amount_);
}
/**
* @dev Set the `amount_` of tokens `spender_` is allowed to spend from `account_`.
* @param account_ The address for which the allowance is set.
* @param spender_ The address allowed to spend the tokens.
* @param amount_ The amount of tokens being allowed for spending.
*/
function _setAllowance(address account_, address spender_, uint256 amount_) internal virtual {
allowance[account_][spender_] = amount_;
}
/**
* @dev Performs the approval based on the permit info, validates the deadline, and returns the digest.
* @param owner_ The address of the account approving the allowance.
* @param spender_ The address of the account being allowed to spend the tokens.
* @param amount_ The amount of tokens being approved for spending.
* @param deadline_ The deadline by which the signature must be used.
* @return digest_ The EIP-712 digest of the permit.
*/
function _permitAndGetDigest(
address owner_,
address spender_,
uint256 amount_,
uint256 deadline_
) internal virtual returns (bytes32 digest_) {
_revertIfExpired(deadline_);
_approve(owner_, spender_, amount_);
unchecked {
// Nonce realistically cannot overflow.
return
_getDigest(
keccak256(abi.encode(PERMIT_TYPEHASH, owner_, spender_, amount_, nonces[owner_]++, deadline_))
);
}
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC3009 } from "./interfaces/IERC3009.sol";
import { StatefulERC712 } from "./StatefulERC712.sol";
/**
* @title ERC3009 implementation allowing the transfer of fungible assets via a signed authorization.
* @author M^0 Labs
* @dev Inherits from ERC712 and StatefulERC712.
*/
abstract contract ERC3009 is IERC3009, StatefulERC712 {
/* ============ Variables ============ */
// solhint-disable-next-line max-line-length
/// @dev keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
/// @inheritdoc IERC3009
bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH =
0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
// solhint-disable-next-line max-line-length
/// @dev keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
/// @inheritdoc IERC3009
bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH =
0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;
/**
* @inheritdoc IERC3009
* @dev keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
*/
bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH =
0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;
/// @inheritdoc IERC3009
mapping(address authorizer => mapping(bytes32 nonce => bool isNonceUsed)) public authorizationState;
/* ============ Constructor ============ */
/**
* @notice Construct the ERC3009 contract.
* @param name_ The name of the contract.
*/
constructor(string memory name_) StatefulERC712(name_) {}
/* ============ Interactive Functions ============ */
/// @inheritdoc IERC3009
function transferWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
bytes memory signature_
) external {
_revertIfInvalidSignature(
from_,
_getTransferWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
signature_
);
_transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function transferWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
bytes32 r_,
bytes32 vs_
) external {
_revertIfInvalidSignature(
from_,
_getTransferWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
r_,
vs_
);
_transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function transferWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
uint8 v_,
bytes32 r_,
bytes32 s_
) external {
_revertIfInvalidSignature(
from_,
_getTransferWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
v_,
r_,
s_
);
_transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function receiveWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
bytes memory signature_
) external {
_revertIfInvalidSignature(
from_,
_getReceiveWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
signature_
);
_receiveWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function receiveWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
bytes32 r_,
bytes32 vs_
) external {
_revertIfInvalidSignature(
from_,
_getReceiveWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
r_,
vs_
);
_receiveWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function receiveWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_,
uint8 v_,
bytes32 r_,
bytes32 s_
) external {
_revertIfInvalidSignature(
from_,
_getReceiveWithAuthorizationDigest(from_, to_, value_, validAfter_, validBefore_, nonce_),
v_,
r_,
s_
);
_receiveWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/// @inheritdoc IERC3009
function cancelAuthorization(address authorizer_, bytes32 nonce_, bytes memory signature_) external {
_revertIfInvalidSignature(authorizer_, _getCancelAuthorizationDigest(authorizer_, nonce_), signature_);
_cancelAuthorization(authorizer_, nonce_);
}
/// @inheritdoc IERC3009
function cancelAuthorization(address authorizer_, bytes32 nonce_, bytes32 r_, bytes32 vs_) external {
_revertIfInvalidSignature(authorizer_, _getCancelAuthorizationDigest(authorizer_, nonce_), r_, vs_);
_cancelAuthorization(authorizer_, nonce_);
}
/// @inheritdoc IERC3009
function cancelAuthorization(address authorizer_, bytes32 nonce_, uint8 v_, bytes32 r_, bytes32 s_) external {
_revertIfInvalidSignature(authorizer_, _getCancelAuthorizationDigest(authorizer_, nonce_), v_, r_, s_);
_cancelAuthorization(authorizer_, nonce_);
}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Common transfer function used by `transferWithAuthorization` and `_receiveWithAuthorization`.
* @param from_ Payer's address (Authorizer).
* @param to_ Payee's address.
* @param value_ Amount to be transferred.
* @param validAfter_ The time after which this is valid (unix time).
* @param validBefore_ The time before which this is valid (unix time).
* @param nonce_ Unique nonce.
*/
function _transferWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_
) internal {
if (block.timestamp <= validAfter_) revert AuthorizationNotYetValid(block.timestamp, validAfter_);
if (block.timestamp >= validBefore_) revert AuthorizationExpired(block.timestamp, validBefore_);
_revertIfAuthorizationAlreadyUsed(from_, nonce_);
authorizationState[from_][nonce_] = true;
emit AuthorizationUsed(from_, nonce_);
_transfer(from_, to_, value_);
}
/**
* @dev Common receive function used by `receiveWithAuthorization`.
* @param from_ Payer's address (Authorizer).
* @param to_ Payee's address.
* @param value_ Amount to be transferred.
* @param validAfter_ The time after which this is valid (unix time).
* @param validBefore_ The time before which this is valid (unix time).
* @param nonce_ Unique nonce.
*/
function _receiveWithAuthorization(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_
) internal {
if (msg.sender != to_) revert CallerMustBePayee(msg.sender, to_);
_transferWithAuthorization(from_, to_, value_, validAfter_, validBefore_, nonce_);
}
/**
* @dev Common cancel function used by `cancelAuthorization`.
* @param authorizer_ Authorizer's address.
* @param nonce_ Nonce of the authorization.
*/
function _cancelAuthorization(address authorizer_, bytes32 nonce_) internal {
_revertIfAuthorizationAlreadyUsed(authorizer_, nonce_);
authorizationState[authorizer_][nonce_] = true;
emit AuthorizationCanceled(authorizer_, nonce_);
}
/**
* @dev Internal ERC20 transfer function that needs to be implemented by the inheriting contract.
* @param sender_ The sender's address.
* @param recipient_ The recipient's address.
* @param amount_ The amount to be transferred.
*/
function _transfer(address sender_, address recipient_, uint256 amount_) internal virtual;
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns the internal EIP-712 digest of a transferWithAuthorization call.
* @param from_ Payer's address (Authorizer).
* @param to_ Payee's address.
* @param value_ Amount to be transferred.
* @param validAfter_ The time after which this is valid (unix time).
* @param validBefore_ The time before which this is valid (unix time).
* @param nonce_ Unique nonce.
* @return The internal EIP-712 digest.
*/
function _getTransferWithAuthorizationDigest(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_
) internal view returns (bytes32) {
return
_getDigest(
keccak256(
abi.encode(
TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
from_,
to_,
value_,
validAfter_,
validBefore_,
nonce_
)
)
);
}
/**
* @dev Returns the internal EIP-712 digest of a receiveWithAuthorization call.
* @param from_ Payer's address (Authorizer).
* @param to_ Payee's address.
* @param value_ Amount to be transferred.
* @param validAfter_ The time after which this is valid (unix time).
* @param validBefore_ The time before which this is valid (unix time).
* @param nonce_ Unique nonce.
* @return The internal EIP-712 digest.
*/
function _getReceiveWithAuthorizationDigest(
address from_,
address to_,
uint256 value_,
uint256 validAfter_,
uint256 validBefore_,
bytes32 nonce_
) internal view returns (bytes32) {
return
_getDigest(
keccak256(
abi.encode(
RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
from_,
to_,
value_,
validAfter_,
validBefore_,
nonce_
)
)
);
}
/**
* @dev Returns the internal EIP-712 digest of a cancelAuthorization call.
* @param authorizer_ Authorizer's address.
* @param nonce_ Nonce of the authorization.
* @return The internal EIP-712 digest.
*/
function _getCancelAuthorizationDigest(address authorizer_, bytes32 nonce_) internal view returns (bytes32) {
return _getDigest(keccak256(abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer_, nonce_)));
}
/**
* @dev Reverts if the authorization is already used.
* @param authorizer_ The authorizer's address.
* @param nonce_ The nonce of the authorization.
*/
function _revertIfAuthorizationAlreadyUsed(address authorizer_, bytes32 nonce_) internal view {
if (authorizationState[authorizer_][nonce_]) revert AuthorizationAlreadyUsed(authorizer_, nonce_);
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { StatefulERC712 } from "../../lib/common/src/StatefulERC712.sol";
import { IERC5805 } from "./interfaces/IERC5805.sol";
/**
* @title Voting with voting weight tracking and delegation support.
* @author M^0 Labs
*/
abstract contract ERC5805 is IERC5805, StatefulERC712 {
/* ============ Variables ============ */
/**
* @inheritdoc IERC5805
* @dev Keeping this constant, despite `delegateBySig` param name differences, to ensure max EIP-5805 compatibility.
* keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)")
*/
bytes32 public constant DELEGATION_TYPEHASH = 0xe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf;
/* ============ Interactive Functions ============ */
/// @inheritdoc IERC5805
function delegate(address delegatee_) external {
_delegate(msg.sender, delegatee_);
}
/// @inheritdoc IERC5805
function delegateBySig(
address delegatee_,
uint256 nonce_,
uint256 expiry_,
uint8 v_,
bytes32 r_,
bytes32 s_
) external {
bytes32 digest_ = _getDelegationDigest(delegatee_, nonce_, expiry_);
address signer_ = _getSignerAndRevertIfInvalidSignature(digest_, v_, r_, s_);
_revertIfExpired(expiry_);
_checkAndIncrementNonce(signer_, nonce_);
_delegate(signer_, delegatee_);
}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Reverts if a given `nonce_` is reused for `account_`, then increments the nonce in storage.
* @param account_ The address of the account the nonce is being verified for.
* @param nonce_ The nonce being used by the account.
*/
function _checkAndIncrementNonce(address account_, uint256 nonce_) internal {
unchecked {
uint256 currentNonce_ = nonces[account_]++; // Nonce realistically cannot overflow.
if (nonce_ != currentNonce_) revert InvalidAccountNonce(nonce_, currentNonce_);
}
}
/**
* @dev Delegate voting power from `delegator_` to `newDelegatee_`.
* @param delegator_ The address of the account delegating voting power.
* @param newDelegatee_ The address of the account receiving voting power.
*/
function _delegate(address delegator_, address newDelegatee_) internal virtual;
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns the digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
* @param delegatee_ The address of the delegatee to delegate to.
* @param nonce_ The nonce of the account delegating.
* @param expiry_ The timestamp until which the signature is still valid.
* @return The digest to be signed.
*/
function _getDelegationDigest(address delegatee_, uint256 nonce_, uint256 expiry_) internal view returns (bytes32) {
return _getDigest(keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee_, nonce_, expiry_)));
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC712 } from "./interfaces/IERC712.sol";
import { IERC712Extended } from "./interfaces/IERC712Extended.sol";
import { SignatureChecker } from "./libs/SignatureChecker.sol";
/**
* @title Typed structured data hashing and signing via EIP-712, extended by EIP-5267.
* @author M^0 Labs
* @dev An abstract implementation to satisfy EIP-712: https://eips.ethereum.org/EIPS/eip-712
*/
abstract contract ERC712Extended is IERC712Extended {
/* ============ Variables ============ */
/// @dev keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
bytes32 internal constant _EIP712_DOMAIN_HASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev keccak256("1")
bytes32 internal constant _EIP712_VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
/// @dev Initial Chain ID set at deployment.
uint256 internal immutable _INITIAL_CHAIN_ID;
/// @dev Initial EIP-712 domain separator set at deployment.
bytes32 internal immutable _INITIAL_DOMAIN_SEPARATOR;
/// @dev The name of the contract.
string internal _name;
/* ============ Constructor ============ */
/**
* @notice Constructs the EIP-712 domain separator.
* @param name_ The name of the contract.
*/
constructor(string memory name_) {
_name = name_;
_INITIAL_CHAIN_ID = block.chainid;
_INITIAL_DOMAIN_SEPARATOR = _getDomainSeparator();
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IERC712Extended
function eip712Domain()
external
view
virtual
returns (
bytes1 fields_,
string memory name_,
string memory version_,
uint256 chainId_,
address verifyingContract_,
bytes32 salt_,
uint256[] memory extensions_
)
{
return (
hex"0f", // 01111
_name,
"1",
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/// @inheritdoc IERC712
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == _INITIAL_CHAIN_ID ? _INITIAL_DOMAIN_SEPARATOR : _getDomainSeparator();
}
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Computes the EIP-712 domain separator.
* @return The EIP-712 domain separator.
*/
function _getDomainSeparator() internal view returns (bytes32) {
return
keccak256(
abi.encode(
_EIP712_DOMAIN_HASH,
keccak256(bytes(_name)),
_EIP712_VERSION_HASH,
block.chainid,
address(this)
)
);
}
/**
* @dev Returns the digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
* @param internalDigest_ The internal digest.
* @return The digest to be signed.
*/
function _getDigest(bytes32 internalDigest_) internal view returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), internalDigest_));
}
/**
* @dev Revert if the signature is expired.
* @param expiry_ Timestamp at which the signature expires or max uint256 for no expiry.
*/
function _revertIfExpired(uint256 expiry_) internal view {
if (block.timestamp > expiry_) revert SignatureExpired(expiry_, block.timestamp);
}
/**
* @dev Revert if the signature is invalid.
* @dev We first validate if the signature is a valid ECDSA signature and return early if it is the case.
* Then, we validate if it is a valid ERC-1271 signature, and return early if it is the case.
* If not, we revert with the error from the ECDSA signature validation.
* @param signer_ The signer of the signature.
* @param digest_ The digest that was signed.
* @param signature_ The signature.
*/
function _revertIfInvalidSignature(address signer_, bytes32 digest_, bytes memory signature_) internal view {
SignatureChecker.Error error_ = SignatureChecker.validateECDSASignature(signer_, digest_, signature_);
if (error_ == SignatureChecker.Error.NoError) return;
if (SignatureChecker.isValidERC1271Signature(signer_, digest_, signature_)) return;
_revertIfError(error_);
}
/**
* @dev Returns the signer of a signed digest, via EIP-712, and reverts if the signature is invalid.
* @param digest_ The digest that was signed.
* @param v_ v of the signature.
* @param r_ r of the signature.
* @param s_ s of the signature.
* @return signer_ The signer of the digest.
*/
function _getSignerAndRevertIfInvalidSignature(
bytes32 digest_,
uint8 v_,
bytes32 r_,
bytes32 s_
) internal pure returns (address signer_) {
SignatureChecker.Error error_;
(error_, signer_) = SignatureChecker.recoverECDSASigner(digest_, v_, r_, s_);
_revertIfError(error_);
}
/**
* @dev Revert if the signature is invalid.
* @param signer_ The signer of the signature.
* @param digest_ The digest that was signed.
* @param r_ An ECDSA/secp256k1 signature parameter.
* @param vs_ An ECDSA/secp256k1 short signature parameter.
*/
function _revertIfInvalidSignature(address signer_, bytes32 digest_, bytes32 r_, bytes32 vs_) internal pure {
_revertIfError(SignatureChecker.validateECDSASignature(signer_, digest_, r_, vs_));
}
/**
* @dev Revert if the signature is invalid.
* @param signer_ The signer of the signature.
* @param digest_ The digest that was signed.
* @param v_ v of the signature.
* @param r_ r of the signature.
* @param s_ s of the signature.
*/
function _revertIfInvalidSignature(
address signer_,
bytes32 digest_,
uint8 v_,
bytes32 r_,
bytes32 s_
) internal pure {
_revertIfError(SignatureChecker.validateECDSASignature(signer_, digest_, v_, r_, s_));
}
/**
* @dev Revert if error.
* @param error_ The SignatureChecker Error enum.
*/
function _revertIfError(SignatureChecker.Error error_) private pure {
if (error_ == SignatureChecker.Error.NoError) return;
if (error_ == SignatureChecker.Error.InvalidSignature) revert InvalidSignature();
if (error_ == SignatureChecker.Error.InvalidSignatureLength) revert InvalidSignatureLength();
if (error_ == SignatureChecker.Error.InvalidSignatureS) revert InvalidSignatureS();
if (error_ == SignatureChecker.Error.InvalidSignatureV) revert InvalidSignatureV();
if (error_ == SignatureChecker.Error.SignerMismatch) revert SignerMismatch();
revert InvalidSignature();
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC20 } from "../../lib/common/src/interfaces/IERC20.sol";
import { ERC20Extended } from "../../lib/common/src/ERC20Extended.sol";
import { UIntMath } from "../../lib/common/src/libs/UIntMath.sol";
import { PureEpochs } from "../libs/PureEpochs.sol";
import { IERC5805 } from "./interfaces/IERC5805.sol";
import { IERC6372 } from "./interfaces/IERC6372.sol";
import { IEpochBasedVoteToken } from "./interfaces/IEpochBasedVoteToken.sol";
import { ERC5805 } from "./ERC5805.sol";
/**
* @title Extension for an ERC5805 token that uses epochs as its clock mode and delegation via IERC1271.
* @author M^0 Labs
*/
abstract contract EpochBasedVoteToken is IEpochBasedVoteToken, ERC5805, ERC20Extended {
/* ============ Structs ============ */
/// @dev A 32-byte struct containing a starting epoch and an address that is valid until the next AccountSnap.
struct AccountSnap {
uint16 startingEpoch;
address account;
}
/// @dev A 32-byte struct containing a starting epoch and an amount that is valid until the next AmountSnap.
struct AmountSnap {
uint16 startingEpoch;
uint240 amount;
}
/* ============ Variables ============ */
/// @dev Store the total supply per epoch.
AmountSnap[] internal _totalSupplies;
/// @dev Store the balance per epoch per account.
mapping(address account => AmountSnap[] balanceSnaps) internal _balances;
/// @dev Store the delegatee per epoch per account.
mapping(address account => AccountSnap[] delegateeSnaps) internal _delegatees;
/// @dev Store the voting power per epoch per delegatee.
mapping(address delegatee => AmountSnap[] votingPowerSnaps) internal _votingPowers;
/* ============ Constructor ============ */
/**
* @notice Constructs a new EpochBasedVoteToken contract.
* @param name_ The name of the token.
* @param symbol_ The symbol of the token.
* @param decimals_ The decimals of the token.
*/
constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20Extended(name_, symbol_, decimals_) {}
/* ============ Interactive Functions ============ */
/// @inheritdoc IEpochBasedVoteToken
function delegateBySig(
address account_,
address delegatee_,
uint256 nonce_,
uint256 expiry_,
bytes memory signature_
) external {
_revertIfExpired(expiry_);
_revertIfInvalidSignature(account_, _getDelegationDigest(delegatee_, nonce_, expiry_), signature_);
_checkAndIncrementNonce(account_, nonce_);
_delegate(account_, delegatee_);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IERC20
function balanceOf(address account_) external view returns (uint256) {
return _getBalance(account_, _clock());
}
/// @inheritdoc IEpochBasedVoteToken
function getDelegationDigest(address delegatee_, uint256 nonce_, uint256 expiry_) external view returns (bytes32) {
return _getDelegationDigest(delegatee_, nonce_, expiry_);
}
/// @inheritdoc IEpochBasedVoteToken
function pastBalanceOf(address account_, uint256 epoch_) external view returns (uint256) {
uint16 safeEpoch_ = UIntMath.safe16(epoch_);
_revertIfNotPastTimepoint(safeEpoch_); // Per EIP-5805, should revert if `epoch_` is not in the past.
return _getBalance(account_, safeEpoch_);
}
/// @inheritdoc IERC6372
function clock() external view returns (uint48 clock_) {
return _clock();
}
/// @inheritdoc IERC5805
function delegates(address account_) external view returns (address) {
return _getDelegatee(account_, _clock());
}
/// @inheritdoc IEpochBasedVoteToken
function pastDelegates(address account_, uint256 epoch_) external view returns (address) {
uint16 safeEpoch_ = UIntMath.safe16(epoch_);
_revertIfNotPastTimepoint(safeEpoch_); // Per EIP-5805, should revert if `epoch_` is not in the past.
return _getDelegatee(account_, safeEpoch_);
}
/// @inheritdoc IERC5805
function getVotes(address account_) external view returns (uint256) {
return _getVotes(account_, _clock());
}
/// @inheritdoc IERC5805
function getPastVotes(address account_, uint256 epoch_) external view returns (uint256) {
uint16 safeEpoch_ = UIntMath.safe16(epoch_);
_revertIfNotPastTimepoint(safeEpoch_); // Per EIP-5805, should revert if `epoch_` is not in the past.
return _getVotes(account_, safeEpoch_);
}
/// @inheritdoc IERC20
function totalSupply() external view returns (uint256) {
return _getTotalSupply(_clock());
}
/// @inheritdoc IEpochBasedVoteToken
function pastTotalSupply(uint256 epoch_) external view returns (uint256) {
uint16 safeEpoch_ = UIntMath.safe16(epoch_);
_revertIfNotPastTimepoint(safeEpoch_); // Per EIP-5805, should revert if `epoch_` is not in the past.
return _getTotalSupply(safeEpoch_);
}
/// @inheritdoc IERC6372
function CLOCK_MODE() external pure returns (string memory clockMode_) {
return PureEpochs.clockMode();
}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Add `amount_` to the balance of `account_`, using unchecked math.
* @param account_ The address of the account to add the balance to.
* @param amount_ The amount to add to the balance.
*/
function _addBalance(address account_, uint240 amount_) internal {
_updateBalance(account_, _addUnchecked, amount_); // Update balance using `_addUnchecked` operation.
}
/**
* @dev Add `amount_` to the total supply, using checked math.
* @param amount_ The amount to add to the total supply.
*/
function _addTotalSupply(uint240 amount_) internal {
_update(_totalSupplies, _add, amount_); // Update total supply using `_add` operation.
}
/**
* @dev Add `amount_` to the voting power of `account_`, using unchecked math.
* @param account_ The address of the account to add the voting power to.
* @param amount_ The amount to add to the voting power.
*/
function _addVotingPower(address account_, uint240 amount_) internal {
_updateVotingPower(account_, _addUnchecked, amount_); // Update voting power using `_addUnchecked` operation.
}
/**
* @dev Set a new delegatee for `delegator_`.
* @param delegator_ The address of the account delegating voting power.
* @param newDelegatee_ The address of the account receiving voting power.
*/
function _delegate(address delegator_, address newDelegatee_) internal virtual override {
// `delegatee_` will be `delegator_` (the default) if `delegatee_` was passed in as `address(0)`.
address newNonZeroDelegatee_ = _getDefaultIfZero(newDelegatee_, delegator_);
address oldDelegatee_ = _setDelegatee(delegator_, newNonZeroDelegatee_);
uint240 votingPower_ = _getBalance(delegator_, _clock());
if (votingPower_ == 0) return;
_removeVotingPower(oldDelegatee_, votingPower_);
_addVotingPower(newNonZeroDelegatee_, votingPower_);
}
/**
* @dev Mint `amount_` tokens to `recipient_`.
* @param recipient_ The address of the account to mint tokens to.
* @param amount_ The amount of tokens to mint.
*/
function _mint(address recipient_, uint256 amount_) internal virtual {
_revertIfInvalidRecipient(recipient_);
if (amount_ == 0) revert InsufficientAmount(amount_);
emit Transfer(address(0), recipient_, amount_);
uint240 safeAmount_ = UIntMath.safe240(amount_);
_addTotalSupply(safeAmount_); // Will revert on overflow.
_addBalance(recipient_, safeAmount_);
_addVotingPower(_getDelegatee(recipient_, _clock()), safeAmount_);
}
/**
* @dev Subtract `amount_` from the balance of `account_`, using checked math.
* @param account_ The address of the account to subtract the balance from.
* @param amount_ The amount to subtract from the balance.
*/
function _removeBalance(address account_, uint240 amount_) internal {
_updateBalance(account_, _sub, amount_); // Update balance using `_sub` operation.
}
/**
* @dev Subtract `amount_` of voting power from the balance of `account_`, using checked math.
* @param account_ The address of the account to subtract the voting power from.
* @param amount_ The amount of voting power to subtract.
*/
function _removeVotingPower(address account_, uint240 amount_) internal {
_updateVotingPower(account_, _sub, amount_); // Update voting power using `_sub` operation.
}
/**
* @dev Set a new delegatee for `delegator_`.
* @param delegator_ The address of the account delegating voting power.
* @param delegatee_ The address of the account receiving voting power.
* @return oldDelegatee_ The address of the previous delegatee of `delegator_`.
*/
function _setDelegatee(address delegator_, address delegatee_) internal returns (address oldDelegatee_) {
uint16 currentEpoch_ = _clock();
AccountSnap[] storage delegateeSnaps_ = _delegatees[delegator_];
uint256 length_ = delegateeSnaps_.length;
// If this will be the first AccountSnap, we can just push it onto the empty array.
if (length_ == 0) {
delegateeSnaps_.push(AccountSnap(currentEpoch_, delegatee_));
emit DelegateChanged(delegator_, delegator_, delegatee_);
return delegator_; // In this case, delegatee has always been the `delegator_` itself.
}
unchecked {
--length_;
}
AccountSnap storage latestDelegateeSnap_ = _unsafeAccess(delegateeSnaps_, length_);
oldDelegatee_ = latestDelegateeSnap_.account;
emit DelegateChanged(delegator_, oldDelegatee_, delegatee_);
// If the current epoch is greater than the last AccountSnap's startingEpoch, we can push a new
// AccountSnap onto the array, else we can just update the last AccountSnap's account.
if (currentEpoch_ > latestDelegateeSnap_.startingEpoch) {
delegateeSnaps_.push(AccountSnap(currentEpoch_, delegatee_));
} else {
latestDelegateeSnap_.account = delegatee_;
}
}
/**
* @dev Transfer `amount_` tokens from `sender_` to `recipient_`.
* @param sender_ The address of the account to transfer tokens from.
* @param recipient_ The address of the account to transfer tokens to.
* @param amount_ The amount of tokens to transfer.
*/
function _transfer(address sender_, address recipient_, uint256 amount_) internal virtual override {
_revertIfInvalidRecipient(recipient_);
emit Transfer(sender_, recipient_, amount_);
uint240 safeAmount_ = UIntMath.safe240(amount_);
uint16 currentEpoch_ = _clock();
_removeBalance(sender_, safeAmount_); // Will revert on underflow.
_removeVotingPower(_getDelegatee(sender_, currentEpoch_), safeAmount_); // Will revert on underflow.
_addBalance(recipient_, safeAmount_);
_addVotingPower(_getDelegatee(recipient_, currentEpoch_), safeAmount_);
}
/**
* @dev Update a storage AmountSnap by `amount_` given `operation_`.
* @param amountSnaps_ The storage pointer to an AmountSnap array to update.
* @param operation_ The operation to perform on the old and new amounts.
* @param amount_ The amount to update the Snap by.
* @return oldAmount_ The previous latest amount of the Snap array.
* @return newAmount_ The new latest amount of the Snap array.
*/
function _update(
AmountSnap[] storage amountSnaps_,
function(uint240, uint240) internal pure returns (uint240) operation_,
uint240 amount_
) internal returns (uint240 oldAmount_, uint240 newAmount_) {
uint16 currentEpoch_ = _clock();
uint256 length_ = amountSnaps_.length;
// If this will be the first AmountSnap, we can just push it onto the empty array.
if (length_ == 0) {
// NOTE: `operation_(0, amount_)` is necessary for almost all operations other than setting or adding.
amountSnaps_.push(AmountSnap(currentEpoch_, operation_(0, amount_)));
return (0, amount_); // In this case, the old amount was 0.
}
unchecked {
--length_;
}
AmountSnap storage lastAmountSnap_ = _unsafeAccess(amountSnaps_, length_);
newAmount_ = operation_(oldAmount_ = lastAmountSnap_.amount, amount_);
// If the current epoch is greater than the last AmountSnap's startingEpoch, we can push a new
// AmountSnap onto the array, else we can just update the last AmountSnap's amount.
if (currentEpoch_ > lastAmountSnap_.startingEpoch) {
amountSnaps_.push(AmountSnap(currentEpoch_, newAmount_));
} else {
lastAmountSnap_.amount = newAmount_;
}
}
/**
* @dev Update the balance of `account_` by `amount_` given `operation_`.
* @param account_ The address of the account to update the balance of.
* @param operation_ The operation to perform on the old and new amounts.
* @param amount_ The amount to update the balance by.
*/
function _updateBalance(
address account_,
function(uint240, uint240) internal pure returns (uint240) operation_,
uint240 amount_
) internal {
_update(_balances[account_], operation_, amount_);
}
/**
* @dev Update the voting power of `delegatee_` by `amount_` given `operation_`.
* @param delegatee_ The address of the account to update the voting power of.
* @param operation_ The operation to perform on the old and new amounts.
* @param amount_ The amount to update the voting power by.
*/
function _updateVotingPower(
address delegatee_,
function(uint240, uint240) internal pure returns (uint240) operation_,
uint240 amount_
) internal {
(uint240 oldAmount_, uint240 newAmount_) = _update(_votingPowers[delegatee_], operation_, amount_);
emit DelegateVotesChanged(delegatee_, oldAmount_, newAmount_);
}
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns the current timepoint according to the mode the contract is operating on.
* @return Current timepoint.
*/
function _clock() internal view returns (uint16) {
return PureEpochs.currentEpoch();
}
/**
* @dev Get the balance of `account_` at `epoch_`.
* @param account_ The address of the account to get the balance of.
* @param epoch_ The epoch to get the balance at.
* @return The balance of `account_` at `epoch_`.
*/
function _getBalance(address account_, uint16 epoch_) internal view virtual returns (uint240) {
return _getValueAt(_balances[account_], epoch_);
}
/**
* @dev Get the delegatee of `account_` at `epoch_`.
* @dev The delegatee is the account itself (the default) if no retrieved delegatee was found.
* @param account_ The address of the account to get the delegatee of.
* @param epoch_ The epoch to get the delegatee at.
* @return The delegatee of `account_` at `epoch_`.
*/
function _getDelegatee(address account_, uint256 epoch_) internal view virtual returns (address) {
if (epoch_ == 0) revert EpochZero();
AccountSnap[] storage delegateeSnaps_ = _delegatees[account_];
uint256 index_ = delegateeSnaps_.length; // NOTE: `index_` starts out as length, and would be out of bounds.
// Keep going back until we find the first snap with a startingEpoch less than or equal to `epoch_`. This snap
// has the account applicable to `epoch_`. If we exhaust the array, then the delegatee is an account itself.
while (index_ > 0) {
unchecked {
AccountSnap storage accountSnap_ = _unsafeAccess(delegateeSnaps_, --index_);
if (accountSnap_.startingEpoch <= epoch_) return accountSnap_.account;
}
}
return account_;
}
/**
* @dev Get the total supply at `epoch_`.
* @param epoch_ The epoch to get the total supply at.
* @return The total supply at `epoch_`.
*/
function _getTotalSupply(uint16 epoch_) internal view virtual returns (uint240) {
return _getValueAt(_totalSupplies, epoch_);
}
/**
* @dev Get the value of an AmountSnap array at a given epoch.
* @param amountSnaps_ The array of AmountSnaps to get the value of.
* @param epoch_ The epoch to get the value at.
* @return The value of the AmountSnap array at `epoch_`.
*/
function _getValueAt(AmountSnap[] storage amountSnaps_, uint16 epoch_) internal view returns (uint240) {
if (epoch_ == 0) revert EpochZero();
uint256 index_ = amountSnaps_.length; // NOTE: `index_` starts out as length, and would be out of bounds.
// Keep going back until we find the first snap with a startingEpoch less than or equal to `epoch_`. This snap
// has the amount applicable to `epoch_`. If we exhaust the array, then the amount is 0.
while (index_ > 0) {
unchecked {
AmountSnap storage amountSnap_ = _unsafeAccess(amountSnaps_, --index_);
if (amountSnap_.startingEpoch <= epoch_) return amountSnap_.amount;
}
}
return 0;
}
/**
* @dev The votes of `account_` at `epoch_`.
* @param account_ The address of the account to get the votes of.
* @param epoch_ The epoch to get the votes at.
* @return The votes of `account_` at `epoch_`.
*/
function _getVotes(address account_, uint16 epoch_) internal view virtual returns (uint240) {
return _getValueAt(_votingPowers[account_], epoch_);
}
/**
* @dev Revert if `epoch_` is not in the past.
* @param epoch_ The epoch to check.
*/
function _revertIfNotPastTimepoint(uint16 epoch_) internal view {
uint16 currentEpoch_ = _clock();
if (epoch_ >= currentEpoch_) revert NotPastTimepoint(epoch_, currentEpoch_);
}
/**
* @dev Reverts if the recipient of a `mint` or `transfer` is address(0).
* @param recipient_ Address of the recipient to check.
*/
function _revertIfInvalidRecipient(address recipient_) internal pure {
if (recipient_ == address(0)) revert InvalidRecipient(recipient_);
}
/**
* @dev Add `b_` to `a_`, using checked math.
* @param a_ The amount to add to.
* @param b_ The amount to add.
* @return The sum of `a_` and `b_`.
*/
function _add(uint240 a_, uint240 b_) internal pure returns (uint240) {
return a_ + b_;
}
/**
* @dev Add `b_` to `a_`, using unchecked math.
* @param a_ The amount to add to.
* @param b_ The amount to add.
* @return The sum of `a_` and `b_`.
*/
function _addUnchecked(uint240 a_, uint240 b_) internal pure returns (uint240) {
unchecked {
return a_ + b_;
}
}
/**
* @dev Return `default_` if `input_` is equal to address(0), else return `input_`.
* @param input_ The input address.
* @param default_ The default address.
* @return The input address if not equal to the zero address, else the default address.
*/
function _getDefaultIfZero(address input_, address default_) internal pure returns (address) {
return input_ == address(0) ? default_ : input_;
}
/**
* @dev Subtract `b_` from `a_`, using checked math.
* @param a_ The amount to subtract from.
* @param b_ The amount to subtract.
* @return The difference of `a_` and `b_`.
*/
function _sub(uint240 a_, uint240 b_) internal pure returns (uint240) {
return a_ - b_;
}
/**
* @dev Returns the AmountSnap in an array at a given index without doing bounds checking.
* @param amountSnaps_ The array of AmountSnaps to parse.
* @param index_ The index of the AmountSnap to return.
* @return amountSnap_ The AmountSnap at `index_`.
*/
function _unsafeAccess(
AmountSnap[] storage amountSnaps_,
uint256 index_
) internal pure returns (AmountSnap storage amountSnap_) {
assembly {
mstore(0, amountSnaps_.slot)
amountSnap_.slot := add(keccak256(0, 0x20), index_)
}
}
/**
* @dev Returns the AccountSnap in an array at a given index without doing bounds checking.
* @param accountSnaps_ The array of AccountSnaps to parse.
* @param index_ The index of the AccountSnap to return.
* @return accountSnap_ The AccountSnap at `index_`.
*/
function _unsafeAccess(
AccountSnap[] storage accountSnaps_,
uint256 index_
) internal pure returns (AccountSnap storage accountSnap_) {
assembly {
mstore(0, accountSnaps_.slot)
accountSnap_.slot := add(keccak256(0, 0x20), index_)
}
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
/**
* @title A Deterministic deployer of contracts using CREATE.
* @author M^0 Labs
*/
interface IDeployer {
/// @notice Returns the nonce used to pre deterministically compute the address of the next deployed contract.
function nonce() external view returns (uint256);
/// @notice Returns the address of the last contract deployed by this contract.
function lastDeploy() external view returns (address);
/// @notice Returns the address of the next contract this contract will deploy.
function nextDeploy() external view returns (address);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
/**
* @title Standard Signature Validation Method for Contracts via EIP-1271.
* @author M^0 Labs
* @dev The interface as defined by EIP-1271: https://eips.ethereum.org/EIPS/eip-1271
*/
interface IERC1271 {
/**
* @dev Returns a specific magic value if the provided signature is valid for the provided digest.
* @param digest Hash of the data purported to have been signed.
* @param signature Signature byte array associated with the digest.
* @return magicValue Magic value 0x1626ba7e if the signature is valid.
*/
function isValidSignature(bytes32 digest, bytes memory signature) external view returns (bytes4 magicValue);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
/**
* @title ERC20 Token Standard.
* @author M^0 Labs
* @dev The interface as defined by EIP-20: https://eips.ethereum.org/EIPS/eip-20
*/
interface IERC20 {
/* ============ Events ============ */
/**
* @notice Emitted when `spender` has been approved for `amount` of the token balance of `account`.
* @param account The address of the account.
* @param spender The address of the spender being approved for the allowance.
* @param amount The amount of the allowance being approved.
*/
event Approval(address indexed account, address indexed spender, uint256 amount);
/**
* @notice Emitted when `amount` tokens is transferred from `sender` to `recipient`.
* @param sender The address of the sender who's token balance is decremented.
* @param recipient The address of the recipient who's token balance is incremented.
* @param amount The amount of tokens being transferred.
*/
event Transfer(address indexed sender, address indexed recipient, uint256 amount);
/* ============ Interactive Functions ============ */
/**
* @notice Allows a calling account to approve `spender` to spend up to `amount` of its token balance.
* @dev MUST emit an `Approval` event.
* @param spender The address of the account being allowed to spend up to the allowed amount.
* @param amount The amount of the allowance being approved.
* @return Whether or not the approval was successful.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @notice Allows a calling account to transfer `amount` tokens to `recipient`.
* @param recipient The address of the recipient who's token balance will be incremented.
* @param amount The amount of tokens being transferred.
* @return Whether or not the transfer was successful.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @notice Allows a calling account to transfer `amount` tokens from `sender`, with allowance, to a `recipient`.
* @param sender The address of the sender who's token balance will be decremented.
* @param recipient The address of the recipient who's token balance will be incremented.
* @param amount The amount of tokens being transferred.
* @return Whether or not the transfer was successful.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/* ============ View/Pure Functions ============ */
/**
* @notice Returns the allowance `spender` is allowed to spend on behalf of `account`.
* @param account The address of the account who's token balance `spender` is allowed to spend.
* @param spender The address of an account allowed to spend on behalf of `account`.
* @return The amount `spender` can spend on behalf of `account`.
*/
function allowance(address account, address spender) external view returns (uint256);
/**
* @notice Returns the token balance of `account`.
* @param account The address of some account.
* @return The token balance of `account`.
*/
function balanceOf(address account) external view returns (uint256);
/// @notice Returns the number of decimals UIs should assume all amounts have.
function decimals() external view returns (uint8);
/// @notice Returns the name of the contract/token.
function name() external view returns (string memory);
/// @notice Returns the symbol of the token.
function symbol() external view returns (string memory);
/// @notice Returns the current total supply of the token.
function totalSupply() external view returns (uint256);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC20 } from "./IERC20.sol";
import { IERC3009 } from "./IERC3009.sol";
/**
* @title An ERC20 token extended with EIP-2612 permits for signed approvals (via EIP-712
* and with EIP-1271 compatibility), and extended with EIP-3009 transfer with authorization (via EIP-712).
* @author M^0 Labs
* @dev The additional interface as defined by EIP-2612: https://eips.ethereum.org/EIPS/eip-2612
*/
interface IERC20Extended is IERC20, IERC3009 {
/* ============ Custom Errors ============ */
/**
* @notice Revert message when spender's allowance is not sufficient.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @notice Revert message emitted when the transferred amount is insufficient.
* @param amount Amount transferred.
*/
error InsufficientAmount(uint256 amount);
/**
* @notice Revert message emitted when the recipient of a token is invalid.
* @param recipient Address of the invalid recipient.
*/
error InvalidRecipient(address recipient);
/* ============ Interactive Functions ============ */
/**
* @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
* @param owner The address of the account who's token balance is being approved to be spent by `spender`.
* @param spender The address of an account allowed to spend on behalf of `owner`.
* @param value The amount of the allowance being approved.
* @param deadline The last block number where the signature is still valid.
* @param v An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param r An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
* @param s An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
* @param owner The address of the account who's token balance is being approved to be spent by `spender`.
* @param spender The address of an account allowed to spend on behalf of `owner`.
* @param value The amount of the allowance being approved.
* @param deadline The last block number where the signature is still valid.
* @param signature An arbitrary signature (EIP-712).
*/
function permit(address owner, address spender, uint256 value, uint256 deadline, bytes memory signature) external;
/* ============ View/Pure Functions ============ */
/// @notice Returns the EIP712 typehash used in the encoding of the digest for the permit function.
function PERMIT_TYPEHASH() external view returns (bytes32);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IStatefulERC712 } from "./IStatefulERC712.sol";
/**
* @title Transfer via signed authorization following EIP-3009 standard.
* @author M^0 Labs
* @dev The interface as defined by EIP-3009: https://eips.ethereum.org/EIPS/eip-3009
*/
interface IERC3009 is IStatefulERC712 {
/* ============ Events ============ */
/**
* @notice Emitted when an authorization has been canceled.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the canceled authorization.
*/
event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce);
/**
* @notice Emitted when an authorization has been used.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the used authorization.
*/
event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
/* ============ Custom Errors ============ */
/**
* @notice Emitted when an authorization has already been used.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the used authorization.
*/
error AuthorizationAlreadyUsed(address authorizer, bytes32 nonce);
/**
* @notice Emitted when an authorization is expired.
* @param timestamp Timestamp at which the transaction was submitted.
* @param validBefore Timestamp before which the authorization would have been valid.
*/
error AuthorizationExpired(uint256 timestamp, uint256 validBefore);
/**
* @notice Emitted when an authorization is not yet valid.
* @param timestamp Timestamp at which the transaction was submitted.
* @param validAfter Timestamp after which the authorization will be valid.
*/
error AuthorizationNotYetValid(uint256 timestamp, uint256 validAfter);
/**
* @notice Emitted when the caller of `receiveWithAuthorization` is not the payee.
* @param caller Caller's address.
* @param payee Payee's address.
*/
error CallerMustBePayee(address caller, address payee);
/* ============ Interactive Functions ============ */
/**
* @notice Execute a transfer with a signed authorization.
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
*/
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes memory signature
) external;
/**
* @notice Execute a transfer with a signed authorization.
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
*/
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes32 r,
bytes32 vs
) external;
/**
* @notice Execute a transfer with a signed authorization.
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param v v of the signature.
* @param r r of the signature.
* @param s s of the signature.
*/
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Receive a transfer with a signed authorization from the payer.
* @dev This has an additional check to ensure that the payee's address matches
* the caller of this function to prevent front-running attacks.
* (See security considerations)
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
*/
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes memory signature
) external;
/**
* @notice Receive a transfer with a signed authorization from the payer.
* @dev This has an additional check to ensure that the payee's address matches
* the caller of this function to prevent front-running attacks.
* (See security considerations)
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
*/
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes32 r,
bytes32 vs
) external;
/**
* @notice Receive a transfer with a signed authorization from the payer.
* @dev This has an additional check to ensure that the payee's address matches
* the caller of this function to prevent front-running attacks.
* (See security considerations)
* @param from Payer's address (Authorizer).
* @param to Payee's address.
* @param value Amount to be transferred.
* @param validAfter The time after which this is valid (unix time).
* @param validBefore The time before which this is valid (unix time).
* @param nonce Unique nonce.
* @param v v of the signature.
* @param r r of the signature.
* @param s s of the signature.
*/
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Attempt to cancel an authorization.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the authorization.
* @param signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
*/
function cancelAuthorization(address authorizer, bytes32 nonce, bytes memory signature) external;
/**
* @notice Attempt to cancel an authorization.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the authorization.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
*/
function cancelAuthorization(address authorizer, bytes32 nonce, bytes32 r, bytes32 vs) external;
/**
* @notice Attempt to cancel an authorization.
* @param authorizer Authorizer's address.
* @param nonce Nonce of the authorization.
* @param v v of the signature.
* @param r r of the signature.
* @param s s of the signature.
*/
function cancelAuthorization(address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external;
/* ============ View/Pure Functions ============ */
/**
* @notice Returns the state of an authorization.
* @dev Nonces are randomly generated 32-byte data unique to the authorizer's address
* @param authorizer Authorizer's address.
* @param nonce Nonce of the authorization.
* @return True if the nonce is used.
*/
function authorizationState(address authorizer, bytes32 nonce) external view returns (bool);
/// @notice Returns `transferWithAuthorization` typehash.
function TRANSFER_WITH_AUTHORIZATION_TYPEHASH() external view returns (bytes32);
/// @notice Returns `receiveWithAuthorization` typehash.
function RECEIVE_WITH_AUTHORIZATION_TYPEHASH() external view returns (bytes32);
/// @notice Returns `cancelAuthorization` typehash.
function CANCEL_AUTHORIZATION_TYPEHASH() external view returns (bytes32);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IStatefulERC712 } from "../../../lib/common/src/interfaces/IStatefulERC712.sol";
import { IERC6372 } from "./IERC6372.sol";
/**
* @title Voting with voting weight tracking and delegation support.
* @author M^0 Labs
* @dev The interface as defined by EIP-5805: https://eips.ethereum.org/EIPS/eip-5805
*/
interface IERC5805 is IStatefulERC712, IERC6372 {
/* ============ Events ============ */
/**
* @notice Emitted when `delegator` changes its voting power delegation from `fromDelegatee` to `toDelegatee`.
* @param delegator The address of the account changing its voting power delegation.
* @param fromDelegatee The previous account the voting power of `delegator` was delegated to.
* @param toDelegatee The new account the voting power of `delegator` is delegated to.
*/
event DelegateChanged(address indexed delegator, address indexed fromDelegatee, address indexed toDelegatee);
/**
* @notice Emitted when the available voting power of `delegatee` changes from `previousBalance` to `newBalance`.
* @param delegatee The address of the account whose voting power is changed.
* @param previousBalance The previous voting power of `delegatee`.
* @param newBalance The new voting power of `delegatee`.
*/
event DelegateVotesChanged(address indexed delegatee, uint256 previousBalance, uint256 newBalance);
/* ============ Custom Errors ============ */
/**
* @notice Revert message when a query for past values is for a timepoint greater or equal to the current clock.
* @param timepoint The timepoint being queried.
* @param clock The current timepoint.
*/
error NotPastTimepoint(uint48 timepoint, uint48 clock);
/* ============ Interactive Functions ============ */
/**
* @notice Allows a calling account to change its voting power delegation to `delegatee`.
* @param delegatee The address of the account the caller's voting power will be delegated to.
*/
function delegate(address delegatee) external;
/**
* @notice Changes the signing account's voting power delegation to `delegatee`.
* @param delegatee The address of the account the signing account's voting power will be delegated to.
* @param nonce The nonce of the account delegating.
* @param expiry The timestamp until which the signature is still valid.
* @param v A signature parameter.
* @param r A signature parameter.
* @param s A signature parameter.
*/
function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
/* ============ View/Pure Functions ============ */
/// @notice Returns the EIP712 typehash used in the encoding of the digest for the delegateBySig function.
function DELEGATION_TYPEHASH() external view returns (bytes32);
/**
* @notice Returns the delegatee the voting power of `account` is delegated to.
* @param account The address of the account that can delegate its voting power.
* @return The address of the account the voting power of `account` will be delegated to.
*/
function delegates(address account) external view returns (address);
/**
* @notice Returns the total voting power of `account` at a past clock value `timepoint`.
* @param account The address of some account.
* @param timepoint The point in time, according to the clock mode the contract is operating on.
* @return The total voting power of `account` at clock value `timepoint`.
*/
function getPastVotes(address account, uint256 timepoint) external view returns (uint256);
/**
* @notice Returns the total voting power of `account`.
* @param account The address of some account.
* @return The total voting power of `account`.
*/
function getVotes(address account) external view returns (uint256);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
/**
* @title Contract clock properties.
* @author M^0 Labs
* @dev The interface as defined by EIP-6372: https://eips.ethereum.org/EIPS/eip-6372
*/
interface IERC6372 {
/// @notice Returns a machine-readable string description of the clock the contract is operating on.
function CLOCK_MODE() external view returns (string memory);
/// @notice Returns the current timepoint according to the mode the contract is operating on.
function clock() external view returns (uint48);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
/**
* @title Typed structured data hashing and signing via EIP-712.
* @author M^0 Labs
* @dev The interface as defined by EIP-712: https://eips.ethereum.org/EIPS/eip-712
*/
interface IERC712 {
/* ============ Custom Errors ============ */
/// @notice Revert message when an invalid signature is detected.
error InvalidSignature();
/// @notice Revert message when a signature with invalid length is detected.
error InvalidSignatureLength();
/// @notice Revert message when the S portion of a signature is invalid.
error InvalidSignatureS();
/// @notice Revert message when the V portion of a signature is invalid.
error InvalidSignatureV();
/**
* @notice Revert message when a signature is being used beyond its deadline (i.e. expiry).
* @param deadline The deadline of the signature.
* @param timestamp The current timestamp.
*/
error SignatureExpired(uint256 deadline, uint256 timestamp);
/// @notice Revert message when a recovered signer does not match the account being purported to have signed.
error SignerMismatch();
/* ============ View/Pure Functions ============ */
/// @notice Returns the EIP712 domain separator used in the encoding of a signed digest.
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC712 } from "./IERC712.sol";
/**
* @title EIP-712 extended by EIP-5267.
* @author M^0 Labs
* @dev The additional interface as defined by EIP-5267: https://eips.ethereum.org/EIPS/eip-5267
*/
interface IERC712Extended is IERC712 {
/* ============ Events ============ */
/// @notice MAY be emitted to signal that the domain could have changed.
event EIP712DomainChanged();
/* ============ View/Pure Functions ============ */
/// @notice Returns the fields and values that describe the domain separator used by this contract for EIP-712.
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC20Extended } from "../../../lib/common/src/interfaces/IERC20Extended.sol";
import { IERC5805 } from "./IERC5805.sol";
/**
* @title Extension for an ERC5805 token that uses epochs as its clock mode and delegation via IERC1271.
* @author M^0 Labs
*/
interface IEpochBasedVoteToken is IERC5805, IERC20Extended {
/* ============ Custom Errors ============ */
/// @notice Revert message when the provided epoch is zero.
error EpochZero();
/* ============ Interactive Functions ============ */
/**
* @notice Changes the voting power delegation for `account` to `delegatee`.
* @param account The purported address of the signing account.
* @param delegatee The address the voting power of `account` will be delegated to.
* @param nonce The nonce used for the signature.
* @param expiry The timestamp until which the signature is still valid.
* @param signature A byte array signature.
*/
function delegateBySig(
address account,
address delegatee,
uint256 nonce,
uint256 expiry,
bytes memory signature
) external;
/* ============ View/Pure Functions ============ */
/**
* @notice Returns the digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
* @param delegatee The address of the delegatee to delegate to.
* @param nonce The nonce of the account delegating.
* @param expiry The timestamp until which the signature is still valid.
* @return The digest to be signed.
*/
function getDelegationDigest(address delegatee, uint256 nonce, uint256 expiry) external view returns (bytes32);
/**
* @notice Returns the token balance of `account` at a past clock value `epoch`.
* @param account The address of some account.
* @param epoch The epoch number as a clock value.
* @return The token balance `account` at `epoch`.
*/
function pastBalanceOf(address account, uint256 epoch) external view returns (uint256);
/**
* @notice Returns the delegatee of `account` at a past clock value `epoch`.
* @param account The address of some account.
* @param epoch The epoch number as a clock value.
* @return The delegatee of the voting power of `account` at `epoch`.
*/
function pastDelegates(address account, uint256 epoch) external view returns (address);
/**
* @notice Returns the total token supply at a past clock value `epoch`.
* @param epoch The epoch number as a clock value.
* @return The total token supply at `epoch`.
*/
function pastTotalSupply(uint256 epoch) external view returns (uint256);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IDeployer } from "./IDeployer.sol";
/**
* @title A Deterministic deployer of Standard Governor contracts using CREATE.
* @author M^0 Labs
*/
interface IStandardGovernorDeployer is IDeployer {
/* ============ Custom Errors ============ */
/// @notice Revert message when the Registrar specified in the constructor is address(0).
error InvalidRegistrarAddress();
/// @notice Revert message when the Vault specified in the constructor is address(0).
error InvalidVaultAddress();
/// @notice Revert message when the Zero Governor specified in the constructor is address(0).
error InvalidZeroGovernorAddress();
/// @notice Revert message when the Zero Token specified in the constructor is address(0).
error InvalidZeroTokenAddress();
/// @notice Revert message when the caller is not the Zero Governor.
error NotZeroGovernor();
/* ============ Interactive Functions ============ */
/**
* @notice Deploys a new instance of a Standard Governor.
* @param powerToken The address of some Power Token that will be used by voters.
* @param emergencyGovernor The address of some Emergency Governor.
* @param cashToken The address of some Cash Token.
* @param proposalFee The proposal fee required to create proposals.
* @param maxTotalZeroRewardPerActiveEpoch The maximum amount of Zero Token rewarded per active epoch.
* @return The address of the deployed Standard Governor.
*/
function deploy(
address powerToken,
address emergencyGovernor,
address cashToken,
uint256 proposalFee,
uint256 maxTotalZeroRewardPerActiveEpoch
) external returns (address);
/* ============ View/Pure Functions ============ */
/// @notice Returns the address of the Registrar.
function registrar() external view returns (address);
/// @notice Returns the address of the Vault.
function vault() external view returns (address);
/// @notice Returns the address of the Zero Governor.
function zeroGovernor() external view returns (address);
/// @notice Returns the address of the Zero Token.
function zeroToken() external view returns (address);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC712Extended } from "./IERC712Extended.sol";
/**
* @title Stateful Extension for EIP-712 typed structured data hashing and signing with nonces.
* @author M^0 Labs
*/
interface IStatefulERC712 is IERC712Extended {
/* ============ Custom Errors ============ */
/**
* @notice Revert message when a signing account's nonce is not the expected current nonce.
* @param nonce The nonce used in the signature.
* @param expectedNonce The expected nonce to be used in a signature by the signing account.
*/
error InvalidAccountNonce(uint256 nonce, uint256 expectedNonce);
/* ============ View/Pure Functions ============ */
/**
* @notice Returns the next nonce to be used in a signature by `account`.
* @param account The address of some account.
* @return nonce The next nonce to be used in a signature by `account`.
*/
function nonces(address account) external view returns (uint256 nonce);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IEpochBasedVoteToken } from "../abstract/interfaces/IEpochBasedVoteToken.sol";
/**
* @title An instance of an EpochBasedVoteToken delegating minting control to a Standard Governor,
* and enabling range queries for past balances, voting powers, delegations, and total supplies.
* @author M^0 Labs
*/
interface IZeroToken is IEpochBasedVoteToken {
/* ============ Custom Errors ============ */
/**
* @notice Revert message when the length of some accounts array does not equal the length of some balances array.
* @param accountsLength The length of the accounts array.
* @param balancesLength The length of the balances array.
*/
error ArrayLengthMismatch(uint256 accountsLength, uint256 balancesLength);
/// @notice Revert message when the Standard Governor Deployer specified in the constructor is address(0).
error InvalidStandardGovernorDeployerAddress();
/// @notice Revert message when the caller is not the Standard Governor.
error NotStandardGovernor();
/// @notice Revert message when the start of an inclusive range query is larger than the end.
error StartEpochAfterEndEpoch();
/* ============ Interactive Functions ============ */
/**
* @notice Mints `amount` token to `recipient`.
* @param recipient The address of the account receiving minted token.
* @param amount The amount of token to mint.
*/
function mint(address recipient, uint256 amount) external;
/* ============ View/Pure Functions ============ */
/**
* @notice Returns an array of token balances of `account`
* between `startEpoch` and `endEpoch` past inclusive clocks.
* @param account The address of some account.
* @param startEpoch The starting epoch number as a clock value.
* @param endEpoch The ending epoch number as a clock value.
* @return An array of token balances, each relating to an epoch in the inclusive range.
*/
function pastBalancesOf(
address account,
uint256 startEpoch,
uint256 endEpoch
) external view returns (uint256[] memory);
/**
* @notice Returns an array of total token supplies between `startEpoch` and `endEpoch` clocks inclusively.
* @param startEpoch The starting epoch number as a clock value.
* @param endEpoch The ending epoch number as a clock value.
* @return An array of total supplies, each relating to an epoch in the inclusive range.
*/
function pastTotalSupplies(uint256 startEpoch, uint256 endEpoch) external view returns (uint256[] memory);
/// @notice Returns the address of the Standard Governor.
function standardGovernor() external view returns (address);
/// @notice Returns the address of the Standard Governor Deployer.
function standardGovernorDeployer() external view returns (address);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
/**
* @notice Defines epochs as periods away from STARTING_TIMESTAMP timestamp.
* @author M^0 Labs
* @dev Provides a `uint16` epoch clock value.
*/
library PureEpochs {
/* ============ Variables ============ */
/// @notice The timestamp of the start of Epoch 1.
uint40 internal constant STARTING_TIMESTAMP = 1713099600;
/// @notice The approximate target of seconds an epoch should endure.
uint40 internal constant EPOCH_PERIOD = 1296000;
/* ============ Internal View/Pure Functions ============ */
/// @dev Returns the current epoch number.
function currentEpoch() internal view returns (uint16) {
return uint16(((block.timestamp - STARTING_TIMESTAMP) / EPOCH_PERIOD) + 1);
}
/// @dev Returns the remaining time in the current epoch.
function timeRemainingInCurrentEpoch() internal view returns (uint40) {
return STARTING_TIMESTAMP + (currentEpoch() * EPOCH_PERIOD) - uint40(block.timestamp);
}
function clockMode() internal pure returns (string memory) {
return "mode=epoch&epochUnderlyingSource=blockTimestamp&epochStartingTimestamp=1713099600&epochPeriod=1296000";
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC1271 } from "../interfaces/IERC1271.sol";
/**
* @title A library to handle ECDSA/secp256k1 and ERC1271 signatures, individually or in arbitrarily in combination.
* @author M^0 Labs
*/
library SignatureChecker {
/* ============ Enums ============ */
/**
* @notice An enum representing the possible errors that can be emitted during signature validation.
* @param NoError No error occurred during signature validation.
* @param InvalidSignature The signature is invalid.
* @param InvalidSignatureLength The signature length is invalid.
* @param InvalidSignatureS The signature parameter S is invalid.
* @param InvalidSignatureV The signature parameter V is invalid.
* @param SignerMismatch The signer does not match the recovered signer.
*/
enum Error {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV,
SignerMismatch
}
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns whether a signature is valid (ECDSA/secp256k1 or ERC1271) for a signer and digest.
* @dev Signatures must not be used as unique identifiers since the `ecrecover` EVM opcode
* allows for malleable (non-unique) signatures.
* See https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-4h98-2769-gh6h
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array signature.
* @return Whether the signature is valid or not.
*/
function isValidSignature(address signer, bytes32 digest, bytes memory signature) internal view returns (bool) {
return isValidECDSASignature(signer, digest, signature) || isValidERC1271Signature(signer, digest, signature);
}
/**
* @dev Returns whether an ERC1271 signature is valid for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array ERC1271 signature.
* @return Whether the signature is valid or not.
*/
function isValidERC1271Signature(
address signer,
bytes32 digest,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (digest, signature))
);
return
success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector);
}
/**
* @dev Decodes an ECDSA/secp256k1 signature from a byte array to standard v, r, and s parameters.
* @param signature A byte array ECDSA/secp256k1 signature.
* @return v An ECDSA/secp256k1 signature parameter.
* @return r An ECDSA/secp256k1 signature parameter.
* @return s An ECDSA/secp256k1 signature parameter.
*/
function decodeECDSASignature(bytes memory signature) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
// ecrecover takes the signature parameters, and they can be decoded using assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
}
/**
* @dev Decodes an ECDSA/secp256k1 short signature as defined by EIP2098
* from a byte array to standard v, r, and s parameters.
* @param signature A byte array ECDSA/secp256k1 short signature.
* @return r An ECDSA/secp256k1 signature parameter.
* @return vs An ECDSA/secp256k1 short signature parameter.
*/
function decodeShortECDSASignature(bytes memory signature) internal pure returns (bytes32 r, bytes32 vs) {
// ecrecover takes the signature parameters, and they can be decoded using assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
}
/**
* @dev Returns whether an ECDSA/secp256k1 signature is valid for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
* @return Whether the signature is valid or not.
*/
function isValidECDSASignature(
address signer,
bytes32 digest,
bytes memory signature
) internal pure returns (bool) {
if (signature.length == 64) {
(bytes32 r, bytes32 vs) = decodeShortECDSASignature(signature);
return isValidECDSASignature(signer, digest, r, vs);
}
return validateECDSASignature(signer, digest, signature) == Error.NoError;
}
/**
* @dev Returns whether an ECDSA/secp256k1 short signature is valid for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
* @return Whether the signature is valid or not.
*/
function isValidECDSASignature(address signer, bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (bool) {
return validateECDSASignature(signer, digest, r, vs) == Error.NoError;
}
/**
* @dev Returns the signer of an ECDSA/secp256k1 signature for some digest.
* @param digest The hash of the data that was signed.
* @param signature A byte array ECDSA/secp256k1 signature.
* @return An error, if any, that occurred during the signer recovery.
* @return The address of the account recovered form the signature (0 if error).
*/
function recoverECDSASigner(bytes32 digest, bytes memory signature) internal pure returns (Error, address) {
if (signature.length != 65) return (Error.InvalidSignatureLength, address(0));
(uint8 v, bytes32 r, bytes32 s) = decodeECDSASignature(signature);
return recoverECDSASigner(digest, v, r, s);
}
/**
* @dev Returns the signer of an ECDSA/secp256k1 short signature for some digest.
* @dev See https://eips.ethereum.org/EIPS/eip-2098
* @param digest The hash of the data that was signed.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
* @return An error, if any, that occurred during the signer recovery.
* @return The address of the account recovered form the signature (0 if error).
*/
function recoverECDSASigner(bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (Error, address) {
unchecked {
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
return recoverECDSASigner(digest, v, r, s);
}
}
/**
* @dev Returns the signer of an ECDSA/secp256k1 signature for some digest.
* @param digest The hash of the data that was signed.
* @param v An ECDSA/secp256k1 signature parameter.
* @param r An ECDSA/secp256k1 signature parameter.
* @param s An ECDSA/secp256k1 signature parameter.
* @return An error, if any, that occurred during the signer recovery.
* @return signer The address of the account recovered form the signature (0 if error).
*/
function recoverECDSASigner(
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (Error, address signer) {
// Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}.
if (uint256(s) > uint256(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0))
return (Error.InvalidSignatureS, address(0));
if (v != 27 && v != 28) return (Error.InvalidSignatureV, address(0));
signer = ecrecover(digest, v, r, s);
return (signer == address(0)) ? (Error.InvalidSignature, address(0)) : (Error.NoError, signer);
}
/**
* @dev Returns an error, if any, in validating an ECDSA/secp256k1 signature for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array ERC1271 signature.
* @return An error, if any, that occurred during the signer recovery.
*/
function validateECDSASignature(
address signer,
bytes32 digest,
bytes memory signature
) internal pure returns (Error) {
(Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, signature);
return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
}
/**
* @dev Returns an error, if any, in validating an ECDSA/secp256k1 short signature for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
* @return An error, if any, that occurred during the signer recovery.
*/
function validateECDSASignature(
address signer,
bytes32 digest,
bytes32 r,
bytes32 vs
) internal pure returns (Error) {
(Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, r, vs);
return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
}
/**
* @dev Returns an error, if any, in validating an ECDSA/secp256k1 signature for a signer and digest.
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param v An ECDSA/secp256k1 signature parameter.
* @param r An ECDSA/secp256k1 signature parameter.
* @param s An ECDSA/secp256k1 signature parameter.
* @return An error, if any, that occurred during the signer recovery.
*/
function validateECDSASignature(
address signer,
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (Error) {
(Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, v, r, s);
return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
}
/**
* @dev Returns an error if `signer` is not `recoveredSigner`.
* @param signer The address of the some signer.
* @param recoveredSigner The address of the some recoveredSigner.
* @return An error if `signer` is not `recoveredSigner`.
*/
function validateRecoveredSigner(address signer, address recoveredSigner) internal pure returns (Error) {
return (signer == recoveredSigner) ? Error.NoError : Error.SignerMismatch;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IStatefulERC712 } from "./interfaces/IStatefulERC712.sol";
import { ERC712Extended } from "./ERC712Extended.sol";
/**
* @title Stateful Extension for EIP-712 typed structured data hashing and signing with nonces.
* @author M^0 Labs
* @dev An abstract implementation to satisfy stateful EIP-712 with nonces.
*/
abstract contract StatefulERC712 is IStatefulERC712, ERC712Extended {
/// @inheritdoc IStatefulERC712
mapping(address account => uint256 nonce) public nonces; // Nonces for all signatures.
/**
* @notice Construct the StatefulERC712 contract.
* @param name_ The name of the contract.
*/
constructor(string memory name_) ERC712Extended(name_) {}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
/**
* @title Library to perform safe math operations on uint types
* @author M^0 Labs
*/
library UIntMath {
/* ============ Custom Errors ============ */
/// @notice Emitted when a passed value is greater than the maximum value of uint16.
error InvalidUInt16();
/// @notice Emitted when a passed value is greater than the maximum value of uint40.
error InvalidUInt40();
/// @notice Emitted when a passed value is greater than the maximum value of uint48.
error InvalidUInt48();
/// @notice Emitted when a passed value is greater than the maximum value of uint112.
error InvalidUInt112();
/// @notice Emitted when a passed value is greater than the maximum value of uint128.
error InvalidUInt128();
/// @notice Emitted when a passed value is greater than the maximum value of uint240.
error InvalidUInt240();
/* ============ Internal View/Pure Functions ============ */
/**
* @notice Casts a given uint256 value to a uint16,
* ensuring that it is less than or equal to the maximum uint16 value.
* @param n The value to check.
* @return The value casted to uint16.
*/
function safe16(uint256 n) internal pure returns (uint16) {
if (n > type(uint16).max) revert InvalidUInt16();
return uint16(n);
}
/**
* @notice Casts a given uint256 value to a uint40,
* ensuring that it is less than or equal to the maximum uint40 value.
* @param n The value to check.
* @return The value casted to uint40.
*/
function safe40(uint256 n) internal pure returns (uint40) {
if (n > type(uint40).max) revert InvalidUInt40();
return uint40(n);
}
/**
* @notice Casts a given uint256 value to a uint48,
* ensuring that it is less than or equal to the maximum uint48 value.
* @param n The value to check.
* @return The value casted to uint48.
*/
function safe48(uint256 n) internal pure returns (uint48) {
if (n > type(uint48).max) revert InvalidUInt48();
return uint48(n);
}
/**
* @notice Casts a given uint256 value to a uint112,
* ensuring that it is less than or equal to the maximum uint112 value.
* @param n The value to check.
* @return The value casted to uint112.
*/
function safe112(uint256 n) internal pure returns (uint112) {
if (n > type(uint112).max) revert InvalidUInt112();
return uint112(n);
}
/**
* @notice Casts a given uint256 value to a uint128,
* ensuring that it is less than or equal to the maximum uint128 value.
* @param n The value to check.
* @return The value casted to uint128.
*/
function safe128(uint256 n) internal pure returns (uint128) {
if (n > type(uint128).max) revert InvalidUInt128();
return uint128(n);
}
/**
* @notice Casts a given uint256 value to a uint240,
* ensuring that it is less than or equal to the maximum uint240 value.
* @param n The value to check.
* @return The value casted to uint240.
*/
function safe240(uint256 n) internal pure returns (uint240) {
if (n > type(uint240).max) revert InvalidUInt240();
return uint240(n);
}
/**
* @notice Limits a given uint256 value to the maximum uint32 value.
* @param n The value to check.
* @return The value limited to within uint32 bounds.
*/
function bound32(uint256 n) internal pure returns (uint32) {
return uint32(min256(n, uint256(type(uint32).max)));
}
/**
* @notice Limits a given uint256 value to the maximum uint112 value.
* @param n The value to check.
* @return The value limited to within uint112 bounds.
*/
function bound112(uint256 n) internal pure returns (uint112) {
return uint112(min256(n, uint256(type(uint112).max)));
}
/**
* @notice Limits a given uint256 value to the maximum uint128 value.
* @param n The value to check.
* @return The value limited to within uint128 bounds.
*/
function bound128(uint256 n) internal pure returns (uint128) {
return uint128(min256(n, uint256(type(uint128).max)));
}
/**
* @notice Limits a given uint256 value to the maximum uint240 value.
* @param n The value to check.
* @return The value limited to within uint240 bounds.
*/
function bound240(uint256 n) internal pure returns (uint240) {
return uint240(min256(n, uint256(type(uint240).max)));
}
/**
* @notice Compares two uint32 values and returns the larger one.
* @param a_ Value to check.
* @param b_ Value to check.
* @return The larger value.
*/
function max32(uint32 a_, uint32 b_) internal pure returns (uint32) {
return a_ > b_ ? a_ : b_;
}
/**
* @notice Compares two uint40 values and returns the larger one.
* @param a_ Value to check.
* @param b_ Value to check.
* @return The larger value.
*/
function max40(uint40 a_, uint40 b_) internal pure returns (uint40) {
return a_ > b_ ? a_ : b_;
}
/**
* @notice Compares two uint32 values and returns the lesser one.
* @param a_ Value to check.
* @param b_ Value to check.
* @return The lesser value.
*/
function min32(uint32 a_, uint32 b_) internal pure returns (uint32) {
return a_ < b_ ? a_ : b_;
}
/**
* @notice Compares two uint40 values and returns the lesser one.
* @param a_ Value to check.
* @param b_ Value to check.
* @return The lesser value.
*/
function min40(uint40 a_, uint40 b_) internal pure returns (uint40) {
return a_ < b_ ? a_ : b_;
}
/**
* @notice Compares two uint240 values and returns the lesser one.
* @param a_ Value to check.
* @param b_ Value to check.
* @return The lesser value.
*/
function min240(uint240 a_, uint240 b_) internal pure returns (uint240) {
return a_ < b_ ? a_ : b_;
}
/**
* @notice Compares two uint112 values and returns the lesser one.
* @param a_ Value to check.
* @param b_ Value to check.
* @return The lesser value.
*/
function min112(uint112 a_, uint112 b_) internal pure returns (uint112) {
return a_ < b_ ? a_ : b_;
}
/**
* @notice Compares two uint256 values and returns the lesser one.
* @param a_ Value to check.
* @param b_ Value to check.
* @return The lesser value.
*/
function min256(uint256 a_, uint256 b_) internal pure returns (uint256) {
return a_ < b_ ? a_ : b_;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { UIntMath } from "../lib/common/src/libs/UIntMath.sol";
import { EpochBasedVoteToken } from "./abstract/EpochBasedVoteToken.sol";
import { IStandardGovernorDeployer } from "./interfaces/IStandardGovernorDeployer.sol";
import { IZeroToken } from "./interfaces/IZeroToken.sol";
/*
███████╗███████╗██████╗ ██████╗ ████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗
╚══███╔╝██╔════╝██╔══██╗██╔═══██╗ ╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║
███╔╝ █████╗ ██████╔╝██║ ██║ ██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║
███╔╝ ██╔══╝ ██╔══██╗██║ ██║ ██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║
███████╗███████╗██║ ██║╚██████╔╝ ██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║
╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝
-->> It all starts at zero. <<--
*/
/**
* @title An instance of an EpochBasedVoteToken delegating minting control to a Standard Governor,
* and enabling range queries for past balances, voting powers, delegations, and total supplies.
* @author M^0 Labs
*/
contract ZeroToken is IZeroToken, EpochBasedVoteToken {
/* ============ Variables ============ */
/// @inheritdoc IZeroToken
address public immutable standardGovernorDeployer;
/* ============ Modifiers ============ */
/// @dev Revert if the caller is not the Standard Governor.
modifier onlyStandardGovernor() {
if (msg.sender != standardGovernor()) revert NotStandardGovernor();
_;
}
/* ============ Constructor ============ */
/**
* @notice Constructs a new ZeroToken contract.
* @param standardGovernorDeployer_ The address of the StandardGovernorDeployer contract.
* @param initialAccounts_ The addresses of the accounts to mint tokens to.
* @param initialBalances_ The amounts of tokens to mint to the accounts.
*/
constructor(
address standardGovernorDeployer_,
address[] memory initialAccounts_,
uint256[] memory initialBalances_
) EpochBasedVoteToken("Zero by M^0", "ZERO", 6) {
if ((standardGovernorDeployer = standardGovernorDeployer_) == address(0)) {
revert InvalidStandardGovernorDeployerAddress();
}
uint256 accountsLength_ = initialAccounts_.length;
uint256 balancesLength_ = initialBalances_.length;
if (accountsLength_ != balancesLength_) revert ArrayLengthMismatch(accountsLength_, balancesLength_);
for (uint256 index_; index_ < accountsLength_; ++index_) {
_mint(initialAccounts_[index_], initialBalances_[index_]);
}
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IZeroToken
function mint(address recipient_, uint256 amount_) external onlyStandardGovernor {
_mint(recipient_, amount_);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IZeroToken
function pastBalancesOf(
address account_,
uint256 startEpoch_,
uint256 endEpoch_
) external view returns (uint256[] memory) {
uint16 safeEndEpoch_ = UIntMath.safe16(endEpoch_);
_revertIfNotPastTimepoint(safeEndEpoch_);
return _getValuesBetween(_balances[account_], UIntMath.safe16(startEpoch_), safeEndEpoch_);
}
/// @inheritdoc IZeroToken
function pastTotalSupplies(uint256 startEpoch_, uint256 endEpoch_) external view returns (uint256[] memory) {
uint16 safeEndEpoch_ = UIntMath.safe16(endEpoch_);
_revertIfNotPastTimepoint(safeEndEpoch_);
return _getValuesBetween(_totalSupplies, UIntMath.safe16(startEpoch_), safeEndEpoch_);
}
/// @inheritdoc IZeroToken
function standardGovernor() public view returns (address) {
return IStandardGovernorDeployer(standardGovernorDeployer).lastDeploy();
}
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns the values of `amountSnaps_` between `startEpoch_` and `endEpoch_`.
* @param amountSnaps_ The array of AmountSnaps to query.
* @param startEpoch_ The epoch from which to start querying.
* @param endEpoch_ The epoch at which to stop querying.
* @return values_ The values of `amountSnaps_` between `startEpoch_` and `endEpoch_`.
*/
function _getValuesBetween(
AmountSnap[] storage amountSnaps_,
uint16 startEpoch_,
uint16 endEpoch_
) internal view returns (uint256[] memory values_) {
if (startEpoch_ == 0) revert EpochZero();
if (startEpoch_ > endEpoch_) revert StartEpochAfterEndEpoch();
uint16 epochsIndex_ = endEpoch_ - startEpoch_ + 1;
values_ = new uint256[](epochsIndex_);
uint256 snapIndex_ = amountSnaps_.length;
// Keep going back as long as the epoch is greater or equal to the previous AmountSnap's startingEpoch.
while (snapIndex_ > 0) {
unchecked {
AmountSnap storage amountSnap_ = _unsafeAccess(amountSnaps_, --snapIndex_);
uint256 snapStartingEpoch_ = amountSnap_.startingEpoch;
// Keep checking if the AmountSnap's startingEpoch is applicable to the current and decrementing epoch.
while (snapStartingEpoch_ <= endEpoch_) {
values_[--epochsIndex_] = amountSnap_.amount;
if (epochsIndex_ == 0) return values_;
--endEpoch_;
}
}
}
}
}
{
"compilationTarget": {
"lib/ttg/src/ZeroToken.sol": "ZeroToken"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"remappings": [
":common/=lib/protocol/lib/common/src/",
":contract-test-utils/=lib/ttg/lib/erc20-helper/lib/contract-test-utils/contracts/",
":ds-test/=lib/protocol/lib/solmate/lib/ds-test/src/",
":erc20-helper/=lib/ttg/lib/erc20-helper/src/",
":forge-std/=lib/forge-std/src/",
":protocol/=lib/protocol/",
":solmate/=lib/protocol/lib/solmate/src/",
":ttg/=lib/ttg/"
]
}
[{"inputs":[{"internalType":"address","name":"standardGovernorDeployer_","type":"address"},{"internalType":"address[]","name":"initialAccounts_","type":"address[]"},{"internalType":"uint256[]","name":"initialBalances_","type":"uint256[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"accountsLength","type":"uint256"},{"internalType":"uint256","name":"balancesLength","type":"uint256"}],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationAlreadyUsed","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"}],"name":"AuthorizationExpired","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"}],"name":"AuthorizationNotYetValid","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"payee","type":"address"}],"name":"CallerMustBePayee","type":"error"},{"inputs":[],"name":"EpochZero","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expectedNonce","type":"uint256"}],"name":"InvalidAccountNonce","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSignatureLength","type":"error"},{"inputs":[],"name":"InvalidSignatureS","type":"error"},{"inputs":[],"name":"InvalidSignatureV","type":"error"},{"inputs":[],"name":"InvalidStandardGovernorDeployerAddress","type":"error"},{"inputs":[],"name":"InvalidUInt16","type":"error"},{"inputs":[],"name":"InvalidUInt240","type":"error"},{"inputs":[{"internalType":"uint48","name":"timepoint","type":"uint48"},{"internalType":"uint48","name":"clock","type":"uint48"}],"name":"NotPastTimepoint","type":"error"},{"inputs":[],"name":"NotStandardGovernor","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"SignatureExpired","type":"error"},{"inputs":[],"name":"SignerMismatch","type":"error"},{"inputs":[],"name":"StartEpochAfterEndEpoch","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegatee","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegatee","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegatee","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"CANCEL_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CLOCK_MODE","outputs":[{"internalType":"string","name":"clockMode_","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RECEIVE_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"allowance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"authorizationState","outputs":[{"internalType":"bool","name":"isNonceUsed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer_","type":"address"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"vs_","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer_","type":"address"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer_","type":"address"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clock","outputs":[{"internalType":"uint48","name":"clock_","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee_","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee_","type":"address"},{"internalType":"uint256","name":"nonce_","type":"uint256"},{"internalType":"uint256","name":"expiry_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"address","name":"delegatee_","type":"address"},{"internalType":"uint256","name":"nonce_","type":"uint256"},{"internalType":"uint256","name":"expiry_","type":"uint256"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields_","type":"bytes1"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"version_","type":"string"},{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"address","name":"verifyingContract_","type":"address"},{"internalType":"bytes32","name":"salt_","type":"bytes32"},{"internalType":"uint256[]","name":"extensions_","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee_","type":"address"},{"internalType":"uint256","name":"nonce_","type":"uint256"},{"internalType":"uint256","name":"expiry_","type":"uint256"}],"name":"getDelegationDigest","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"epoch_","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"name_","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"epoch_","type":"uint256"}],"name":"pastBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"startEpoch_","type":"uint256"},{"internalType":"uint256","name":"endEpoch_","type":"uint256"}],"name":"pastBalancesOf","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"epoch_","type":"uint256"}],"name":"pastDelegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startEpoch_","type":"uint256"},{"internalType":"uint256","name":"endEpoch_","type":"uint256"}],"name":"pastTotalSupplies","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch_","type":"uint256"}],"name":"pastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"vs_","type":"bytes32"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"standardGovernor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"standardGovernorDeployer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender_","type":"address"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"vs_","type":"bytes32"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"validAfter_","type":"uint256"},{"internalType":"uint256","name":"validBefore_","type":"uint256"},{"internalType":"bytes32","name":"nonce_","type":"bytes32"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"}]