// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern and gas-optimized ERC20 + EIP-2612 implementation with COMP-style governance.abstractcontractERC20Vote{
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
eventDelegateChanged(addressindexed delegator, addressindexed fromDelegate, addressindexed toDelegate);
eventDelegateVotesChanged(addressindexed delegate, uint256 previousBalance, uint256 newBalance);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*///////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*///////////////////////////////////////////////////////////////
DAO STORAGE
//////////////////////////////////////////////////////////////*/bytes32publicconstant DELEGATION_TYPEHASH =keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
mapping(address=>address) internal _delegates;
mapping(address=>mapping(uint256=> Checkpoint)) public checkpoints;
mapping(address=>uint256) public numCheckpoints;
structCheckpoint {
uint32 fromTimestamp;
uint96 votes;
}
/*///////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/bytes32publicconstant PERMIT_TYPEHASH =keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*///////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (bool) {
balanceOf[msg.sender] -= amount;
// this is safe from overflow because the sum of all user// balances can't exceed 'type(uint256).max'unchecked {
balanceOf[to] += amount;
}
_moveDelegates(delegates(msg.sender), delegates(to), amount);
emit Transfer(msg.sender, to, amount);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (bool) {
if (allowance[from][msg.sender] !=type(uint256).max)
allowance[from][msg.sender] -= amount;
balanceOf[from] -= amount;
// this is safe from overflow because the sum of all user// balances can't exceed 'type(uint256).max'unchecked {
balanceOf[to] += amount;
}
_moveDelegates(delegates(from), delegates(to), amount);
emit Transfer(from, to, amount);
returntrue;
}
/*///////////////////////////////////////////////////////////////
DAO LOGIC
//////////////////////////////////////////////////////////////*/functiondelegates(address delegator) publicviewvirtualreturns (address delegatee) {
address current = _delegates[delegator];
delegatee = current ==address(0) ? delegator : current;
}
functiongetCurrentVotes(address account) publicviewvirtualreturns (uint256 votes) {
// this is safe from underflow because decrement only occurs if `nCheckpoints` is positiveunchecked {
uint256 nCheckpoints = numCheckpoints[account];
votes = nCheckpoints !=0 ? checkpoints[account][nCheckpoints -1].votes : 0;
}
}
functiondelegate(address delegatee) publicvirtual{
_delegate(msg.sender, delegatee);
}
functiondelegateBySig(address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
bytes32 structHash =keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
bytes32 digest =keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), structHash));
address signatory =ecrecover(digest, v, r, s);
require(signatory !=address(0), "ZERO_ADDRESS");
// this is reasonably safe from overflow because incrementing `nonces` beyond// 'type(uint256).max' is exceedingly unlikely compared to optimization benefitsunchecked {
require(nonce == nonces[signatory]++, "INVALID_NONCE");
}
require(block.timestamp<= expiry, "SIGNATURE_EXPIRED");
_delegate(signatory, delegatee);
}
functiongetPriorVotes(address account, uint256 timestamp) publicviewvirtualreturns (uint96 votes) {
require(block.timestamp> timestamp, "NOT_YET_DETERMINED");
uint256 nCheckpoints = numCheckpoints[account];
if (nCheckpoints ==0) return0;
// this is safe from underflow because decrement only occurs if `nCheckpoints` is positiveunchecked {
if (checkpoints[account][nCheckpoints -1].fromTimestamp <= timestamp)
return checkpoints[account][nCheckpoints -1].votes;
if (checkpoints[account][0].fromTimestamp > timestamp) return0;
uint256 lower;
// this is safe from underflow because decrement only occurs if `nCheckpoints` is positiveuint256 upper = nCheckpoints -1;
while (upper > lower) {
// this is safe from underflow because `upper` ceiling is provideduint256 center = upper - (upper - lower) /2;
Checkpoint memory cp = checkpoints[account][center];
if (cp.fromTimestamp == timestamp) {
return cp.votes;
} elseif (cp.fromTimestamp < timestamp) {
lower = center;
} else {
upper = center -1;
}
}
return checkpoints[account][lower].votes;
}
}
function_delegate(address delegator, address delegatee) internalvirtual{
address currentDelegate = _delegates[delegator];
_delegates[delegator] = delegatee;
_moveDelegates(currentDelegate, delegatee, balanceOf[delegator]);
emit DelegateChanged(delegator, currentDelegate, delegatee);
}
function_moveDelegates(address srcRep,
address dstRep,
uint256 amount
) internalvirtual{
if (srcRep != dstRep && amount !=0)
if (srcRep !=address(0)) {
uint256 srcRepNum = numCheckpoints[srcRep];
uint256 srcRepOld = srcRepNum !=0 ? checkpoints[srcRep][srcRepNum -1].votes : 0;
uint256 srcRepNew = srcRepOld - amount;
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep !=address(0)) {
uint256 dstRepNum = numCheckpoints[dstRep];
uint256 dstRepOld = dstRepNum !=0 ? checkpoints[dstRep][dstRepNum -1].votes : 0;
uint256 dstRepNew = dstRepOld + amount;
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
function_writeCheckpoint(address delegatee,
uint256 nCheckpoints,
uint256 oldVotes,
uint256 newVotes
) internalvirtual{
unchecked {
// this is safe from underflow because decrement only occurs if `nCheckpoints` is positiveif (nCheckpoints !=0&& checkpoints[delegatee][nCheckpoints -1].fromTimestamp ==block.timestamp) {
checkpoints[delegatee][nCheckpoints -1].votes = safeCastTo96(newVotes);
} else {
checkpoints[delegatee][nCheckpoints] = Checkpoint(safeCastTo32(block.timestamp), safeCastTo96(newVotes));
// this is reasonably safe from overflow because incrementing `nCheckpoints` beyond// 'type(uint256).max' is exceedingly unlikely compared to optimization benefits
numCheckpoints[delegatee] = nCheckpoints +1;
}
}
emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
}
/*///////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
require(block.timestamp<= deadline, "PERMIT_DEADLINE_EXPIRED");
// this is reasonably safe from overflow because incrementing `nonces` beyond// 'type(uint256).max' is exceedingly unlikely compared to optimization benefitsunchecked {
bytes32 digest =keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress =ecrecover(digest, v, r, s);
require(recoveredAddress !=address(0) && recoveredAddress == owner, 'INVALID_PERMIT_SIGNATURE');
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32 domainSeparator) {
domainSeparator =block.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32 domainSeparator) {
domainSeparator =keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes("1")),
block.chainid,
address(this)
)
);
}
/*///////////////////////////////////////////////////////////////
MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 amount) internalvirtual{
totalSupply += amount;
// this is safe because the sum of all user// balances can't exceed 'type(uint256).max'unchecked {
balanceOf[to] += amount;
}
_moveDelegates(address(0), delegates(to), amount);
emit Transfer(address(0), to, amount);
}
function_burn(addressfrom, uint256 amount) internalvirtual{
balanceOf[from] -= amount;
// this is safe because a user won't ever// have a balance larger than `totalSupply`unchecked {
totalSupply -= amount;
}
_moveDelegates(delegates(from), address(0), amount);
emit Transfer(from, address(0), amount);
}
/*///////////////////////////////////////////////////////////////
SAFECAST LOGIC
//////////////////////////////////////////////////////////////*/functionsafeCastTo32(uint256 x) internalpurevirtualreturns (uint32 y) {
require(x <=type(uint32).max);
y =uint32(x);
}
functionsafeCastTo96(uint256 x) internalpurevirtualreturns (uint96 y) {
require(x <=type(uint96).max);
y =uint96(x);
}
}
Contract Source Code
File 2 of 7: FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Arithmetic library with operations for fixed-point numbers./// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)libraryFixedPointMathLib{
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/uint256internalconstant WAD =1e18; // The scalar of ETH and most ERC20s.functionmulWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
functionmulWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
functiondivWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
functiondivWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/functionmulDivDown(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
assembly {
// Store x * y in z for now.
z :=mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))ifiszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// Divide z by the denominator.
z :=div(z, denominator)
}
}
functionmulDivUp(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
assembly {
// Store x * y in z for now.
z :=mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))ifiszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// First, divide z - 1 by the denominator and add 1.// We allow z - 1 to underflow if z is 0, because we multiply the// end result by 0 if z is zero, ensuring we return 0 if z is zero.
z :=mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
}
}
functionrpow(uint256 x,
uint256 n,
uint256 scalar
) internalpurereturns (uint256 z) {
assembly {
switch x
case0 {
switch n
case0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z :=0
}
}
default {
switchmod(n, 2)
case0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.let half :=shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n :=shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n :=shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.// Equivalent to iszero(eq(div(xx, x), x)) here.ifshr(128, x) {
revert(0, 0)
}
// Store x squared.let xx :=mul(x, x)
// Round to the nearest number.let xxRound :=add(xx, half)
// Revert if xx + half overflowed.iflt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x :=div(xxRound, scalar)
// If n is even:ifmod(n, 2) {
// Compute z * x.let zx :=mul(z, x)
// If z * x overflowed:ifiszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.ifiszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.let zxRound :=add(zx, half)
// Revert if zx + half overflowed.iflt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z :=div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/functionsqrt(uint256 x) internalpurereturns (uint256 z) {
assembly {
// Start off with z at 1.
z :=1// Used below to help find a nearby power of 2.let y := x
// Find the lowest power of 2 that is at least sqrt(x).ifiszero(lt(y, 0x100000000000000000000000000000000)) {
y :=shr(128, y) // Like dividing by 2 ** 128.
z :=shl(64, z) // Like multiplying by 2 ** 64.
}
ifiszero(lt(y, 0x10000000000000000)) {
y :=shr(64, y) // Like dividing by 2 ** 64.
z :=shl(32, z) // Like multiplying by 2 ** 32.
}
ifiszero(lt(y, 0x100000000)) {
y :=shr(32, y) // Like dividing by 2 ** 32.
z :=shl(16, z) // Like multiplying by 2 ** 16.
}
ifiszero(lt(y, 0x10000)) {
y :=shr(16, y) // Like dividing by 2 ** 16.
z :=shl(8, z) // Like multiplying by 2 ** 8.
}
ifiszero(lt(y, 0x100)) {
y :=shr(8, y) // Like dividing by 2 ** 8.
z :=shl(4, z) // Like multiplying by 2 ** 4.
}
ifiszero(lt(y, 0x10)) {
y :=shr(4, y) // Like dividing by 2 ** 4.
z :=shl(2, z) // Like multiplying by 2 ** 2.
}
ifiszero(lt(y, 0x8)) {
// Equivalent to 2 ** z.
z :=shl(1, z)
}
// Shifting right by 1 is like dividing by 2.
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
// Compute a rounded down version of z.let zRoundDown :=div(x, z)
// If zRoundDown is smaller, use it.iflt(zRoundDown, z) {
z := zRoundDown
}
}
}
}
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.librarySafeTransferLib{
/*///////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool callStatus;
assembly {
// Transfer the ETH and store if it succeeded or not.
callStatus :=call(gas(), to, amount, 0, 0, 0, 0)
}
require(callStatus, "ETH_TRANSFER_FAILED");
}
/*///////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(address token,
addressfrom,
address to,
uint256 amount
) internal{
bool callStatus;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.// Call the token and store if it succeeded or not.// We use 100 because the calldata length is 4 + 32 * 3.
callStatus :=call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
}
functionsafeTransfer(address token,
address to,
uint256 amount
) internal{
bool callStatus;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.// Call the token and store if it succeeded or not.// We use 68 because the calldata length is 4 + 32 * 2.
callStatus :=call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED");
}
functionsafeApprove(address token,
address to,
uint256 amount
) internal{
bool callStatus;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector.mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.// Call the token and store if it succeeded or not.// We use 68 because the calldata length is 4 + 32 * 2.
callStatus :=call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
}
/*///////////////////////////////////////////////////////////////
INTERNAL HELPER LOGIC
//////////////////////////////////////////////////////////////*/functiondidLastOptionalReturnCallSucceed(bool callStatus) privatepurereturns (bool success) {
assembly {
// Get how many bytes the call returned.let returnDataSize :=returndatasize()
// If the call reverted:ifiszero(callStatus) {
// Copy the revert message into memory.returndatacopy(0, 0, returnDataSize)
// Revert with the same message.revert(0, returnDataSize)
}
switch returnDataSize
case32 {
// Copy the return data into memory.returndatacopy(0, 0, returnDataSize)
// Set success to whether it returned true.
success :=iszero(iszero(mload(0)))
}
case0 {
// There was no return data.
success :=1
}
default {
// It returned some malformed input.
success :=0
}
}
}
}