// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. 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}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./ECDSA.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*/
abstract contract EIP712 {
/* solhint-disable var-name-mixedcase */
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
uint256 private immutable _CACHED_CHAIN_ID;
address private immutable _CACHED_THIS;
bytes32 private immutable _HASHED_NAME;
bytes32 private immutable _HASHED_VERSION;
bytes32 private immutable _TYPE_HASH;
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
bytes32 typeHash = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
_CACHED_CHAIN_ID = block.chainid;
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
_CACHED_THIS = address(this);
_TYPE_HASH = typeHash;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
return _CACHED_DOMAIN_SEPARATOR;
} else {
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
}
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 nameHash,
bytes32 versionHash
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
}
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
import "openzeppelin-contracts/utils/cryptography/EIP712.sol";
import "../interfaces/ISignaturesValidator.sol";
/**
* @dev Utility for signing Solidity function calls.
*/
abstract contract EOASignaturesValidator is ISignaturesValidator, EIP712 {
error EOASignaturesValidator__ExpiredSignature();
error EOASignaturesValidator__MalformedSignature();
error EOASignaturesValidator__RevertWithErrorCode(uint256 errorCode);
// Replay attack prevention for each account.
mapping(address => uint256) internal _nextNonce;
function getDomainSeparator() public view override returns (bytes32) {
return _domainSeparatorV4();
}
function getNextNonce(address account) public view override returns (uint256) {
return _nextNonce[account];
}
function _ensureValidSignature(address account, bytes32 structHash, bytes memory signature, uint256 errorCode)
internal
{
return _ensureValidSignature(account, structHash, signature, type(uint256).max, errorCode);
}
function _ensureValidSignature(
address account,
bytes32 structHash,
bytes memory signature,
uint256 deadline,
uint256 errorCode
) internal {
bytes32 digest = _hashTypedDataV4(structHash);
if (!_isValidSignature(account, digest, signature)) {
revert EOASignaturesValidator__RevertWithErrorCode(errorCode);
}
// We could check for the deadline before validating the signature, but this leads to saner error processing (as
// we only care about expired deadlines if the signature is correct) and only affects the gas cost of the revert
// scenario, which will only occur infrequently, if ever.
// The deadline is timestamp-based: it should not be relied upon for sub-minute accuracy.
// solhint-disable-next-line not-rely-on-time
if (deadline < block.timestamp) {
revert EOASignaturesValidator__ExpiredSignature();
}
// We only advance the nonce after validating the signature. This is irrelevant for this module, but it can be
// important in derived contracts that override _isValidSignature (e.g. SignaturesValidator), as we want for
// the observable state to still have the current nonce as the next valid one.
_nextNonce[account] += 1;
}
function _isValidSignature(address account, bytes32 digest, bytes memory signature)
internal
view
virtual
returns (bool)
{
if (signature.length != 65) {
revert EOASignaturesValidator__MalformedSignature();
}
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the r, s and v signature parameters, and the only way to get them is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
address recoveredAddress = ecrecover(digest, v, r, s);
// ecrecover returns the zero address on recover failure, so we need to handle that explicitly.
return (recoveredAddress != address(0) && recoveredAddress == account);
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";
import {SafeCast} from "openzeppelin-contracts/utils/math/SafeCast.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol";
import "./lib/OptionalOnlyCaller.sol";
import "./interfaces/IVotingEscrow.sol";
import "./interfaces/IFeeDistributor.sol";
// solhint-disable not-rely-on-time
/**
* @title Fee Distributor
* @notice Distributes any tokens transferred to the contract (e.g. Protocol fees and any token emissions) among vetoken
* holders proportionally based on a snapshot of the week at which the tokens are sent to the FeeDistributor contract.
* @dev Supports distributing arbitrarily many different tokens. In order to start distributing a new token to vetoken
* holders simply transfer the tokens to the `FeeDistributor` contract and then call `checkpointToken`.
*/
contract FeeDistributor is IFeeDistributor, OptionalOnlyCaller, ReentrancyGuard {
/// -----------------------------------------------------------------------
/// Library usage
/// -----------------------------------------------------------------------
using SafeTransferLib for ERC20;
/// -----------------------------------------------------------------------
/// Errors
/// -----------------------------------------------------------------------
error FeeDistributor__InputLengthMismatch();
error FeeDistributor__VotingEscrowZeroTotalSupply();
error FeeDistributor__CannotStartBeforeCurrentWeek();
/// -----------------------------------------------------------------------
/// Immutable params
/// -----------------------------------------------------------------------
uint256 private immutable _startTime;
IVotingEscrow private immutable _votingEscrow;
/// -----------------------------------------------------------------------
/// Storage variables
/// -----------------------------------------------------------------------
// Global State
uint256 private _timeCursor;
mapping(uint256 => uint256) private _veSupplyCache;
// Token State
// `startTime` and `timeCursor` are both timestamps so comfortably fit in a uint64.
// `cachedBalance` will comfortably fit the total supply of any meaningful token.
// Should more than 2^128 tokens be sent to this contract then checkpointing this token will fail until enough
// tokens have been claimed to bring the total balance back below 2^128.
struct TokenState {
uint64 startTime;
uint64 timeCursor;
uint128 cachedBalance;
}
mapping(ERC20 => TokenState) private _tokenState;
mapping(ERC20 => mapping(uint256 => uint256)) private _tokensPerWeek;
// User State
// `startTime` and `timeCursor` are timestamps so will comfortably fit in a uint64.
// For `lastEpochCheckpointed` to overflow would need over 2^128 transactions to the VotingEscrow contract.
struct UserState {
uint64 startTime;
uint64 timeCursor;
uint128 lastEpochCheckpointed;
}
mapping(address => UserState) internal _userState;
mapping(address => mapping(uint256 => uint256)) private _userBalanceAtTimestamp;
mapping(address => mapping(ERC20 => uint256)) private _userTokenTimeCursor;
/// -----------------------------------------------------------------------
/// Constructor
/// -----------------------------------------------------------------------
constructor(IVotingEscrow votingEscrow, uint256 startTime) EIP712("FeeDistributor", "1") {
_votingEscrow = votingEscrow;
startTime = _roundDownTimestamp(startTime);
uint256 currentWeek = _roundDownTimestamp(block.timestamp);
if (startTime < currentWeek) {
revert FeeDistributor__CannotStartBeforeCurrentWeek();
}
if (startTime == currentWeek) {
// We assume that `votingEscrow` has been deployed in a week previous to this one.
// If `votingEscrow` did not have a non-zero supply at the beginning of the current week
// then any tokens which are distributed this week will be lost permanently.
if (votingEscrow.totalSupply(currentWeek) == 0) {
revert FeeDistributor__VotingEscrowZeroTotalSupply();
}
}
_startTime = startTime;
_timeCursor = startTime;
}
/// -----------------------------------------------------------------------
/// View functions
/// -----------------------------------------------------------------------
/**
* @notice Returns the VotingEscrow (vetoken) token contract
*/
function getVotingEscrow() external view override returns (IVotingEscrow) {
return _votingEscrow;
}
/**
* @notice Returns the global time cursor representing the most earliest uncheckpointed week.
*/
function getTimeCursor() external view override returns (uint256) {
return _timeCursor;
}
/**
* @notice Returns the user-level time cursor representing the most earliest uncheckpointed week.
* @param user - The address of the user to query.
*/
function getUserTimeCursor(address user) external view override returns (uint256) {
return _userState[user].timeCursor;
}
/**
* @notice Returns the token-level time cursor storing the timestamp at up to which tokens have been distributed.
* @param token - The ERC20 token address to query.
*/
function getTokenTimeCursor(ERC20 token) external view override returns (uint256) {
return _tokenState[token].timeCursor;
}
/**
* @notice Returns the user-level time cursor storing the timestamp of the latest token distribution claimed.
* @param user - The address of the user to query.
* @param token - The ERC20 token address to query.
*/
function getUserTokenTimeCursor(address user, ERC20 token) external view override returns (uint256) {
return _getUserTokenTimeCursor(user, token);
}
/**
* @notice Returns the user's cached balance of vetoken as of the provided timestamp.
* @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
* This function requires `user` to have been checkpointed past `timestamp` so that their balance is cached.
* @param user - The address of the user of which to read the cached balance of.
* @param timestamp - The timestamp at which to read the `user`'s cached balance at.
*/
function getUserBalanceAtTimestamp(address user, uint256 timestamp) external view override returns (uint256) {
return _userBalanceAtTimestamp[user][timestamp];
}
/**
* @notice Returns the cached total supply of vetoken as of the provided timestamp.
* @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
* This function requires the contract to have been checkpointed past `timestamp` so that the supply is cached.
* @param timestamp - The timestamp at which to read the cached total supply at.
*/
function getTotalSupplyAtTimestamp(uint256 timestamp) external view override returns (uint256) {
return _veSupplyCache[timestamp];
}
/**
* @notice Returns the FeeDistributor's cached balance of `token`.
*/
function getTokenLastBalance(ERC20 token) external view override returns (uint256) {
return _tokenState[token].cachedBalance;
}
/**
* @notice Returns the amount of `token` which the FeeDistributor received in the week beginning at `timestamp`.
* @param token - The ERC20 token address to query.
* @param timestamp - The timestamp corresponding to the beginning of the week of interest.
*/
function getTokensDistributedInWeek(ERC20 token, uint256 timestamp) external view override returns (uint256) {
return _tokensPerWeek[token][timestamp];
}
/// -----------------------------------------------------------------------
/// User actions
/// -----------------------------------------------------------------------
// Depositing
/**
* @notice Deposits tokens to be distributed in the current week.
* @dev Sending tokens directly to the FeeDistributor instead of using `depositToken` may result in tokens being
* retroactively distributed to past weeks, or for the distribution to carry over to future weeks.
*
* If for some reason `depositToken` cannot be called, in order to ensure that all tokens are correctly distributed
* manually call `checkpointToken` before and after the token transfer.
* @param token - The ERC20 token address to distribute.
* @param amount - The amount of tokens to deposit.
*/
function depositToken(ERC20 token, uint256 amount) external override nonReentrant {
_checkpointToken(token, false);
token.safeTransferFrom(msg.sender, address(this), amount);
_checkpointToken(token, true);
}
/**
* @notice Deposits tokens to be distributed in the current week.
* @dev A version of `depositToken` which supports depositing multiple `tokens` at once.
* See `depositToken` for more details.
* @param tokens - An array of ERC20 token addresses to distribute.
* @param amounts - An array of token amounts to deposit.
*/
function depositTokens(ERC20[] calldata tokens, uint256[] calldata amounts) external override nonReentrant {
if (tokens.length != amounts.length) {
revert FeeDistributor__InputLengthMismatch();
}
uint256 length = tokens.length;
for (uint256 i = 0; i < length;) {
_checkpointToken(tokens[i], false);
tokens[i].safeTransferFrom(msg.sender, address(this), amounts[i]);
_checkpointToken(tokens[i], true);
unchecked {
++i;
}
}
}
// Checkpointing
/**
* @notice Caches the total supply of vetoken at the beginning of each week.
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
*/
function checkpoint() external override nonReentrant {
_checkpointTotalSupply();
}
/**
* @notice Caches the user's balance of vetoken at the beginning of each week.
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
* @param user - The address of the user to be checkpointed.
*/
function checkpointUser(address user) external override nonReentrant {
_checkpointUserBalance(user);
}
/**
* @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
* @dev Any `token` balance held by the FeeDistributor above that which is returned by `getTokenLastBalance`
* will be distributed evenly across the time period since `token` was last checkpointed.
*
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
* @param token - The ERC20 token address to be checkpointed.
*/
function checkpointToken(ERC20 token) external override nonReentrant {
_checkpointToken(token, true);
}
/**
* @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
* @dev A version of `checkpointToken` which supports checkpointing multiple tokens.
* See `checkpointToken` for more details.
* @param tokens - An array of ERC20 token addresses to be checkpointed.
*/
function checkpointTokens(ERC20[] calldata tokens) external override nonReentrant {
uint256 tokensLength = tokens.length;
for (uint256 i = 0; i < tokensLength;) {
_checkpointToken(tokens[i], true);
unchecked {
++i;
}
}
}
// Claiming
/**
* @notice Claims all pending distributions of the provided token for a user.
* @dev It's not necessary to explicitly checkpoint before calling this function, it will ensure the FeeDistributor
* is up to date before calculating the amount of tokens to be claimed.
* @param user - The user on behalf of which to claim.
* @param token - The ERC20 token address to be claimed.
* @return The amount of `token` sent to `user` as a result of claiming.
*/
function claimToken(address user, ERC20 token)
external
override
nonReentrant
optionalOnlyCaller(user)
returns (uint256)
{
_checkpointTotalSupply();
_checkpointUserBalance(user);
_checkpointToken(token, false);
uint256 amount = _claimToken(user, token);
return amount;
}
/**
* @notice Claims a number of tokens on behalf of a user.
* @dev A version of `claimToken` which supports claiming multiple `tokens` on behalf of `user`.
* See `claimToken` for more details.
* @param user - The user on behalf of which to claim.
* @param tokens - An array of ERC20 token addresses to be claimed.
* @return An array of the amounts of each token in `tokens` sent to `user` as a result of claiming.
*/
function claimTokens(address user, ERC20[] calldata tokens)
external
override
nonReentrant
optionalOnlyCaller(user)
returns (uint256[] memory)
{
_checkpointTotalSupply();
_checkpointUserBalance(user);
uint256 tokensLength = tokens.length;
uint256[] memory amounts = new uint256[](tokensLength);
for (uint256 i = 0; i < tokensLength;) {
_checkpointToken(tokens[i], false);
amounts[i] = _claimToken(user, tokens[i]);
unchecked {
++i;
}
}
return amounts;
}
/// -----------------------------------------------------------------------
/// Internal functions
/// -----------------------------------------------------------------------
/**
* @dev It is required that both the global, token and user state have been properly checkpointed
* before calling this function.
*/
function _claimToken(address user, ERC20 token) internal returns (uint256) {
TokenState storage tokenState = _tokenState[token];
uint256 nextUserTokenWeekToClaim = _getUserTokenTimeCursor(user, token);
// The first week which cannot be correctly claimed is the earliest of:
// - A) The global or user time cursor (whichever is earliest), rounded up to the end of the week.
// - B) The token time cursor, rounded down to the beginning of the week.
//
// This prevents the two failure modes:
// - A) A user may claim a week for which we have not processed their balance, resulting in tokens being locked.
// - B) A user may claim a week which then receives more tokens to be distributed. However the user has
// already claimed for that week so their share of these new tokens are lost.
uint256 firstUnclaimableWeek = Math.min(
_roundUpTimestamp(Math.min(_timeCursor, _userState[user].timeCursor)),
_roundDownTimestamp(tokenState.timeCursor)
);
mapping(uint256 => uint256) storage tokensPerWeek = _tokensPerWeek[token];
mapping(uint256 => uint256) storage userBalanceAtTimestamp = _userBalanceAtTimestamp[user];
uint256 amount;
for (uint256 i = 0; i < 20;) {
// We clearly cannot claim for `firstUnclaimableWeek` and so we break here.
if (nextUserTokenWeekToClaim >= firstUnclaimableWeek) break;
unchecked {
amount += (tokensPerWeek[nextUserTokenWeekToClaim] * userBalanceAtTimestamp[nextUserTokenWeekToClaim])
/ _veSupplyCache[nextUserTokenWeekToClaim];
nextUserTokenWeekToClaim += 1 weeks;
++i;
}
}
// Update the stored user-token time cursor to prevent this user claiming this week again.
_userTokenTimeCursor[user][token] = nextUserTokenWeekToClaim;
if (amount > 0) {
unchecked {
// For a token to be claimable it must have been added to the cached balance so this is safe.
tokenState.cachedBalance = uint128(tokenState.cachedBalance - amount);
}
token.safeTransfer(user, amount);
emit TokensClaimed(user, token, amount, nextUserTokenWeekToClaim);
}
return amount;
}
/**
* @dev Calculate the amount of `token` to be distributed to `_votingEscrow` holders since the last checkpoint.
*/
function _checkpointToken(ERC20 token, bool force) internal {
TokenState storage tokenState = _tokenState[token];
uint256 lastTokenTime = tokenState.timeCursor;
uint256 timeSinceLastCheckpoint;
if (lastTokenTime == 0) {
// If it's the first time we're checkpointing this token then start distributing from now.
// Also mark at which timestamp users should start attempts to claim this token from.
lastTokenTime = block.timestamp;
tokenState.startTime = uint64(_roundDownTimestamp(block.timestamp));
// Prevent someone from assigning tokens to an inaccessible week.
require(block.timestamp > _startTime, "Fee distribution has not started yet");
} else {
unchecked {
timeSinceLastCheckpoint = block.timestamp - lastTokenTime;
if (!force) {
// Checkpointing N times within a single week is completely equivalent to checkpointing once at the end.
// We then want to get as close as possible to a single checkpoint every Wed 23:59 UTC to save gas.
// We then skip checkpointing if we're in the same week as the previous checkpoint.
bool alreadyCheckpointedThisWeek =
_roundDownTimestamp(block.timestamp) == _roundDownTimestamp(lastTokenTime);
// However we want to ensure that all of this week's fees are assigned to the current week without
// overspilling into the next week. To mitigate this, we checkpoint if we're near the end of the week.
bool nearingEndOfWeek = _roundUpTimestamp(block.timestamp) - block.timestamp < 1 days;
// This ensures that we checkpoint once at the beginning of the week and again for each user interaction
// towards the end of the week to give an accurate final reading of the balance.
if (alreadyCheckpointedThisWeek && !nearingEndOfWeek) {
return;
}
}
}
}
tokenState.timeCursor = uint64(block.timestamp);
uint256 tokenBalance = token.balanceOf(address(this));
uint256 newTokensToDistribute = tokenBalance - tokenState.cachedBalance;
if (newTokensToDistribute == 0) return;
require(tokenBalance <= type(uint128).max, "Maximum token balance exceeded");
tokenState.cachedBalance = uint128(tokenBalance);
uint256 firstIncompleteWeek = _roundDownTimestamp(lastTokenTime);
uint256 nextWeek = 0;
// Distribute `newTokensToDistribute` evenly across the time period from `lastTokenTime` to now.
// These tokens are assigned to weeks proportionally to how much of this period falls into each week.
mapping(uint256 => uint256) storage tokensPerWeek = _tokensPerWeek[token];
for (uint256 i = 0; i < 20;) {
unchecked {
// This is safe as we're incrementing a timestamp.
nextWeek = firstIncompleteWeek + 1 weeks;
if (block.timestamp < nextWeek) {
// `firstIncompleteWeek` is now the beginning of the current week, i.e. this is the final iteration.
if (timeSinceLastCheckpoint == 0 && block.timestamp == lastTokenTime) {
tokensPerWeek[firstIncompleteWeek] += newTokensToDistribute;
} else {
// block.timestamp >= lastTokenTime by definition.
tokensPerWeek[firstIncompleteWeek] +=
(newTokensToDistribute * (block.timestamp - lastTokenTime)) / timeSinceLastCheckpoint;
}
// As we've caught up to the present then we should now break.
break;
} else {
// We've gone a full week or more without checkpointing so need to distribute tokens to previous weeks.
if (timeSinceLastCheckpoint == 0 && nextWeek == lastTokenTime) {
// It shouldn't be possible to enter this block
tokensPerWeek[firstIncompleteWeek] += newTokensToDistribute;
} else {
// nextWeek > lastTokenTime by definition.
tokensPerWeek[firstIncompleteWeek] +=
(newTokensToDistribute * (nextWeek - lastTokenTime)) / timeSinceLastCheckpoint;
}
}
// We've now "checkpointed" up to the beginning of next week so must update timestamps appropriately.
lastTokenTime = nextWeek;
firstIncompleteWeek = nextWeek;
++i;
}
}
emit TokenCheckpointed(token, newTokensToDistribute, lastTokenTime);
}
/**
* @dev Cache the `user`'s balance of `_votingEscrow` at the beginning of each new week
*/
function _checkpointUserBalance(address user) internal {
uint256 maxUserEpoch = _votingEscrow.user_point_epoch(user);
// If user has no epochs then they have never locked vetoken.
// They clearly will not then receive fees.
if (maxUserEpoch == 0) return;
UserState storage userState = _userState[user];
// `nextWeekToCheckpoint` represents the timestamp of the beginning of the first week
// which we haven't checkpointed the user's VotingEscrow balance yet.
uint256 nextWeekToCheckpoint = userState.timeCursor;
uint256 userEpoch;
if (nextWeekToCheckpoint == 0) {
// First checkpoint for user so need to do the initial binary search
userEpoch = _findTimestampUserEpoch(user, _startTime, 0, maxUserEpoch);
} else {
if (nextWeekToCheckpoint >= block.timestamp) {
// User has checkpointed the current week already so perform early return.
// This prevents a user from processing epochs created later in this week, however this is not an issue
// as if a significant number of these builds up then the user will skip past them with a binary search.
return;
}
// Otherwise use the value saved from last time
userEpoch = userState.lastEpochCheckpointed;
unchecked {
// This optimizes a scenario common for power users, which have frequent `VotingEscrow` interactions in
// the same week. We assume that any such user is also claiming fees every week, and so we only perform
// a binary search here rather than integrating it into the main search algorithm, effectively skipping
// most of the week's irrelevant checkpoints.
// The slight tradeoff is that users who have multiple infrequent `VotingEscrow` interactions and also don't
// claim frequently will also perform the binary search, despite it not leading to gas savings.
if (maxUserEpoch - userEpoch > 20) {
userEpoch = _findTimestampUserEpoch(user, nextWeekToCheckpoint, userEpoch, maxUserEpoch);
}
}
}
// Epoch 0 is always empty so bump onto the next one so that we start on a valid epoch.
if (userEpoch == 0) {
userEpoch = 1;
}
IVotingEscrow.Point memory nextUserPoint = _votingEscrow.user_point_history(user, userEpoch);
// If this is the first checkpoint for the user, calculate the first week they're eligible for.
// i.e. the timestamp of the first Thursday after they locked.
// If this is earlier then the first distribution then fast forward to then.
if (nextWeekToCheckpoint == 0) {
// Disallow checkpointing before `startTime`.
require(block.timestamp > _startTime, "Fee distribution has not started yet");
nextWeekToCheckpoint = Math.max(_startTime, _roundUpTimestamp(nextUserPoint.ts));
userState.startTime = uint64(nextWeekToCheckpoint);
}
// It's safe to increment `userEpoch` and `nextWeekToCheckpoint` in this loop as epochs and timestamps
// are always much smaller than 2^256 and are being incremented by small values.
IVotingEscrow.Point memory currentUserPoint;
for (uint256 i = 0; i < 50;) {
unchecked {
if (nextWeekToCheckpoint >= nextUserPoint.ts && userEpoch <= maxUserEpoch) {
// The week being considered is contained in a user epoch after that described by `currentUserPoint`.
// We then shift `nextUserPoint` into `currentUserPoint` and query the Point for the next user epoch.
// We do this in order to step though epochs until we find the first epoch starting after
// `nextWeekToCheckpoint`, making the previous epoch the one that contains `nextWeekToCheckpoint`.
userEpoch += 1;
currentUserPoint = nextUserPoint;
if (userEpoch > maxUserEpoch) {
nextUserPoint = IVotingEscrow.Point(0, 0, 0, 0);
} else {
nextUserPoint = _votingEscrow.user_point_history(user, userEpoch);
}
} else {
// The week being considered lies inside the user epoch described by `oldUserPoint`
// we can then use it to calculate the user's balance at the beginning of the week.
if (nextWeekToCheckpoint >= block.timestamp) {
// Break if we're trying to cache the user's balance at a timestamp in the future.
// We only perform this check here to ensure that we can still process checkpoints created
// in the current week.
break;
}
int128 dt = SafeCast.toInt128(SafeCast.toInt256(nextWeekToCheckpoint - currentUserPoint.ts));
uint256 userBalance = currentUserPoint.bias > currentUserPoint.slope * dt
? uint256(SafeCast.toUint256(currentUserPoint.bias - currentUserPoint.slope * dt))
: 0;
// User's lock has expired and they haven't relocked yet.
if (userBalance == 0 && userEpoch > maxUserEpoch) {
nextWeekToCheckpoint = _roundUpTimestamp(block.timestamp);
break;
}
// User had a nonzero lock and so is eligible to collect fees.
_userBalanceAtTimestamp[user][nextWeekToCheckpoint] = userBalance;
nextWeekToCheckpoint += 1 weeks;
}
++i;
}
}
// We subtract off 1 from the userEpoch to step back once so that on the next attempt to checkpoint
// the current `currentUserPoint` will be loaded as `nextUserPoint`. This ensures that we can't skip over the
// user epoch containing `nextWeekToCheckpoint`.
unchecked {
// userEpoch > 0 so this is safe.
userState.lastEpochCheckpointed = uint64(userEpoch - 1);
}
userState.timeCursor = uint64(nextWeekToCheckpoint);
}
/**
* @dev Cache the totalSupply of VotingEscrow token at the beginning of each new week
*/
function _checkpointTotalSupply() internal {
uint256 nextWeekToCheckpoint = _timeCursor;
uint256 weekStart = _roundDownTimestamp(block.timestamp);
// We expect `timeCursor == weekStart + 1 weeks` when fully up to date.
if (nextWeekToCheckpoint > weekStart || weekStart == block.timestamp) {
// We've already checkpointed up to this week so perform early return
return;
}
_votingEscrow.checkpoint();
// Step through the each week and cache the total supply at beginning of week on this contract
for (uint256 i = 0; i < 20;) {
unchecked {
if (nextWeekToCheckpoint > weekStart) break;
_veSupplyCache[nextWeekToCheckpoint] = _votingEscrow.totalSupply(nextWeekToCheckpoint);
// This is safe as we're incrementing a timestamp
nextWeekToCheckpoint += 1 weeks;
++i;
}
}
// Update state to the end of the current week (`weekStart` + 1 weeks)
_timeCursor = nextWeekToCheckpoint;
}
// Helper functions
/**
* @dev Wrapper around `_userTokenTimeCursor` which returns the start timestamp for `token`
* if `user` has not attempted to interact with it previously.
*/
function _getUserTokenTimeCursor(address user, ERC20 token) internal view returns (uint256) {
uint256 userTimeCursor = _userTokenTimeCursor[user][token];
if (userTimeCursor > 0) return userTimeCursor;
// This is the first time that the user has interacted with this token.
// We then start from the latest out of either when `user` first locked vetoken or `token` was first checkpointed.
return Math.max(_userState[user].startTime, _tokenState[token].startTime);
}
/**
* @dev Return the user epoch number for `user` corresponding to the provided `timestamp`
*/
function _findTimestampUserEpoch(address user, uint256 timestamp, uint256 minUserEpoch, uint256 maxUserEpoch)
internal
view
returns (uint256)
{
uint256 min = minUserEpoch;
uint256 max = maxUserEpoch;
// Perform binary search through epochs to find epoch containing `timestamp`
for (uint256 i = 0; i < 128;) {
unchecked {
if (min >= max) break;
// Algorithm assumes that inputs are less than 2^128 so this operation is safe.
// +2 avoids getting stuck in min == mid < max
uint256 mid = (min + max + 2) / 2;
IVotingEscrow.Point memory pt = _votingEscrow.user_point_history(user, mid);
if (pt.ts <= timestamp) {
min = mid;
} else {
// max > min so this is safe.
max = mid - 1;
}
}
}
return min;
}
/**
* @dev Rounds the provided timestamp down to the beginning of the previous week (Thurs 00:00 UTC)
*/
function _roundDownTimestamp(uint256 timestamp) private pure returns (uint256) {
unchecked {
// Division by zero or overflows are impossible here.
return (timestamp / 1 weeks) * 1 weeks;
}
}
/**
* @dev Rounds the provided timestamp up to the beginning of the next week (Thurs 00:00 UTC)
*/
function _roundUpTimestamp(uint256 timestamp) private pure returns (uint256) {
unchecked {
// Overflows are impossible here for all realistic inputs.
return _roundDownTimestamp(timestamp + 1 weeks - 1);
}
}
}
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
import {ERC20} from "solmate/tokens/ERC20.sol";
import "./IVotingEscrow.sol";
/**
* @title Fee Distributor
* @notice Distributes any tokens transferred to the contract (e.g. Protocol fees and any BAL emissions) among veBAL
* holders proportionally based on a snapshot of the week at which the tokens are sent to the FeeDistributor contract.
* @dev Supports distributing arbitrarily many different tokens. In order to start distributing a new token to veBAL
* holders simply transfer the tokens to the `FeeDistributor` contract and then call `checkpointToken`.
*/
interface IFeeDistributor {
event TokenCheckpointed(ERC20 token, uint256 amount, uint256 lastCheckpointTimestamp);
event TokensClaimed(address user, ERC20 token, uint256 amount, uint256 userTokenTimeCursor);
/**
* @notice Returns the VotingEscrow (veBAL) token contract
*/
function getVotingEscrow() external view returns (IVotingEscrow);
/**
* @notice Returns the global time cursor representing the most earliest uncheckpointed week.
*/
function getTimeCursor() external view returns (uint256);
/**
* @notice Returns the user-level time cursor representing the most earliest uncheckpointed week.
* @param user - The address of the user to query.
*/
function getUserTimeCursor(address user) external view returns (uint256);
/**
* @notice Returns the token-level time cursor storing the timestamp at up to which tokens have been distributed.
* @param token - The ERC20 token address to query.
*/
function getTokenTimeCursor(ERC20 token) external view returns (uint256);
/**
* @notice Returns the user-level time cursor storing the timestamp of the latest token distribution claimed.
* @param user - The address of the user to query.
* @param token - The ERC20 token address to query.
*/
function getUserTokenTimeCursor(address user, ERC20 token) external view returns (uint256);
/**
* @notice Returns the user's cached balance of veBAL as of the provided timestamp.
* @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
* This function requires `user` to have been checkpointed past `timestamp` so that their balance is cached.
* @param user - The address of the user of which to read the cached balance of.
* @param timestamp - The timestamp at which to read the `user`'s cached balance at.
*/
function getUserBalanceAtTimestamp(address user, uint256 timestamp) external view returns (uint256);
/**
* @notice Returns the cached total supply of veBAL as of the provided timestamp.
* @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
* This function requires the contract to have been checkpointed past `timestamp` so that the supply is cached.
* @param timestamp - The timestamp at which to read the cached total supply at.
*/
function getTotalSupplyAtTimestamp(uint256 timestamp) external view returns (uint256);
/**
* @notice Returns the FeeDistributor's cached balance of `token`.
*/
function getTokenLastBalance(ERC20 token) external view returns (uint256);
/**
* @notice Returns the amount of `token` which the FeeDistributor received in the week beginning at `timestamp`.
* @param token - The ERC20 token address to query.
* @param timestamp - The timestamp corresponding to the beginning of the week of interest.
*/
function getTokensDistributedInWeek(ERC20 token, uint256 timestamp) external view returns (uint256);
// Depositing
/**
* @notice Deposits tokens to be distributed in the current week.
* @dev Sending tokens directly to the FeeDistributor instead of using `depositTokens` may result in tokens being
* retroactively distributed to past weeks, or for the distribution to carry over to future weeks.
*
* If for some reason `depositTokens` cannot be called, in order to ensure that all tokens are correctly distributed
* manually call `checkpointToken` before and after the token transfer.
* @param token - The ERC20 token address to distribute.
* @param amount - The amount of tokens to deposit.
*/
function depositToken(ERC20 token, uint256 amount) external;
/**
* @notice Deposits tokens to be distributed in the current week.
* @dev A version of `depositToken` which supports depositing multiple `tokens` at once.
* See `depositToken` for more details.
* @param tokens - An array of ERC20 token addresses to distribute.
* @param amounts - An array of token amounts to deposit.
*/
function depositTokens(ERC20[] calldata tokens, uint256[] calldata amounts) external;
// Checkpointing
/**
* @notice Caches the total supply of veBAL at the beginning of each week.
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
*/
function checkpoint() external;
/**
* @notice Caches the user's balance of veBAL at the beginning of each week.
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
* @param user - The address of the user to be checkpointed.
*/
function checkpointUser(address user) external;
/**
* @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
* @dev Any `token` balance held by the FeeDistributor above that which is returned by `getTokenLastBalance`
* will be distributed evenly across the time period since `token` was last checkpointed.
*
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
* @param token - The ERC20 token address to be checkpointed.
*/
function checkpointToken(ERC20 token) external;
/**
* @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
* @dev A version of `checkpointToken` which supports checkpointing multiple tokens.
* See `checkpointToken` for more details.
* @param tokens - An array of ERC20 token addresses to be checkpointed.
*/
function checkpointTokens(ERC20[] calldata tokens) external;
// Claiming
/**
* @notice Claims all pending distributions of the provided token for a user.
* @dev It's not necessary to explicitly checkpoint before calling this function, it will ensure the FeeDistributor
* is up to date before calculating the amount of tokens to be claimed.
* @param user - The user on behalf of which to claim.
* @param token - The ERC20 token address to be claimed.
* @return The amount of `token` sent to `user` as a result of claiming.
*/
function claimToken(address user, ERC20 token) external returns (uint256);
/**
* @notice Claims a number of tokens on behalf of a user.
* @dev A version of `claimToken` which supports claiming multiple `tokens` on behalf of `user`.
* See `claimToken` for more details.
* @param user - The user on behalf of which to claim.
* @param tokens - An array of ERC20 token addresses to be claimed.
* @return An array of the amounts of each token in `tokens` sent to `user` as a result of claiming.
*/
function claimTokens(address user, ERC20[] calldata tokens) external returns (uint256[] memory);
}
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity >=0.6.11;
/**
* @dev Interface for the OptionalOnlyCaller helper, used to opt in to a caller
* verification for a given address to methods that are otherwise callable by any address.
*/
interface IOptionalOnlyCaller {
/**
* @dev Emitted every time setOnlyCallerCheck is called.
*/
event OnlyCallerOptIn(address user, bool enabled);
/**
* @dev Enables / disables verification mechanism for caller.
* @param enabled - True if caller verification shall be enabled, false otherwise.
*/
function setOnlyCallerCheck(bool enabled) external;
function setOnlyCallerCheckWithSignature(address user, bool enabled, bytes memory signature) external;
/**
* @dev Returns true if caller verification is enabled for the given user, false otherwise.
*/
function isOnlyCallerEnabled(address user) external view returns (bool);
}
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity >=0.6.11;
/**
* @dev Interface for the SignatureValidator helper, used to support meta-transactions.
*/
interface ISignaturesValidator {
/**
* @dev Returns the EIP712 domain separator.
*/
function getDomainSeparator() external view returns (bytes32);
/**
* @dev Returns the next nonce used by an address to sign messages.
*/
function getNextNonce(address user) external view returns (uint256);
}
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity >=0.6.11;
pragma experimental ABIEncoderV2;
// For compatibility, we're keeping the same function names as in the original Curve code, including the mixed-case
// naming convention.
// solhint-disable func-name-mixedcase
interface IVotingEscrow {
struct Point {
int128 bias;
int128 slope; // - dweight / dt
uint256 ts;
uint256 blk; // block
}
function epoch() external view returns (uint256);
function totalSupply(uint256 timestamp) external view returns (uint256);
function user_point_epoch(address user) external view returns (uint256);
function point_history(uint256 timestamp) external view returns (Point memory);
function user_point_history(address user, uint256 timestamp) external view returns (Point memory);
function checkpoint() external;
function admin() external view returns (address);
function smart_wallet_checker() external view returns (address);
function commit_smart_wallet_checker(address newSmartWalletChecker) external;
function apply_smart_wallet_checker() external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
import "./EOASignaturesValidator.sol";
import "../interfaces/IOptionalOnlyCaller.sol";
abstract contract OptionalOnlyCaller is IOptionalOnlyCaller, EOASignaturesValidator {
error OptionalOnlyCaller__SenderNotAllowed();
mapping(address => bool) private _isOnlyCallerEnabled;
uint256 private constant INVALID_SIGNATURE_ERROR = 1;
bytes32 private constant _SET_ONLY_CALLER_CHECK_TYPEHASH =
keccak256("SetOnlyCallerCheck(address user,bool enabled,uint256 nonce)");
/**
* @dev Reverts if the verification mechanism is enabled and the given address is not the caller.
* @param user - Address to validate as the only allowed caller, if the verification is enabled.
*/
modifier optionalOnlyCaller(address user) {
_verifyCaller(user);
_;
}
function setOnlyCallerCheck(bool enabled) external override {
_setOnlyCallerCheck(msg.sender, enabled);
}
function setOnlyCallerCheckWithSignature(address user, bool enabled, bytes memory signature) external override {
bytes32 structHash = keccak256(abi.encode(_SET_ONLY_CALLER_CHECK_TYPEHASH, user, enabled, getNextNonce(user)));
_ensureValidSignature(user, structHash, signature, INVALID_SIGNATURE_ERROR);
_setOnlyCallerCheck(user, enabled);
}
function _setOnlyCallerCheck(address user, bool enabled) private {
_isOnlyCallerEnabled[user] = enabled;
emit OnlyCallerOptIn(user, enabled);
}
function isOnlyCallerEnabled(address user) external view override returns (bool) {
return _isOnlyCallerEnabled[user];
}
function _verifyCaller(address user) private view {
if (_isOnlyCallerEnabled[user]) {
if (msg.sender != user) {
revert OptionalOnlyCaller__SenderNotAllowed();
}
}
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toUint216(uint256 value) internal pure returns (uint216) {
require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toUint208(uint256 value) internal pure returns (uint208) {
require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toUint200(uint256 value) internal pure returns (uint200) {
require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toUint184(uint256 value) internal pure returns (uint184) {
require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toUint176(uint256 value) internal pure returns (uint176) {
require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toUint168(uint256 value) internal pure returns (uint168) {
require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toUint152(uint256 value) internal pure returns (uint152) {
require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toUint144(uint256 value) internal pure returns (uint144) {
require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toUint136(uint256 value) internal pure returns (uint136) {
require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v2.5._
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toUint120(uint256 value) internal pure returns (uint120) {
require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toUint112(uint256 value) internal pure returns (uint112) {
require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toUint104(uint256 value) internal pure returns (uint104) {
require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.2._
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toUint80(uint256 value) internal pure returns (uint80) {
require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toUint72(uint256 value) internal pure returns (uint72) {
require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v2.5._
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toUint56(uint256 value) internal pure returns (uint56) {
require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toUint40(uint256 value) internal pure returns (uint40) {
require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v2.5._
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toUint24(uint256 value) internal pure returns (uint24) {
require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v2.5._
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v2.5._
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*
* _Available since v3.0._
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.7._
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.7._
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*
* _Available since v3.0._
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
{
"compilationTarget": {
"src/FeeDistributor.sol": "FeeDistributor"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000000
},
"remappings": [
":create3-factory/=lib/create3-factory/src/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
":solmate/=lib/solmate/src/"
]
}
[{"inputs":[{"internalType":"contract IVotingEscrow","name":"votingEscrow","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EOASignaturesValidator__ExpiredSignature","type":"error"},{"inputs":[],"name":"EOASignaturesValidator__MalformedSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"EOASignaturesValidator__RevertWithErrorCode","type":"error"},{"inputs":[],"name":"FeeDistributor__CannotStartBeforeCurrentWeek","type":"error"},{"inputs":[],"name":"FeeDistributor__InputLengthMismatch","type":"error"},{"inputs":[],"name":"FeeDistributor__VotingEscrowZeroTotalSupply","type":"error"},{"inputs":[],"name":"OptionalOnlyCaller__SenderNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"OnlyCallerOptIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lastCheckpointTimestamp","type":"uint256"}],"name":"TokenCheckpointed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"contract ERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userTokenTimeCursor","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"checkpointToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20[]","name":"tokens","type":"address[]"}],"name":"checkpointTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"checkpointUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"claimToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract ERC20[]","name":"tokens","type":"address[]"}],"name":"claimTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"depositTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDomainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getNextNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"getTokenLastBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"getTokenTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getTokensDistributedInWeek","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getTotalSupplyAtTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getUserBalanceAtTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"getUserTokenTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVotingEscrow","outputs":[{"internalType":"contract IVotingEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isOnlyCallerEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setOnlyCallerCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"setOnlyCallerCheckWithSignature","outputs":[],"stateMutability":"nonpayable","type":"function"}]