// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./EntropyStructs.sol";
interface EntropyEvents {
event Registered(EntropyStructs.ProviderInfo provider);
event Requested(EntropyStructs.Request request);
event RequestedWithCallback(
address indexed provider,
address indexed requestor,
uint64 indexed sequenceNumber,
bytes32 userRandomNumber,
EntropyStructs.Request request
);
event Revealed(
EntropyStructs.Request request,
bytes32 userRevelation,
bytes32 providerRevelation,
bytes32 blockHash,
bytes32 randomNumber
);
event RevealedWithCallback(
EntropyStructs.Request request,
bytes32 userRandomNumber,
bytes32 providerRevelation,
bytes32 randomNumber
);
event ProviderFeeUpdated(address provider, uint128 oldFee, uint128 newFee);
event ProviderUriUpdated(address provider, bytes oldUri, bytes newUri);
event ProviderFeeManagerUpdated(
address provider,
address oldFeeManager,
address newFeeManager
);
event Withdrawal(
address provider,
address recipient,
uint128 withdrawnAmount
);
}
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
contract EntropyStructs {
struct ProviderInfo {
uint128 feeInWei;
uint128 accruedFeesInWei;
// The commitment that the provider posted to the blockchain, and the sequence number
// where they committed to this. This value is not advanced after the provider commits,
// and instead is stored to help providers track where they are in the hash chain.
bytes32 originalCommitment;
uint64 originalCommitmentSequenceNumber;
// Metadata for the current commitment. Providers may optionally use this field to help
// manage rotations (i.e., to pick the sequence number from the correct hash chain).
bytes commitmentMetadata;
// Optional URI where clients can retrieve revelations for the provider.
// Client SDKs can use this field to automatically determine how to retrieve random values for each provider.
// TODO: specify the API that must be implemented at this URI
bytes uri;
// The first sequence number that is *not* included in the current commitment (i.e., an exclusive end index).
// The contract maintains the invariant that sequenceNumber <= endSequenceNumber.
// If sequenceNumber == endSequenceNumber, the provider must rotate their commitment to add additional random values.
uint64 endSequenceNumber;
// The sequence number that will be assigned to the next inbound user request.
uint64 sequenceNumber;
// The current commitment represents an index/value in the provider's hash chain.
// These values are used to verify requests for future sequence numbers. Note that
// currentCommitmentSequenceNumber < sequenceNumber.
//
// The currentCommitment advances forward through the provider's hash chain as values
// are revealed on-chain.
bytes32 currentCommitment;
uint64 currentCommitmentSequenceNumber;
// An address that is authorized to set / withdraw fees on behalf of this provider.
address feeManager;
}
struct Request {
// Storage slot 1 //
address provider;
uint64 sequenceNumber;
// The number of hashes required to verify the provider revelation.
uint32 numHashes;
// Storage slot 2 //
// The commitment is keccak256(userCommitment, providerCommitment). Storing the hash instead of both saves 20k gas by
// eliminating 1 store.
bytes32 commitment;
// Storage slot 3 //
// The number of the block where this request was created.
// Note that we're using a uint64 such that we have an additional space for an address and other fields in
// this storage slot. Although block.number returns a uint256, 64 bits should be plenty to index all of the
// blocks ever generated.
uint64 blockNumber;
// The address that requested this random number.
address requester;
// If true, incorporate the blockhash of blockNumber into the generated random value.
bool useBlockhash;
// If true, the requester will be called back with the generated random value.
bool isRequestWithCallback;
// There are 2 remaining bytes of free space in this slot.
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
interface IDistribution {
error InvalidTick(int32 tick);
error InvalidBinId(uint32 binId);
error CurveIndexNotSupported(uint256 curveIndex);
function lastTick() external returns (int32);
function binIdToTick(bool tokenIsA, uint32 binId) external pure returns (int32);
function ticks(bool tokenIsA) external pure returns (int32[] memory _ticks);
function tickToBinId(bool tokenIsA, int32 _tick) external pure returns (uint32 binId);
function amount(bool tokenIsA, uint256 k, uint256 curveIndex) external pure returns (uint128);
function amounts(bool tokenIsA, uint256 curveIndex) external pure returns (uint128[] memory _amounts);
function quoteBaseline(bool tokenIsA, uint256 k, uint256 curveIndex) external pure returns (uint256 quoteAmount);
function quoteBaselineAtTick(
bool tokenIsA,
int32 tick,
uint256 curveIndex
) external pure returns (uint256 quoteAmount);
function tailAmounts() external pure returns (uint128[] memory _amounts);
function swappedTick(bool tokenIsA) external pure returns (int32);
function amountBaselines() external pure returns (uint256[6] memory baselines);
function tailBaseline() external pure returns (uint256 baseline);
function tailTicks(bool tokenIsA) external pure returns (int32[] memory _ticks);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5805.sol)
pragma solidity ^0.8.20;
import {IVotes} from "../governance/utils/IVotes.sol";
import {IERC6372} from "./IERC6372.sol";
interface IERC5805 is IERC6372, IVotes {}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC6372.sol)
pragma solidity ^0.8.20;
interface IERC6372 {
/**
* @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
*/
function clock() external view returns (uint48);
/**
* @dev Description of the clock
*/
// solhint-disable-next-line func-name-mixedcase
function CLOCK_MODE() external view returns (string memory);
}
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "./EntropyEvents.sol";
interface IEntropy is EntropyEvents {
// Register msg.sender as a randomness provider. The arguments are the provider's configuration parameters
// and initial commitment. Re-registering the same provider rotates the provider's commitment (and updates
// the feeInWei).
//
// chainLength is the number of values in the hash chain *including* the commitment, that is, chainLength >= 1.
function register(
uint128 feeInWei,
bytes32 commitment,
bytes calldata commitmentMetadata,
uint64 chainLength,
bytes calldata uri
) external;
// Withdraw a portion of the accumulated fees for the provider msg.sender.
// Calling this function will transfer `amount` wei to the caller (provided that they have accrued a sufficient
// balance of fees in the contract).
function withdraw(uint128 amount) external;
// Withdraw a portion of the accumulated fees for provider. The msg.sender must be the fee manager for this provider.
// Calling this function will transfer `amount` wei to the caller (provided that they have accrued a sufficient
// balance of fees in the contract).
function withdrawAsFeeManager(address provider, uint128 amount) external;
// As a user, request a random number from `provider`. Prior to calling this method, the user should
// generate a random number x and keep it secret. The user should then compute hash(x) and pass that
// as the userCommitment argument. (You may call the constructUserCommitment method to compute the hash.)
//
// This method returns a sequence number. The user should pass this sequence number to
// their chosen provider (the exact method for doing so will depend on the provider) to retrieve the provider's
// number. The user should then call fulfillRequest to construct the final random number.
//
// This method will revert unless the caller provides a sufficient fee (at least getFee(provider)) as msg.value.
// Note that excess value is *not* refunded to the caller.
function request(
address provider,
bytes32 userCommitment,
bool useBlockHash
) external payable returns (uint64 assignedSequenceNumber);
// Request a random number. The method expects the provider address and a secret random number
// in the arguments. It returns a sequence number.
//
// The address calling this function should be a contract that inherits from the IEntropyConsumer interface.
// The `entropyCallback` method on that interface will receive a callback with the generated random number.
//
// This method will revert unless the caller provides a sufficient fee (at least getFee(provider)) as msg.value.
// Note that excess value is *not* refunded to the caller.
function requestWithCallback(
address provider,
bytes32 userRandomNumber
) external payable returns (uint64 assignedSequenceNumber);
// Fulfill a request for a random number. This method validates the provided userRandomness and provider's proof
// against the corresponding commitments in the in-flight request. If both values are validated, this function returns
// the corresponding random number.
//
// Note that this function can only be called once per in-flight request. Calling this function deletes the stored
// request information (so that the contract doesn't use a linear amount of storage in the number of requests).
// If you need to use the returned random number more than once, you are responsible for storing it.
function reveal(
address provider,
uint64 sequenceNumber,
bytes32 userRevelation,
bytes32 providerRevelation
) external returns (bytes32 randomNumber);
// Fulfill a request for a random number. This method validates the provided userRandomness
// and provider's revelation against the corresponding commitment in the in-flight request. If both values are validated
// and the requestor address is a contract address, this function calls the requester's entropyCallback method with the
// sequence number, provider address and the random number as arguments. Else if the requestor is an EOA, it won't call it.
//
// Note that this function can only be called once per in-flight request. Calling this function deletes the stored
// request information (so that the contract doesn't use a linear amount of storage in the number of requests).
// If you need to use the returned random number more than once, you are responsible for storing it.
//
// Anyone can call this method to fulfill a request, but the callback will only be made to the original requester.
function revealWithCallback(
address provider,
uint64 sequenceNumber,
bytes32 userRandomNumber,
bytes32 providerRevelation
) external;
function getProviderInfo(
address provider
) external view returns (EntropyStructs.ProviderInfo memory info);
function getDefaultProvider() external view returns (address provider);
function getRequest(
address provider,
uint64 sequenceNumber
) external view returns (EntropyStructs.Request memory req);
function getFee(address provider) external view returns (uint128 feeAmount);
function getAccruedPythFees()
external
view
returns (uint128 accruedPythFeesInWei);
function setProviderFee(uint128 newFeeInWei) external;
function setProviderFeeAsFeeManager(
address provider,
uint128 newFeeInWei
) external;
function setProviderUri(bytes calldata newUri) external;
// Set manager as the fee manager for the provider msg.sender.
// After calling this function, manager will be able to set the provider's fees and withdraw them.
// Only one address can be the fee manager for a provider at a time -- calling this function again with a new value
// will override the previous value. Call this function with the all-zero address to disable the fee manager role.
function setFeeManager(address manager) external;
function constructUserCommitment(
bytes32 userRandomness
) external pure returns (bytes32 userCommitment);
function combineRandomValues(
bytes32 userRandomness,
bytes32 providerRandomness,
bytes32 blockHash
) external pure returns (bytes32 combinedRandomness);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IFeeVault {
event DepositAmount(IERC20 indexed token, address indexed account, uint256 amount);
event Claim(IERC20 indexed token, address indexed account, uint256 amount);
/**
* @notice View the balance of a given address for a given token.
*/
function tokenUserToBalance(IERC20 token, address user) external view returns (uint256 balance);
/**
* @notice Transfers any fee set aside for sender to the sender.
*/
function claim(IERC20 token) external returns (uint256 amount);
/**
* @notice Deposit token amounts to addresses. This function will
* transfer the sum amounts to the FeeVault. The amounts can later be
* `claim`ed at any time by the respective deposited address.
*/
function depositAmount(IERC20 token, address addr1, uint256 amount1) external;
/**
* @notice Deposit token amounts to addresses. This function will
* transfer the sum amounts to the FeeVault. The amounts can later be
* `claim`ed at any time by the respective deposited address.
*/
function depositAmounts(
IERC20 token,
address addr1,
uint256 amount1,
address addr2,
uint256 amount2,
address addr3,
uint256 amount3,
address addr4,
uint256 amount4
) external;
/**
* @notice Deposit token amounts to addresses. This function will
* transfer the sum amounts to the FeeVault. The amounts can later be
* `claim`ed at any time by the respective deposited address.
*/
function depositAmounts(IERC20 token, address addr1, uint256 amount1, address addr2, uint256 amount2) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @notice Adds support for tracking historical balance on ERC20 and adds
* support for contributing and retrieving incentives pro-rata of historical
* balanceOf.
*
* @notice Uses a timestamp-based clock for checkpoints as opposed to the
* default OZ implementation that is blocknumber based.
*/
interface IHistoricalBalanceNonTransferableERC20 is IERC20 {
error ERC5805FutureLookup(uint256 timepoint, uint48 clock);
error TransferNotAllowed();
error OnlyMinter(address sender, address minter);
event NewTopHolder(address topHolder, uint256 topHolderBalance);
event NewTopDayHolder(uint256 dayNumber, address topHolder, uint256 topHolderBalance);
struct AddressBalance {
address holder;
uint256 balance;
}
/**
* @notice This function retrieves the historical balance of an account at
* a specific point in time.
* @param account The address of the account for which to retrieve the
* historical balance.
* @param timepoint The timepoint (block number or timestamp depending on
* implementation) at which to query the balance (uint256).
* @return balance The balance of the account at the specified timepoint.
*/
function getPastBalanceOf(address account, uint256 timepoint) external view returns (uint256 balance);
/**
* @notice Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*
* NOTE: This value is the sum of all available balance.
*/
function getPastTotalSupply(uint256 timepoint) external view returns (uint256);
/**
* @notice Token units this trackers tracks
*/
function trackerToken() external view returns (IERC20);
/**
* @notice Account that can mint tokens
*/
function minter() external view returns (address);
/**
* @notice Mint token
*/
function mint(address recipient, uint256 amount) external;
/**
* @notice Account/Balance that has the highest balance
*/
function topAccount() external view returns (address, uint256);
/**
* @notice Account/Balance of the top holder for given day
*/
function topAccountByDay(uint256 dayNumber) external view returns (address, uint256);
/**
* @notice Account/Balance of the top holder for given day
*/
function topAccountCurrentDay() external view returns (address, uint256);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ITokenManager} from "./ITokenManager.sol";
import {ITokenManagerLens} from "./ITokenManagerLens.sol";
import {ISwapper} from "./ISwapper.sol";
import {IMaverickV2Pool} from "../v2-common/interfaces/IMaverickV2Pool.sol";
import {IMaverickV2Factory} from "../v2-common/interfaces/IMaverickV2Factory.sol";
interface ILaunchFactory {
error InvalidLaunchFee(uint256 valueSent, uint256 valueRequired);
error InvalidPoolFee(uint256 fee, uint256 feeMinAllowed, uint256 feeMaxAllowed);
error InvalidBorrowFeeRate();
error AllLiquidityDeployed();
error MainLiquidityDeployed();
error MainLiquidityNotDeployed();
error NameSymbolAlreadyDeployed();
struct TokenData {
string name;
string symbol;
string imageHash;
string metadataHash;
}
struct BorrowFeeRates {
uint64 proportionToVotingDistributorD18;
uint64 proportionToCreatorD18;
uint64 proportionToVoterD18;
}
struct PoolData {
uint64 buyFee;
uint64 sellFee;
uint8 curveIndex;
}
struct TempLaunchData {
TokenData tokenData;
IERC20 token;
IMaverickV2Pool pool;
bool borrowingEnabled;
address feeRecipient;
PoolData poolData;
}
event CreateTokenManager(
IERC20 indexed token,
IMaverickV2Pool indexed pool,
ITokenManager indexed tokenManager,
address feeRecipient,
bool borrowingEnabled,
TokenData tokenData,
PoolData poolData,
IERC20 quoteToken,
bool tokenIsA,
uint256 ethLaunchFeePaid
);
event SetProtocolFeeCollector(address protocolFeeCollector, bool notifyFeeCollector);
event SetLaunchFeeCollector(address launchFeeCollector);
event SetSwapper(ISwapper swapper);
event SetBorrowFeeRateD18(uint256 borrowFeeRate);
event SetEthLaunchFee(uint128 ethLaunchFee);
event SetProtocolFee(uint128 protocolFeeProportionD18);
event SetBorrowFeeRates(BorrowFeeRates rates);
event DeployTailLiqudiity(IERC20 indexed token, IMaverickV2Pool indexed pool, ITokenManager indexed tokenManager);
event DeployMainLiqudiity(IERC20 indexed token, IMaverickV2Pool indexed pool, ITokenManager indexed tokenManager);
event AddFreeMinter(address user);
event RemoveFreeMinter(address user);
function tempLaunchData() external view returns (TempLaunchData memory);
/**
* @notice Indicator of whether an address can mint a token without paying
* the eth fee
*/
function freeMinter(address) external view returns (bool);
/**
* @notice Gets the ratio of fees going to voter/creator/protocol
*/
function borrowFeeRates()
external
view
returns (uint64 proportionToVotingDistributorD18, uint64 proportionToCreatorD18, uint64 proportionToVoterD18);
/**
* @notice Gets the ETH launch fee for creating a new token manager
*/
function ethLaunchFee() external view returns (uint128);
/**
* @notice Gets Borrowing fee rate
*/
function borrowFeeRateD18() external view returns (uint256);
/**
* @notice Gets the quote contract address
*/
function quoteToken() external view returns (IERC20);
/**
* @notice Gets the Token Manager Lens contract
*/
function lens() external view returns (ITokenManagerLens);
/**
* @notice Gets the Maverick V2 Factory contract
*/
function factory() external view returns (IMaverickV2Factory);
/**
* @notice Gets the launch fee collector address
*/
function launchFeeCollector() external view returns (address);
/**
* @notice Checks if a given Token Manager is managed by this factory
*/
function isFactoryManager(ITokenManager tokenManager) external view returns (bool);
/**
* @notice Checks if a given token has been created by this factory
*/
function isFactoryToken(IERC20 token) external view returns (bool);
/**
* @notice Checks if a given pool has been created by this factory
*/
function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);
/**
* @notice Indicates if symbol hash has already been deployed
*/
function symbolHashDeployed(bytes32 spaceStrippedLowerCaseSymbolHash) external view returns (bool);
/**
* @notice Gets the Token Manager associated with a given ERC20 token
*/
function managerFromToken(IERC20 token) external view returns (ITokenManager tokenManager);
/**
* @notice True if tail liquidity has been deployed
*/
function tailLiquidityDeployed(IERC20 token) external view returns (bool);
/**
* @notice True if main liquidity has been deployed
*/
function mainLiquidityDeployed(IERC20 token) external view returns (bool);
/**
* @notice Gets the Token Manager associated with a given pool
*/
function managerFromPool(IMaverickV2Pool pool) external view returns (ITokenManager tokenManager);
/**
* @notice Gets the total number of Token Managers created by this factory
*/
function managerCount() external view returns (uint256 _managerCount);
/**
* @notice Gets the factory swapper which is also the permissioned pool accessor
*/
function swapper() external view returns (ISwapper swapper);
/**
* @notice Gets a list of Token Managers within a specified range
* @param startIndex The starting index of the range (inclusive)
* @param endIndex The ending index of the range (exclusive)
of Token Manager contracts
*/
function managers(uint256 startIndex, uint256 endIndex) external view returns (ITokenManager[] memory);
/**
* @notice Creates a new Token Manager contract
* @param tokenData The name/symbol and ipfs data of the new token
* @param poolData The pool fee/distribution parameters
* @param feeRecipient The address that will receive the fees
* @param borrowingEnabled Whether borrowing is enabled for the new token
* @param tokenSalt Salt for token create2
* @return tokenManager The newly created Token Manager contract
*/
function createTokenManager(
TokenData memory tokenData,
PoolData memory poolData,
address feeRecipient,
bool borrowingEnabled,
string memory tokenSalt
) external payable returns (ITokenManager tokenManager);
/**
* @notice Creates a new Token Manager contract but does not add liquidity to pool.
* @param tokenData The name/symbol and ipfs data of the new token
* @param poolData The pool fee/distribution parameters
* @param feeRecipient The address that will receive the fees
* @param borrowingEnabled Whether borrowing is enabled for the new token
* @param tokenSalt Salt for token create2
* @return tokenManager The newly created Token Manager contract
*/
function createTokenManagerWithoutLiquidity(
TokenData memory tokenData,
PoolData memory poolData,
address feeRecipient,
bool borrowingEnabled,
string memory tokenSalt
) external payable returns (ITokenManager tokenManager);
/**
* @notice Creates a new Token Manager contract
* @param tokenData The name/symbol and ipfs data of the new token
* @param poolData The pool fee/distribution parameters
* @param feeRecipient The address that will receive the fees
* @param borrowingEnabled Whether borrowing is enabled for the new token
* @param tokenSalt Salt for token create2
* @param tokenRecipient The address to receive the purchased tokens
* @param ethToQuotePool The Maverick pool that can swap eth for quote token
* @param amountOutMinimum The minimum amount of tokens to receive
* @param deployTail True to deploy all supply; false to only deploy main supply
* @return tokenManager The newly created Token Manager contract
* @return amountOut Amount sent with buy
*/
function createTokenManagerAndBuy(
TokenData memory tokenData,
PoolData memory poolData,
address feeRecipient,
bool borrowingEnabled,
string memory tokenSalt,
address tokenRecipient,
IMaverickV2Pool ethToQuotePool,
uint256 amountOutMinimum,
bool deployTail
) external payable returns (ITokenManager tokenManager, uint256 amountOut);
/**
* @notice Deploys a thin layer of liquidity 100 ticks past the end of the
* initial distribution. This is effectively prices the supply out to
* price = inifinity meaning that the launch pool will always have supply to
* sell.
*/
function deployTailLiquidity(IERC20 token) external;
/**
* @notice Deploys main liquidity to a token's pool.
*/
function deployMainLiquidity(IERC20 token) external;
/**
* @notice Init code hash of launch token. Useful for computing create2
* addresses of tokens. Address can be computed in solidity with:
*
* ```
* Create2.computeAddress(
* keccak256(abi.encode(symbol, salt)),
* factory.tokenCreationCodeHash(),
* address(_factory)
* )
*```
*/
function tokenCreationCodeHash() external pure returns (bytes32);
}
// SPDX-License-Identifier: GPL-2.0-or-later
// As the copyright holder of this work, Ubiquity Labs retains
// the right to distribute, use, and modify this code under any license of
// their choosing, in addition to the terms of the GPL-v2 or later.
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IMaverickV2Pool} from "./IMaverickV2Pool.sol";
interface IMaverickV2Factory {
error FactoryInvalidProtocolFeeRatio(uint8 protocolFeeRatioD3);
error FactoryInvalidLendingFeeRate(uint256 protocolLendingFeeRateD18);
error FactoryProtocolFeeOnRenounce(uint8 protocolFeeRatioD3);
error FactorAlreadyInitialized();
error FactorNotInitialized();
error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);
error FactoryInvalidFee();
error FactoryInvalidKinds(uint8 kinds);
error FactoryInvalidTickSpacing(uint256 tickSpacing);
error FactoryInvalidLookback(uint256 lookback);
error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);
error FactoryPoolAlreadyExists(
uint256 feeAIn,
uint256 feeBIn,
uint256 tickSpacing,
uint256 lookback,
IERC20 tokenA,
IERC20 tokenB,
uint8 kinds,
address accessor
);
error FactoryAccessorMustBeNonZero();
event PoolCreated(
IMaverickV2Pool poolAddress,
uint8 protocolFeeRatio,
uint256 feeAIn,
uint256 feeBIn,
uint256 tickSpacing,
uint256 lookback,
int32 activeTick,
IERC20 tokenA,
IERC20 tokenB,
uint8 kinds,
address accessor
);
event SetFactoryProtocolFeeRatio(uint8 protocolFeeRatioD3);
event SetFactoryProtocolLendingFeeRate(uint256 lendingFeeRateD18);
event SetFactoryProtocolFeeReceiver(address receiver);
struct DeployParameters {
uint64 feeAIn;
uint64 feeBIn;
uint32 lookback;
int32 activeTick;
uint64 tokenAScale;
uint64 tokenBScale;
// slot
IERC20 tokenA;
// slot
IERC20 tokenB;
// slot
uint16 tickSpacing;
uint8 options;
address accessor;
}
/**
* @notice Called by deployer library to initialize a pool.
*/
function deployParameters()
external
view
returns (
uint64 feeAIn,
uint64 feeBIn,
uint32 lookback,
int32 activeTick,
uint64 tokenAScale,
uint64 tokenBScale,
// slot
IERC20 tokenA,
// slot
IERC20 tokenB,
// slot
uint16 tickSpacing,
uint8 options,
address accessor
);
/**
* @notice Create a new MaverickV2Pool with symmetric swap fees.
* @param fee Fraction of the pool swap amount that is retained as an LP in
* D18 scale.
* @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the
* bin width.
* @param lookback Pool lookback in seconds.
* @param tokenA Address of tokenA.
* @param tokenB Address of tokenB.
* @param activeTick Tick position that contains the active bins.
* @param kinds 1-15 number to represent the active kinds
* 0b0001 = static;
* 0b0010 = right;
* 0b0100 = left;
* 0b1000 = both.
* E.g. a pool with all 4 modes will have kinds = b1111 = 15
*/
function create(
uint64 fee,
uint16 tickSpacing,
uint32 lookback,
IERC20 tokenA,
IERC20 tokenB,
int32 activeTick,
uint8 kinds
) external returns (IMaverickV2Pool);
/**
* @notice Create a new MaverickV2Pool.
* @param feeAIn Fraction of the pool swap amount for tokenA-input swaps
* that is retained as an LP in D18 scale.
* @param feeBIn Fraction of the pool swap amount for tokenB-input swaps
* that is retained as an LP in D18 scale.
* @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the
* bin width.
* @param lookback Pool lookback in seconds.
* @param tokenA Address of tokenA.
* @param tokenB Address of tokenB.
* @param activeTick Tick position that contains the active bins.
* @param kinds 1-15 number to represent the active kinds
* 0b0001 = static;
* 0b0010 = right;
* 0b0100 = left;
* 0b1000 = both.
* e.g. a pool with all 4 modes will have kinds = b1111 = 15
*/
function create(
uint64 feeAIn,
uint64 feeBIn,
uint16 tickSpacing,
uint32 lookback,
IERC20 tokenA,
IERC20 tokenB,
int32 activeTick,
uint8 kinds
) external returns (IMaverickV2Pool);
/**
* @notice Create a new MaverickV2PoolPermissioned with symmetric swap fees
* with all functions permissioned. Set fee to zero to make the pool fee settable by the accessor.
* @param fee Fraction of the pool swap amount that is retained as an LP in
* D18 scale.
* @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the
* bin width.
* @param lookback Pool lookback in seconds.
* @param tokenA Address of tokenA.
* @param tokenB Address of tokenB.
* @param activeTick Tick position that contains the active bins.
* @param kinds 1-15 number to represent the active kinds
* 0b0001 = static;
* 0b0010 = right;
* 0b0100 = left;
* 0b1000 = both.
* E.g. a pool with all 4 modes will have kinds = b1111 = 15
* @param accessor Only address that can access the pool's public write functions.
*/
function createPermissioned(
uint64 fee,
uint16 tickSpacing,
uint32 lookback,
IERC20 tokenA,
IERC20 tokenB,
int32 activeTick,
uint8 kinds,
address accessor
) external returns (IMaverickV2Pool);
/**
* @notice Create a new MaverickV2PoolPermissioned with all functions
* permissioned. Set fees to zero to make the pool fee settable by the
* accessor.
* @param feeAIn Fraction of the pool swap amount for tokenA-input swaps
* that is retained as an LP in D18 scale.
* @param feeBIn Fraction of the pool swap amount for tokenB-input swaps
* that is retained as an LP in D18 scale.
* @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the
* bin width.
* @param lookback Pool lookback in seconds.
* @param tokenA Address of tokenA.
* @param tokenB Address of tokenB.
* @param activeTick Tick position that contains the active bins.
* @param kinds 1-15 number to represent the active kinds
* 0b0001 = static;
* 0b0010 = right;
* 0b0100 = left;
* 0b1000 = both.
* E.g. a pool with all 4 modes will have kinds = b1111 = 15
* @param accessor only address that can access the pool's public write functions.
*/
function createPermissioned(
uint64 feeAIn,
uint64 feeBIn,
uint16 tickSpacing,
uint32 lookback,
IERC20 tokenA,
IERC20 tokenB,
int32 activeTick,
uint8 kinds,
address accessor
) external returns (IMaverickV2Pool);
/**
* @notice Create a new MaverickV2PoolPermissioned with the option to make
* a subset of function permissionless. Set fee to zero to make the pool
* fee settable by the accessor.
* @param feeAIn Fraction of the pool swap amount for tokenA-input swaps
* that is retained as an LP in D18 scale.
* @param feeBIn Fraction of the pool swap amount for tokenB-input swaps
* that is retained as an LP in D18 scale.
* @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the
* bin width.
* @param lookback Pool lookback in seconds.
* @param tokenA Address of tokenA.
* @param tokenB Address of tokenB.
* @param activeTick Tick position that contains the active bins.
* @param kinds 1-15 number to represent the active kinds
* 0b0001 = static;
* 0b0010 = right;
* 0b0100 = left;
* 0b1000 = both.
* E.g. a pool with all 4 modes will have kinds = b1111 = 15
* @param accessor only address that can access the pool's public permissioned write functions.
* @param permissionedLiquidity If true, then only accessor can call
* pool's liquidity management functions: `flashLoan`,
* `migrateBinsUpstack`, `addLiquidity`, `removeLiquidity`.
* @param permissionedSwap If true, then only accessor can call
* pool's swap function.
*/
function createPermissioned(
uint64 feeAIn,
uint64 feeBIn,
uint16 tickSpacing,
uint32 lookback,
IERC20 tokenA,
IERC20 tokenB,
int32 activeTick,
uint8 kinds,
address accessor,
bool permissionedLiquidity,
bool permissionedSwap
) external returns (IMaverickV2Pool pool);
/**
* @notice Update the protocol fee ratio for a pool. Can be called
* permissionlessly allowing any user to sync the pool protocol fee value
* with the factory protocol fee value.
* @param pool The pool for which to update.
*/
function updateProtocolFeeRatioForPool(IMaverickV2Pool pool) external;
/**
* @notice Update the protocol lending fee rate for a pool. Can be called
* permissionlessly allowing any user to sync the pool protocol lending fee
* rate value with the factory value.
* @param pool The pool for which to update.
*/
function updateProtocolLendingFeeRateForPool(IMaverickV2Pool pool) external;
/**
* @notice Claim protocol fee for a pool and transfer it to the protocolFeeReceiver.
* @param pool The pool from which to claim the protocol fee.
* @param isTokenA A boolean indicating whether tokenA (true) or tokenB
* (false) is being collected.
*/
function claimProtocolFeeForPool(IMaverickV2Pool pool, bool isTokenA) external;
/**
* @notice Claim protocol fee for a pool and transfer it to the protocolFeeReceiver.
* @param pool The pool from which to claim the protocol fee.
*/
function claimProtocolFeeForPool(IMaverickV2Pool pool) external;
/**
* @notice Bool indicating whether the pool was deployed from this factory.
*/
function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);
/**
* @notice Address that receives the protocol fee when users call
* `claimProtocolFeeForPool`.
*/
function protocolFeeReceiver() external view returns (address);
/**
* @notice Lookup a pool for given parameters.
*
* @dev options bit map of kinds and function permissions
* 0b000001 = static;
* 0b000010 = right;
* 0b000100 = left;
* 0b001000 = both;
* 0b010000 = liquidity functions are permissioned
* 0b100000 = swap function is permissioned
*/
function lookupPermissioned(
uint256 feeAIn,
uint256 feeBIn,
uint256 tickSpacing,
uint256 lookback,
IERC20 tokenA,
IERC20 tokenB,
uint8 options,
address accessor
) external view returns (IMaverickV2Pool);
/**
* @notice Lookup a pool for given parameters.
*/
function lookupPermissioned(
IERC20 _tokenA,
IERC20 _tokenB,
address accessor,
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2Pool[] memory pools);
/**
* @notice Lookup a pool for given parameters.
*/
function lookupPermissioned(
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2Pool[] memory pools);
/**
* @notice Lookup a pool for given parameters.
*/
function lookup(
uint256 feeAIn,
uint256 feeBIn,
uint256 tickSpacing,
uint256 lookback,
IERC20 tokenA,
IERC20 tokenB,
uint8 kinds
) external view returns (IMaverickV2Pool);
/**
* @notice Lookup a pool for given parameters.
*/
function lookup(
IERC20 _tokenA,
IERC20 _tokenB,
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2Pool[] memory pools);
/**
* @notice Lookup a pool for given parameters.
*/
function lookup(uint256 startIndex, uint256 endIndex) external view returns (IMaverickV2Pool[] memory pools);
/**
* @notice Count of permissionless pools.
*/
function poolCount() external view returns (uint256 _poolCount);
/**
* @notice Count of permissioned pools.
*/
function poolPermissionedCount() external view returns (uint256 _poolCount);
/**
* @notice Count of pools for a given accessor and token pair. For
* permissionless pools, pass `accessor = address(0)`.
*/
function poolByTokenCount(
IERC20 _tokenA,
IERC20 _tokenB,
address accessor
) external view returns (uint256 _poolCount);
/**
* @notice Get the current factory owner.
*/
function owner() external view returns (address);
/**
* @notice Proportion of protocol fee to collect on each swap. Value is in
* 3-decimal format with a maximum value of 0.25e3.
*/
function protocolFeeRatioD3() external view returns (uint8);
/**
* @notice Fee rate charged by the protocol for flashloans. Value is in
* 18-decimal format with a maximum value of 0.02e18.
*/
function protocolLendingFeeRateD18() external view returns (uint256);
}
// SPDX-License-Identifier: GPL-2.0-or-later
// As the copyright holder of this work, Ubiquity Labs retains
// the right to distribute, use, and modify this code under any license of
// their choosing, in addition to the terms of the GPL-v2 or later.
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IMaverickV2Factory} from "./IMaverickV2Factory.sol";
interface IMaverickV2Pool {
error PoolZeroLiquidityAdded();
error PoolMinimumLiquidityNotMet();
error PoolLocked();
error PoolInvalidFee();
error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);
error PoolTicksAmountsLengthMismatch(uint256 ticksLength, uint256 amountsLength);
error PoolBinIdsAmountsLengthMismatch(uint256 binIdsLength, uint256 amountsLength);
error PoolKindNotSupported(uint256 kinds, uint256 kind);
error PoolInsufficientBalance(uint256 deltaLpAmount, uint256 accountBalance);
error PoolReservesExceedMaximum(uint256 amount);
error PoolValueExceedsBits(uint256 amount, uint256 bits);
error PoolTickMaxExceeded(uint256 tick);
error PoolMigrateBinFirst();
error PoolCurrentTickBeyondSwapLimit(int32 startingTick);
error PoolSenderNotAccessor(address sender_, address accessor);
error PoolSenderNotFactory(address sender_, address accessor);
error PoolFunctionNotImplemented();
error PoolTokenNotSolvent(uint256 internalReserve, uint256 tokenBalance, IERC20 token);
event PoolSwap(address sender, address recipient, SwapParams params, uint256 amountIn, uint256 amountOut);
event PoolAddLiquidity(
address sender,
address recipient,
uint256 subaccount,
AddLiquidityParams params,
uint256 tokenAAmount,
uint256 tokenBAmount,
uint32[] binIds
);
event PoolMigrateBinsUpStack(address sender, uint32 binId, uint32 maxRecursion);
event PoolRemoveLiquidity(
address sender,
address recipient,
uint256 subaccount,
RemoveLiquidityParams params,
uint256 tokenAOut,
uint256 tokenBOut
);
event PoolSetVariableFee(uint256 newFeeAIn, uint256 newFeeBIn);
/**
* @notice Tick state parameters.
*/
struct TickState {
uint128 reserveA;
uint128 reserveB;
uint128 totalSupply;
uint32[4] binIdsByTick;
}
/**
* @notice Tick data parameters.
* @param currentReserveA Current reserve of token A.
* @param currentReserveB Current reserve of token B.
* @param currentLiquidity Current liquidity amount.
*/
struct TickData {
uint256 currentReserveA;
uint256 currentReserveB;
uint256 currentLiquidity;
}
/**
* @notice Bin state parameters.
* @param mergeBinBalance LP token balance that this bin possesses of the merge bin.
* @param mergeId Bin ID of the bin that this bin has merged into.
* @param totalSupply Total amount of LP tokens in this bin.
* @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).
* @param tick The lower price tick of the bin in its current state.
* @param tickBalance Balance of the tick.
*/
struct BinState {
uint128 mergeBinBalance;
uint128 tickBalance;
uint128 totalSupply;
uint8 kind;
int32 tick;
uint32 mergeId;
}
/**
* @notice Parameters for swap.
* @param amount Amount of the token that is either the input if exactOutput is false
* or the output if exactOutput is true.
* @param tokenAIn Boolean indicating whether tokenA is the input.
* @param exactOutput Boolean indicating whether the amount specified is
* the exact output amount (true).
* @param tickLimit The furthest tick a swap will execute in. If no limit
* is desired, value should be set to type(int32).max for a tokenAIn swap
* and type(int32).min for a swap where tokenB is the input.
*/
struct SwapParams {
uint256 amount;
bool tokenAIn;
bool exactOutput;
int32 tickLimit;
}
/**
* @notice Parameters associated with adding liquidity.
* @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).
* @param ticks Array of ticks to add liquidity to.
* @param amounts Array of bin LP amounts to add.
*/
struct AddLiquidityParams {
uint8 kind;
int32[] ticks;
uint128[] amounts;
}
/**
* @notice Parameters for each bin that will have liquidity removed.
* @param binIds Index array of the bins losing liquidity.
* @param amounts Array of bin LP amounts to remove.
*/
struct RemoveLiquidityParams {
uint32[] binIds;
uint128[] amounts;
}
/**
* @notice State of the pool.
* @param reserveA Pool tokenA balanceOf at end of last operation
* @param reserveB Pool tokenB balanceOf at end of last operation
* @param lastTwaD8 Value of log time weighted average price at last block.
* Value is 8-decimal scale and is in the fractional tick domain. E.g. a
* value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th
* tick.
* @param lastLogPriceD8 Value of log price at last block. Value is
* 8-decimal scale and is in the fractional tick domain. E.g. a value of
* 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.
* @param lastTimestamp Last block.timestamp value in seconds for latest
* swap transaction.
* @param activeTick Current tick position that contains the active bins.
* @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values
* defined in Pool.sol.
* @param binCounter Index of the last bin created.
* @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the
* protocol.
*/
struct State {
uint128 reserveA;
uint128 reserveB;
int64 lastTwaD8;
int64 lastLogPriceD8;
uint40 lastTimestamp;
int32 activeTick;
bool isLocked;
uint32 binCounter;
uint8 protocolFeeRatioD3;
}
/**
* @notice Internal data used for data passing between Pool and Bin code.
*/
struct BinDelta {
uint128 deltaA;
uint128 deltaB;
}
/**
* @notice 1-15 number to represent the active kinds.
* @notice 0b0001 = static;
* @notice 0b0010 = right;
* @notice 0b0100 = left;
* @notice 0b1000 = both;
*
* E.g. a pool with all 4 modes will have kinds = b1111 = 15
*/
function kinds() external view returns (uint8 _kinds);
/**
* @notice Returns whether a pool has permissioned functions. If true, the
* `accessor()` of the pool can set the pool fees. Other functions in the
* pool may also be permissioned; whether or not they are can be determined
* through calls to `permissionedLiquidity()` and `permissionedSwap()`.
*/
function permissionedPool() external view returns (bool _permissionedPool);
/**
* @notice Returns whether a pool has permissioned liquidity management
* functions. If true, the pool is incompatible with permissioned pool
* liquidity management infrastructure.
*/
function permissionedLiquidity() external view returns (bool _permissionedLiquidity);
/**
* @notice Returns whether a pool has a permissioned swap functions. If
* true, the pool is incompatible with permissioned pool swap router
* infrastructure.
*/
function permissionedSwap() external view returns (bool _permissionedSwap);
/**
* @notice Pool swap fee for the given direction (A-in or B-in swap) in
* 18-decimal format. E.g. 0.01e18 is a 1% swap fee.
*/
function fee(bool tokenAIn) external view returns (uint256);
/**
* @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.
*/
function tickSpacing() external view returns (uint256);
/**
* @notice Lookback period of pool in seconds.
*/
function lookback() external view returns (uint256);
/**
* @notice Address of Pool accessor. This is Zero address for
* permissionless pools.
*/
function accessor() external view returns (address);
/**
* @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.
*/
function tokenA() external view returns (IERC20);
/**
* @notice Pool tokenB.
*/
function tokenB() external view returns (IERC20);
/**
* @notice Deploying factory of the pool and also contract that has ability
* to set and collect protocol fees for the pool.
*/
function factory() external view returns (IMaverickV2Factory);
/**
* @notice Most significant bit of scale value is a flag to indicate whether
* tokenA has more or less than 18 decimals. Scale is used in conjuction
* with Math.toScale/Math.fromScale functions to convert from token amounts
* to D18 scale internal pool accounting.
*/
function tokenAScale() external view returns (uint256);
/**
* @notice Most significant bit of scale value is a flag to indicate whether
* tokenA has more or less than 18 decimals. Scale is used in conjuction
* with Math.toScale/Math.fromScale functions to convert from token amounts
* to D18 scale internal pool accounting.
*/
function tokenBScale() external view returns (uint256);
/**
* @notice ID of bin at input tick position and kind.
*/
function binIdByTickKind(int32 tick, uint256 kind) external view returns (uint32);
/**
* @notice Accumulated tokenA protocol fee.
*/
function protocolFeeA() external view returns (uint128);
/**
* @notice Accumulated tokenB protocol fee.
*/
function protocolFeeB() external view returns (uint128);
/**
* @notice Lending fee rate on flash loans.
*/
function lendingFeeRateD18() external view returns (uint256);
/**
* @notice External function to get the current time-weighted average price.
*/
function getCurrentTwa() external view returns (int256);
/**
* @notice External function to get the state of the pool.
*/
function getState() external view returns (State memory);
/**
* @notice Return state of Bin at input binId.
*/
function getBin(uint32 binId) external view returns (BinState memory bin);
/**
* @notice Return state of Tick at input tick position.
*/
function getTick(int32 tick) external view returns (TickState memory tickState);
/**
* @notice Retrieves the balance of a user within a bin.
* @param user The user's address.
* @param subaccount The subaccount for the user.
* @param binId The ID of the bin.
*/
function balanceOf(address user, uint256 subaccount, uint32 binId) external view returns (uint128 lpToken);
/**
* @notice Add liquidity to a pool. This function allows users to deposit
* tokens into a liquidity pool.
* @dev This function will call `maverickV2AddLiquidityCallback` on the
* calling contract to collect the tokenA/tokenB payment.
* @param recipient The account that will receive credit for the added liquidity.
* @param subaccount The account that will receive credit for the added liquidity.
* @param params Parameters containing the details for adding liquidity,
* such as token types and amounts.
* @param data Bytes information that gets passed to the callback.
* @return tokenAAmount The amount of token A added to the pool.
* @return tokenBAmount The amount of token B added to the pool.
* @return binIds An array of bin IDs where the liquidity is stored.
*/
function addLiquidity(
address recipient,
uint256 subaccount,
AddLiquidityParams calldata params,
bytes calldata data
) external returns (uint256 tokenAAmount, uint256 tokenBAmount, uint32[] memory binIds);
/**
* @notice Removes liquidity from the pool.
* @dev Liquidy can only be removed from a bin that is either unmerged or
* has a mergeId of an unmerged bin. If a bin is merged more than one
* level deep, it must be migrated up the merge stack to the root bin
* before liquidity removal.
* @param recipient The address to receive the tokens.
* @param subaccount The subaccount for the recipient.
* @param params The parameters for removing liquidity.
* @return tokenAOut The amount of token A received.
* @return tokenBOut The amount of token B received.
*/
function removeLiquidity(
address recipient,
uint256 subaccount,
RemoveLiquidityParams calldata params
) external returns (uint256 tokenAOut, uint256 tokenBOut);
/**
* @notice Migrate bins up the linked list of merged bins so that its
* mergeId is the currrent active bin.
* @dev Liquidy can only be removed from a bin that is either unmerged or
* has a mergeId of an unmerged bin. If a bin is merged more than one
* level deep, it must be migrated up the merge stack to the root bin
* before liquidity removal.
* @param binId The ID of the bin to migrate.
* @param maxRecursion The maximum recursion depth for the migration.
*/
function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;
/**
* @notice Swap tokenA/tokenB assets in the pool. The swap user has two
* options for funding their swap.
* - The user can push the input token amount to the pool before calling
* the swap function. In order to avoid having the pool call the callback,
* the user should pass a zero-length `data` bytes object with the swap
* call.
* - The user can send the input token amount to the pool when the pool
* calls the `maverickV2SwapCallback` function on the calling contract.
* That callback has input parameters that specify the token address of the
* input token, the input and output amounts, and the bytes data sent to
* the swap function.
* @dev If the users elects to do a callback-based swap, the output
* assets will be sent before the callback is called, allowing the user to
* execute flash swaps. However, the pool does have reentrancy protection,
* so a swapper will not be able to interact with the same pool again
* while they are in the callback function.
* @param recipient The address to receive the output tokens.
* @param params Parameters containing the details of the swap
* @param data Bytes information that gets passed to the callback.
*/
function swap(
address recipient,
SwapParams memory params,
bytes calldata data
) external returns (uint256 amountIn, uint256 amountOut);
/**
* @notice Loan tokenA/tokenB assets from the pool to recipient. The fee
* rate of a loan is determined by `lendingFeeRateD18`, which is set at the
* protocol level by the factory. This function calls
* `maverickV2FlashLoanCallback` on the calling contract. At the end of
* the callback, the caller must pay back the loan with fee (if there is a
* fee).
* @param recipient The address to receive the loaned tokens.
* @param amountB Loan amount of tokenA sent to recipient.
* @param amountB Loan amount of tokenB sent to recipient.
* @param data Bytes information that gets passed to the callback.
*/
function flashLoan(
address recipient,
uint256 amountA,
uint256 amountB,
bytes calldata data
) external returns (uint128 lendingFeeA, uint128 lendingFeeB);
/**
* @notice Sets fee for permissioned pools. May only be called by the
* accessor.
*/
function setFee(uint256 newFeeAIn, uint256 newFeeBIn) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IMaverickV2Pool} from "../../v2-common/interfaces/IMaverickV2Pool.sol";
interface IMaverickV2Quoter {
error QuoterInvalidSwap();
error QuoterInvalidAddLiquidity();
/**
* @notice Calculates a swap on a MaverickV2Pool and returns the resulting
* amount and estimated gas. The gas estimate is only a rough estimate and
* may not match a swap's gas.
* @param pool The MaverickV2Pool to swap on.
* @param amount The input amount.
* @param tokenAIn Indicates if token A is the input token.
* @param exactOutput Indicates if the amount is the output amount (true)
* or input amount (false). If the tickLimit is reached, the full value of
* the exactOutput may not be returned because the pool will stop swapping
* before the whole order is filled.
* @param tickLimit The tick limit for the swap. Once the swap lands in
* this tick, it will stop and return the output amount swapped up to that
* tick.
*/
function calculateSwap(
IMaverickV2Pool pool,
uint128 amount,
bool tokenAIn,
bool exactOutput,
int32 tickLimit
) external returns (uint256 amountIn, uint256 amountOut, uint256 gasEstimate);
/**
* @notice Calculates a multihop swap and returns the resulting amount and
* estimated gas. The gas estimate is only a rough estimate and
* may not match a swap's gas.
* @param path The path of pools to swap through. Path is given by an
* packed array of (pool, tokenAIn) tuples. So each step in the path is 160
* + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);
* @param amount The input amount.
* @param exactOutput A boolean indicating if exact output is required.
*/
function calculateMultiHopSwap(
bytes memory path,
uint256 amount,
bool exactOutput
) external returns (uint256 returnAmount, uint256 gasEstimate);
/**
* @notice Computes the token amounts required for a given set of
* addLiquidity parameters. The gas estimate is only a rough estimate and
* may not match a add's gas.
*/
function calculateAddLiquidity(
IMaverickV2Pool pool,
IMaverickV2Pool.AddLiquidityParams calldata params
) external returns (uint256 amountA, uint256 amountB, uint256 gasEstimate);
/**
* @notice Pool's sqrt price.
*/
function poolSqrtPrice(IMaverickV2Pool pool) external view returns (uint256 sqrtPrice);
}
// SPDX-License-Identifier: GPL-2.0-or-later
// As the copyright holder of this work, Ubiquity Labs retains
// the right to distribute, use, and modify this code under any license of
// their choosing, in addition to the terms of the GPL-v2 or later.
pragma solidity ^0.8.25;
interface IMulticall {
function multicall(bytes[] calldata data) external returns (bytes[] memory results);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IHistoricalBalanceNonTransferableERC20} from "./IHistoricalBalanceNonTransferableERC20.sol";
import {ISwapper} from "./ISwapper.sol";
interface IRaffleVault {
error NoLootBoxesToOpen();
error RaffleBuyTooLittleReceived(uint256 amountOutMinimum, uint256 amountOut);
error EpochHasNotEnded(uint256 currentTimestamp, uint256 endEpochTimestamp);
error InvalidEndEpochTimestamp(uint256 inputTimstamp);
error OutputTokenDoesNotMatch(IERC20 outputToken, IERC20 expectedOutputToken);
error InsufficientEthForFee(uint256 amountAvailable, uint256 amountRequired);
struct LootBoxData {
address user;
uint8 count;
uint64 sequenceNumber;
uint256 endEpochTimestamp;
}
struct LootBoxUserData {
uint32 countOfLootBoxesOpened;
uint32 countOfEpochsWithLootBoxOpened;
uint32 minRandomNumber;
uint160 totalQuoteTokenPrizes;
}
event OpenLootBoxes(IERC20 indexed quoteToken, LootBoxData lootBoxData);
event SpendPrizeEscrow(address user, uint256 amountIn, uint256 amountOut, IERC20 inputToken, IERC20 outputToken);
event RevealLoot(
IERC20 indexed quoteToken,
uint256 nextMultiplier,
uint256 prizeAmount,
uint256 randomNumber,
uint256 boxNumber,
LootBoxData lootBoxData
);
/**
* @notice Returns buy fee tracker token
*/
function buyTracker() external view returns (IHistoricalBalanceNonTransferableERC20);
/**
* @notice Returns Swapper contract that created this vault
*/
function swapper() external view returns (ISwapper);
/**
* @notice Returns quote token that this vault holds
*/
function quoteToken() external view returns (IERC20);
/**
* @notice Returns total amount of quote token escrowed by users who have
* claimed their loot boxes. This escrow is spent when the users call
* `spendPrizeEscrow`
*/
function totalEscrow() external view returns (uint256);
/**
* @notice Returns epoch-end timestamp of the epoch that that `timestamp` is in
* offset by `epochOffset`. For instance, to find yesterepoch's
* `epochEndTimestamp`, set timestamp to any timestamp toepoch ,like
* `block.timestamp`, and set `epochOffset` to `-1`.
*/
function endEpochTimestampAtOffset(
uint256 timestamp,
int256 epochOffset
) external view returns (uint256 endEpochTimestamp);
/**
* @notice Returns epoch-end timestamp of toepoch. This `endEpochTimestamp` will
* not yet be claimable.
*/
function currentEndEpochTimestamp() external view returns (uint256 endEpochTimestamp);
/**
* @notice Returns lootbox user data
*/
function lootBoxUserData(
address user
)
external
returns (
uint32 countOfLootBoxesOpened,
uint32 countOfEpochsWithLootBoxOpened,
uint32 minRandomNumber,
uint160 totalQuoteTokenPrizes
);
/**
* @notice Returns multiplier that will applied to the next loot box open
* for this user (0 is equivilent to a "multiplier" of 1)
*/
function multiplierByUser(address user) external view returns (uint256);
/**
* @notice Returns an indicator of whether this user has already collected
* for a given epoch
*/
function hasCollectedByUserEpoch(address user, uint256 datTs) external view returns (bool);
/**
* @notice Returns escrowed prize for a user; users can spend this escrow
* to collect their prize with `spendPrizeEscrow`
*/
function escrowByUser(address user) external view returns (uint256);
/**
* @notice Returns number of loot boxes the user can claim for that epoch
*/
function newLootBoxCount(
address user,
uint256 endEpochTimestamp
) external view returns (uint256 numberOfNewLootBoxes);
/**
* @notice Returns fractional number of loot boxes for a given epoch.
*/
function lootBoxRawData(
address user,
uint256 endEpochTimestamp
) external view returns (uint256 numberOfNewLootBoxes, uint256 epochBalanceChange, uint256 balancePerLootBox);
/**
* @notice Claims and "Opens" loot boxes for user to realize the random prize result;
* the result with either by a probability multiplier for the next loot box
* the user opens or it will be a portion of the vault's quote token assets
* which will be escrowed until the user calls `spendPrizeEscrow`.
*
* @notice Caller needs to get the eth call fee from `getOpenLootBoxFee`
* and send that much value.
*/
function openLootBoxes(uint256 endEpochTimestamp) external payable returns (uint256 numberOfNewLootBoxes);
/**
* @notice Amount of gas token value that must be sent when calling
* `openLootBoxes`.
*/
function getOpenLootBoxFee() external view returns (uint256 fee);
/**
* @notice Buys the Swapper-specified token with the user's prize escrow.
* Caller passes in the expected token they will receive which is
* `swapper.getTopToken(quoteToken)`, but the top token may change as the
* call is being submitted. In the case that the expected token does not
* match the output token, this call will revert.
*/
function spendPrizeEscrow(
IERC20 expectedOutputToken,
uint256 amountOutMinimum
) external returns (IERC20 outputToken, uint256 amountOut);
/**
* @notice Estimates the amount of token a user will get if they spend
* their escrow
*/
function estimatePrize(address user) external returns (IERC20 outputToken, uint256 amountOut);
/**
* @notice Interval in seconds between raffles (1 epoch)
*/
// solhint-disable-next-line func-name-mixedcase
function RAFFLE_INTERVAL() external view returns (uint256);
/**
* @notice Move tokens from fee vault to raffle contract.
*/
function pullTokensFromFeeVault() external returns (uint256 amount);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {ISwapper} from "./ISwapper.sol";
interface IReferralRegistry {
event Refer(address referee, address referer, uint256 timestamp);
error SenderAlreadyRefered();
error OnlySwapperOwner();
/**
* @notice Registers caller to the given referer
*/
function refer(address referer) external;
/**
* @notice Registers specific refer pair. Only callable by swapper owner.
*/
function setReferer(address user, address referer) external;
/**
* @notice User lookup
*/
function userToRefererData(address user) external view returns (address referer, uint96 timestamp);
/**
* @notice Paginated lookup of referees
*/
function referees(uint256 startIndex, uint256 endIndex, address referer) external view returns (address[] memory);
/**
* @notice Swapper
*/
function swapper() external view returns (ISwapper);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IEntropy} from "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
import {IMaverickV2Pool} from "../v2-common/interfaces/IMaverickV2Pool.sol";
import {IMaverickV2Factory} from "../v2-common/interfaces/IMaverickV2Factory.sol";
import {IWETH9} from "./external/IWETH9.sol";
import {IMaverickV2Quoter} from "./external/IMaverickV2Quoter.sol";
import {IHistoricalBalanceNonTransferableERC20} from "./IHistoricalBalanceNonTransferableERC20.sol";
import {IRaffleVault} from "./IRaffleVault.sol";
import {ILaunchFactory} from "./ILaunchFactory.sol";
import {IFeeVault} from "./IFeeVault.sol";
import {IVotingDistributor} from "./IVotingDistributor.sol";
import {IReferralRegistry} from "./IReferralRegistry.sol";
interface ISwapper {
error SenderNotWETH();
error InvalidFeeParameter();
error TooLittleReceived(uint256 amountOutMinimumExcludingFee, uint256 amountOut);
error TokenNotFromLaunchFactory();
error IncorrectEthValueSent(uint256 amountQuoteIn, uint256 valueSent);
error QuoteTokenIsNotEth(IERC20 quoteToken);
error NoTracker();
error QuoteTokenAlreadyRegistered(IERC20 quoteToken);
error ValueSentForNonEthQuoteToken(IERC20 quoteToken, uint256 valueSent);
error NotFactoryPool(IMaverickV2Pool ethToQuotePool);
struct Trackers {
IHistoricalBalanceNonTransferableERC20 sell;
IHistoricalBalanceNonTransferableERC20 buy;
IHistoricalBalanceNonTransferableERC20 token;
IHistoricalBalanceNonTransferableERC20 creator;
IHistoricalBalanceNonTransferableERC20 referer;
}
struct FeeRates {
uint64 proportionToRaffleVaultD18;
uint64 proportionToRefererD18;
uint64 proportionToVotingDistributorD18;
uint64 proportionToCreatorD18;
uint64 proportionToVoterD18;
}
struct Amounts {
uint256 amountToProtocol;
uint256 amountToCreator;
uint256 amountToReferer;
uint256 amountToRaffleVault;
uint256 amountToSwapper;
uint256 amountToVoteDistribution;
uint256 amountToVoters;
}
struct Recipients {
address protocol;
address creator;
address referer;
address raffleVault;
address votingDistributor;
address swapper;
}
event SetFeeVault(IFeeVault feeVault);
event NewTrackers(
IERC20 quoteToken,
ILaunchFactory factory,
IHistoricalBalanceNonTransferableERC20 sellTracker,
IHistoricalBalanceNonTransferableERC20 buyTracker,
IHistoricalBalanceNonTransferableERC20 tokenTracker,
IHistoricalBalanceNonTransferableERC20 creatorTracker,
IHistoricalBalanceNonTransferableERC20 refererTracker
);
event SetFeeRates(FeeRates feeRates);
event SetFeeRate(uint64 feeRateD18);
event SetProtocolRecipient(address protocolRecipient);
event SetReferralRegistry(IReferralRegistry registry);
event SetEntropy(IEntropy entropy);
event FeeEmission(IERC20 indexed token, Recipients recipients, Amounts amounts, uint256 rawAmount, bool isBuy);
event NewRaffleVault(IERC20 quoteToken, IRaffleVault raffleVault);
event SetBalancePerLootBox(IERC20 quoteToken, uint256 balanceRequirement);
event SetPrizesAndThresholds(uint24[8] thresholds, uint64[8] prizes, uint8 boxBonus);
event SetVotingDistributor(IERC20 quoteToken, IVotingDistributor votingDistributor);
event BuyToken(
address recipient,
IERC20 token,
uint256 amountQuoteIn,
uint256 amountOutMinimum,
address referer,
address raffleRecipient,
uint256 amountOut,
IMaverickV2Pool pool,
uint256 poolSqrtPrice
);
event SellToken(
address recipient,
IERC20 token,
uint256 amountTokenIn,
uint256 amountOutMinimum,
address referer,
address raffleRecipient,
uint256 amountOut,
IMaverickV2Pool pool,
uint256 poolSqrtPrice
);
/**
* @notice Buys tokens using quote token.
* @param recipient The address to receive the purchased tokens
* @param token The ERC20 token to buy
* @param amountQuoteIn The amount of quote to spend
* @param amountOutMinimum The minimum amount of tokens to receive
* @param referer The address of the referrer (adddress(0) should be used if there is no referer)
* @param raffleRecipient The address that gets credit for the buy volume in the tracker token
* @return amountOut The actual amount of tokens received
* @return pool The Maverick V2 pool used for the swap
*/
function buyTokenSpecifyRaffleRecipient(
address recipient,
IERC20 token,
uint256 amountQuoteIn,
uint256 amountOutMinimum,
address referer,
address raffleRecipient
) external payable returns (uint256 amountOut, IMaverickV2Pool pool);
/**
* @notice Buys tokens using eth where the quote token is not ETH. The
* token is bought through a two-pool swap: ethToQuote -> quoteToToken.
* @param recipient The address to receive the purchased tokens
* @param token The ERC20 token to buy
* @param ethToQuotePool The Maverick pool that can swap eth for quote token
* @param amountEthIn The amount of ETH to spend
* @param amountOutMinimum The minimum amount of tokens to receive
* @param referer The address of the referrer (adddress(0) should be used if there is no referer)
* @param raffleRecipient The address that gets credit for the buy volume in the tracker token
* @return amountOut The actual amount of tokens received
* @return pool The Maverick V2 pool used for the swap
*/
function buyTokenTwoHopSpecifyRaffleRecipient(
address recipient,
IERC20 token,
IMaverickV2Pool ethToQuotePool,
uint256 amountEthIn,
uint256 amountOutMinimum,
address referer,
address raffleRecipient
) external payable returns (uint256 amountOut, IMaverickV2Pool pool);
/**
* @notice Sells tokens for quote token.
* @param recipient The address to receive the quote
* @param token The ERC20 token to sell
* @param amountTokenIn The amount of tokens to sell
* @param amountOutMinimum The minimum amount of ETH to receive
* @param referer The address of the referrer (adddress(0) should be used if there is no referer)
* @param raffleRecipient The address that gets credit for the buy volume in the tracker token
* @return amountOut The actual amount of quote token received
* @return pool The Maverick V2 pool used for the swap
*/
function sellTokenSpecifyRaffleRecipient(
address recipient,
IERC20 token,
uint256 amountTokenIn,
uint256 amountOutMinimum,
address referer,
address raffleRecipient
) external returns (uint256 amountOut, IMaverickV2Pool pool);
/**
* @notice Sells tokens for quote token and then swaps the quote token for eth.
* @param recipient The address to receive the quote
* @param token The ERC20 token to sell
* @param ethToQuotePool The Maverick pool that can swap quote token for eth
* @param amountTokenIn The amount of tokens to sell
* @param amountOutMinimum The minimum amount of ETH to receive
* @param referer The address of the referrer (adddress(0) should be used if there is no referer)
* @param raffleRecipient The address that gets credit for the buy volume in the tracker token
* @return amountOut The actual amount of ETH received
* @return pool The Maverick V2 pool used for the swap
*/
function sellTokenTwoHopSpecifyRaffleRecipient(
address recipient,
IERC20 token,
IMaverickV2Pool ethToQuotePool,
uint256 amountTokenIn,
uint256 amountOutMinimum,
address referer,
address raffleRecipient
) external returns (uint256 amountOut, IMaverickV2Pool pool);
/**
* @notice Buys tokens using quote token. Credits msg.sender with the buy volume on the tracker token.
* @param recipient The address to receive the purchased tokens
* @param token The ERC20 token to buy
* @param amountQuoteIn The amount of quote to spend
* @param amountOutMinimum The minimum amount of tokens to receive
* @param referer The address of the referrer (adddress(0) should be used if there is no referer)
* @return amountOut The actual amount of tokens received
* @return pool The Maverick V2 pool used for the swap
*/
function buyToken(
address recipient,
IERC20 token,
uint256 amountQuoteIn,
uint256 amountOutMinimum,
address referer
) external payable returns (uint256 amountOut, IMaverickV2Pool pool);
/**
* @notice Buys tokens using eth where the quote token is not eth. The
* token is bought through a two-pool swap: ethToQuote -> quoteToToken.
* Credits msg.sender with the buy volume on the tracker token.
* @param recipient The address to receive the purchased tokens
* @param token The ERC20 token to buy
* @param ethToQuotePool The Maverick pool that can swap eth for quote token
* @param amountEthIn The amount of ETH to spend
* @param amountOutMinimum The minimum amount of tokens to receive
* @param referer The address of the referrer (adddress(0) should be used if there is no referer)
* @return amountOut The actual amount of tokens received
* @return pool The Maverick V2 pool used for the swap
*/
function buyTokenTwoHop(
address recipient,
IERC20 token,
IMaverickV2Pool ethToQuotePool,
uint256 amountEthIn,
uint256 amountOutMinimum,
address referer
) external payable returns (uint256 amountOut, IMaverickV2Pool pool);
/**
* @notice Sells tokens for quote token. Credits msg.sender with the sell volume on the tracker token.
* @param recipient The address to receive the ETH
* @param token The ERC20 token to sell
* @param amountTokenIn The amount of tokens to sell
* @param amountOutMinimum The minimum amount of ETH to receive
* @param referer The address of the referrer (adddress(0) should be used if there is no referer)
* @return amountOut The actual amount of ETH received
* @return pool The Maverick V2 pool used for the swap
*/
function sellToken(
address recipient,
IERC20 token,
uint256 amountTokenIn,
uint256 amountOutMinimum,
address referer
) external returns (uint256 amountOut, IMaverickV2Pool pool);
/**
* @notice Sells tokens for quote token and then swaps the quote token for eth.
* @param recipient The address to receive the quote
* @param token The ERC20 token to sell
* @param ethToQuotePool The Maverick pool that can swap quote token for eth
* @param amountTokenIn The amount of tokens to sell
* @param amountOutMinimum The minimum amount of ETH to receive
* @param referer The address of the referrer (adddress(0) should be used if there is no referer)
* @return amountOut The actual amount of ETH received
* @return pool The Maverick V2 pool used for the swap
*/
function sellTokenTwoHop(
address recipient,
IERC20 token,
IMaverickV2Pool ethToQuotePool,
uint256 amountTokenIn,
uint256 amountOutMinimum,
address referer
) external returns (uint256 amountOut, IMaverickV2Pool pool);
/**
* @notice Gets an output estimate for buying tokens with quote token
* @param token The ERC20 token to buy
* @param amountQuoteIn The amount of quote to spend
* @return amountOut The estimated amount of tokens that would be received inclusive of all fees
*/
function buyTokenQuote(IERC20 token, uint256 amountQuoteIn) external returns (uint256 amountOut);
/**
* @notice Gets an output estimate for buying tokens with ETH via an intermediate eth-to-quote pool
* @param token The ERC20 token to buy
* @param ethToQuotePool The Maverick pool that can swap eth for quote token
* @param amountEthIn The amount of ETH to spend
* @return amountOut The estimated amount of tokens that would be received inclusive of all fees
*/
function buyTokenTwoHopQuote(
IERC20 token,
IMaverickV2Pool ethToQuotePool,
uint256 amountEthIn
) external returns (uint256 amountOut);
/**
* @notice Gets a quote for selling tokens for ETH
* @param token The ERC20 token to sell
* @param amountTokenIn The amount of tokens to sell
* @return amountOut The estimated amount of ETH that would be received inclusive of all fees
*/
function sellTokenQuote(IERC20 token, uint256 amountTokenIn) external returns (uint256 amountOut);
/**
* @notice Gets a quote for selling tokens for ETH
* @param token The ERC20 token to sell
* @param ethToQuotePool The Maverick pool that can swap quote for eth
* @param amountTokenIn The amount of tokens to sell
* @return amountOut The estimated amount of ETH that would be received inclusive of all fees
*/
function sellTokenTwoHopQuote(
IERC20 token,
IMaverickV2Pool ethToQuotePool,
uint256 amountTokenIn
) external returns (uint256 amountOut);
/**
* @notice Gets the entropy contract address
*/
function entropy() external view returns (IEntropy);
/**
* @notice Gets prize, threshold values and box bonus
*/
function getPrizesAndThresholdsAndBonus()
external
view
returns (uint24[8] memory thresholds, uint64[8] memory prizes, uint8 boxBonus);
/**
* @notice Gets top meme token for given quote token
*/
function getTopToken(IERC20 quoteToken) external view returns (IERC20 topToken);
/**
* @notice Gets the WETH contract address
*/
function weth() external view returns (IWETH9);
/**
* @notice Gets the feeRecipient address
*/
function protocolRecipient() external view returns (address);
/**
* @notice Gets the fee rate in D18 format
*/
function feeRateD18() external view returns (uint64);
/**
* @notice Gets the fee rate in D18 format
*/
function feeRates()
external
view
returns (
uint64 proportionToRaffleVaultD18,
uint64 proportionToRefererD18,
uint64 proportionToVotingDistributorD18,
uint64 proportionToCreatorD18,
uint64 proportionToVoterD18
);
/**
* @notice Gets the Maverick V2 price quoter
*/
function quoter() external view returns (IMaverickV2Quoter);
function feeVault() external view returns (IFeeVault);
function referralRegistry() external view returns (IReferralRegistry);
function poolFactory() external view returns (IMaverickV2Factory);
function quoteToRaffleVault(IERC20) external view returns (IRaffleVault);
function quoteToVotingDistributor(IERC20) external view returns (IVotingDistributor);
function quoteToFactory(IERC20) external view returns (ILaunchFactory);
function quoteToTrackers(
IERC20
)
external
view
returns (
IHistoricalBalanceNonTransferableERC20 sell,
IHistoricalBalanceNonTransferableERC20 buy,
IHistoricalBalanceNonTransferableERC20 token,
IHistoricalBalanceNonTransferableERC20 creator,
IHistoricalBalanceNonTransferableERC20 referer
);
/**
* @notice Gets fee balance requirement needed to claim a lootbox
*/
function balancePerLootBox(IERC20 quoteToken) external view returns (uint256);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IMaverickV2Pool} from "../v2-common/interfaces/IMaverickV2Pool.sol";
import {ILaunchFactory} from "./ILaunchFactory.sol";
interface ITokenManager {
error NotFactoryPool();
error OnlyCurrentRecipientAllowed(address existingRecipient, address newRecipient);
error BorrowingNotEnabled();
error NoLiquidityToBorrow();
error NothingToRedeem();
error TooMuchQuoteInRedeem(uint256 amountRequested, uint256 maxAmount);
error NotInClosePeriod(uint256 lastPoolSwap, uint256 closePeriodThreshold);
error AddressZeroNotValidFeeRecipient();
error MinTimeNotElaspedSinceLastFeeChange(uint256 timeDifference, uint256 minTimeDifference);
error FeeChangeNotEnabled();
error SenderNotWETH();
error MinRedeemNotMet(uint256 totalTokenRedeemed, uint256 minRedeemTokenAmount);
error MinBorrowNotMet(uint256 amountToBorrow, uint256 minAmountToBorrower);
error InsufficientEthSentByUser(uint256 amountToPay, uint256 amountRecived);
event ExtractFee(
int32[] ticks,
IMaverickV2Pool.RemoveLiquidityParams params,
uint256 amountToProtocol,
uint256 amountToRecipient,
address recipient
);
event ChangeFeeRecipient(address existingRecipient, address newRecipient);
event ClosePool(IMaverickV2Pool.RemoveLiquidityParams params, uint256 quoteAmount, uint256 tokenAmount);
event BorrowQuote(
address borrower,
uint128 inputTokenBorrowAmount,
uint128 inputMinSent,
int32[] ticks,
uint128[] tokenAmounts,
uint128[] quoteToRepayAmounts,
uint128 tokenCollateralAmount,
uint128 quoteToRepayAmount,
uint128 quoteToBorrowerAmount,
uint128 ethToBorrowerAmount
);
event RedeemTokenCollateral(
address borrower,
uint128 inputRedeemTokenAmount,
int32[] ticks,
uint128[] tokenAmounts,
uint128[] quoteToRepayAmounts,
uint128 totalQuoteSpent,
uint128 totalEthSpent,
uint128 totalTokenRedeemed
);
event ChangeFees(uint256 newBuyFee, uint256 newSellFee, uint256 timestamp);
event BorrowFeeEmission(IERC20 quoteToken, IERC20 token, Recipients recipients, Amounts amounts);
struct DebtData {
uint128 quoteAmount;
uint128 tokenAmount;
}
struct Recipients {
address protocol;
address creator;
address votingDistributor;
}
struct Amounts {
uint256 amountToCreator;
uint256 amountToProtocol;
uint256 amountToVoters;
uint256 amountToVoteDistribution;
}
/**
* @notice Gets the Maverick V2 fairlaunch pool
*/
function pool() external view returns (IMaverickV2Pool);
/**
* @notice Gets the index of the curve which corresponds to the liquidity
* distribution exponent. Valid values are 0 to 5.
*/
function curveIndex() external view returns (uint8);
/**
* @notice Gets the launch factory
*/
function launchFactory() external view returns (ILaunchFactory);
/**
* @notice Gets the ERC20 token that was launched
*/
function token() external view returns (IERC20);
/**
* @notice Checks if the token is tokenA in the pool
*/
function tokenIsA() external view returns (bool);
/**
* @notice Gets the address that receives fees
*/
function feeRecipient() external view returns (address);
/**
* @notice Checks if borrowing is enabled
*/
function borrowingEnabled() external view returns (bool);
/**
* @notice Returns pool swap fees.
*/
function fees() external view returns (uint64 _buyFee, uint64 _sellFee);
/**
* @notice Returns pool swap sell fee.
*/
function sellFee() external view returns (uint64);
/**
* @notice Returns pool swap sell fee.
*/
function buyFee() external view returns (uint64);
/**
* @notice Returns timestamp of last fee change.
*/
function lastFeeChangeTimestamp() external view returns (uint64);
/**
* @notice Borrows QUOTE from the pool. This function may create a loan for
* less token collateral than the user specifies if there is not enough quote
* in the pool to be borrowed. Users can specify the minimum amount of
* QUOTE they will accept in the loan.
* @param tokenBorrowAmount The desired amount of tokens to send as collateral
* @param minQuoteSent The minimum amount of QUOTE that can be sent to the user
* @return ticks The ticks involved in the borrowing operation
* @return tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return tokenCollateralAmount The amount of tokens used as collateral
* @return quoteToRepayAmount The total amount of QUOTE to repay
* @return quoteToBorrowerAmount The amount of QUOTE sent to the borrower
*/
function borrowQuote(
uint128 tokenBorrowAmount,
uint128 minQuoteSent
)
external
returns (
int32[] memory ticks,
uint128[] memory tokenAmounts,
uint128[] memory quoteToRepayAmounts,
uint128 tokenCollateralAmount,
uint128 quoteToRepayAmount,
uint128 quoteToBorrowerAmount
);
/**
* @notice Borrows QUOTE from the pool and swap to ETH. This function may
* create a loan for less token collateral than the user specifies if there
* is not enough quote in the pool to be borrowed. Users can specify the
* minimum amount of ETH they will accept in the loan.
* @param ethToQuotePool The maverick pool that has eth and quote.
* @param tokenBorrowAmount The desired amount of tokens to send as collateral
* @param minEthSent The minimum amount of ETH that can be sent to the user
* @return ticks The ticks involved in the borrowing operation
* @return tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return tokenCollateralAmount The amount of tokens used as collateral
* @return quoteToRepayAmount The total amount of QUOTE to repay
* @return quoteToBorrowerAmount The amount of QUOTE sent to the borrower
* @return ethToBorrowerAmount The amount of ETH sent to the borrower
*/
function borrowQuoteToEth(
IMaverickV2Pool ethToQuotePool,
uint128 tokenBorrowAmount,
uint128 minEthSent
)
external
returns (
int32[] memory ticks,
uint128[] memory tokenAmounts,
uint128[] memory quoteToRepayAmounts,
uint128 tokenCollateralAmount,
uint128 quoteToRepayAmount,
uint128 quoteToBorrowerAmount,
uint128 ethToBorrowerAmount
);
/**
* @notice Redeems token collateral; The caller needs to approve this
* TokenManager to `transferFrom` amount.
* @param maxRedeemTokenAmount The max amount of tokens to redeem
* @param minRedeemTokenAmount The min amount of tokens to redeem
* @param maxQuoteAmount The max amount of quote tokens to spend in redemption
* @return ticks The ticks involved in the redemption operation
* @return tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return totalQuoteSpent The total amount of QUOTE spent
* @return totalTokenRedeemed The total amount of tokens redeemed
*/
function redeemTokenCollateral(
uint128 maxRedeemTokenAmount,
uint128 minRedeemTokenAmount,
uint128 maxQuoteAmount
)
external
returns (
int32[] memory ticks,
uint128[] memory tokenAmounts,
uint128[] memory quoteToRepayAmounts,
uint128 totalQuoteSpent,
uint128 totalTokenRedeemed
);
/**
* @notice Redeems token collateral at specified ticks which have to be
* passed in sorted order. The caller needs to approve this TokenManager to
* `transferFrom` amount.
* @param ticks The ticks involved in the redemption operation
* @param tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return totalQuoteSpent The total amount of QUOTE spent
* @return totalTokenRedeemed The total amount of tokens redeemed
*/
function redeemTokenCollateralByTick(
int32[] memory ticks,
uint128[] memory tokenAmounts
) external returns (uint128[] memory quoteToRepayAmounts, uint128 totalQuoteSpent, uint128 totalTokenRedeemed);
/**
* @notice Redeems token collateral at specified ticks which have to be
* passed in sorted order. caller needs to send ETH with the call equal to
* the repay amount. Any excess ETH will be sent back to the caller. The
* ETH will be swapped in the input pool for quote token.
* @param ethToQuotePool The maverick pool that has eth and quote.
* @param ticks The ticks involved in the redemption operation
* @param tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return totalQuoteSpent The total amount of QUOTE spent
* @return totalEthSpent The total amount of QUOTE spent
* @return totalTokenRedeemed The total amount of tokens redeemed
*/
function redeemTokenCollateralWithEthByTick(
IMaverickV2Pool ethToQuotePool,
int32[] memory ticks,
uint128[] memory tokenAmounts
)
external
payable
returns (
uint128[] memory quoteToRepayAmounts,
uint128 totalQuoteSpent,
uint128 totalEthSpent,
uint128 totalTokenRedeemed
);
/**
* @notice Redeems token collateral; The caller needs to send ETH with the
* call equal to the repay amount. Any excess ETH will be sent back to the
* caller. The ETH will be swapped in the input pool for quote token.
* @param ethToQuotePool The maverick pool that has eth and quote.
* @param maxRedeemTokenAmount The max amount of tokens to redeem
* @param minRedeemTokenAmount The min amount of tokens to redeem
* @return ticks The ticks involved in the redemption operation
* @return tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return totalQuoteSpent The total amount of QUOTE spent
* @return totalEthSpent The total amount of QUOTE spent
* @return totalTokenRedeemed The total amount of tokens redeemed
*/
function redeemTokenCollateralWithEth(
IMaverickV2Pool ethToQuotePool,
uint128 maxRedeemTokenAmount,
uint128 minRedeemTokenAmount
)
external
payable
returns (
int32[] memory ticks,
uint128[] memory tokenAmounts,
uint128[] memory quoteToRepayAmounts,
uint128 totalQuoteSpent,
uint128 totalEthSpent,
uint128 totalTokenRedeemed
);
/**
* @notice Gets the borrowed amounts for a user at a specific tick
* @param user The user address
* @param tick The tick
* @return amounts The debt data (token collateral amount and QUOTE amount)
*/
function userBorrowedAmounts(address user, int32 tick) external view returns (DebtData memory amounts);
/**
* @notice Gets the total borrowed amounts across all users at a specific tick
* @param tick The tick
* @return amounts The debt data (token collateral amount and QUOTE amount)
*/
function borrowedAmounts(int32 tick) external view returns (DebtData memory amounts);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IMaverickV2Pool} from "../v2-common/interfaces/IMaverickV2Pool.sol";
import {ITokenManager} from "./ITokenManager.sol";
import {IDistribution} from "./IDistribution.sol";
interface ITokenManagerLens {
error BorrowingNotSupported(int32 startTick, int32 endTick);
error RedeemNotSupported(int32 startTick, int32 endTick);
error NonQuoteBin(IMaverickV2Pool pool, uint32 binId);
error TickIsNotAllQuote(int32 tick, int32 activeTick);
error TryingToRedeemMoreThanDebt(uint256 index, int32 tick, uint256 debtDataTokenAmount, uint256 inputTokenAmount);
/**
* @notice Gets information about redeeming tokens for a specific user
* @param manager The token manager contract
* @param user The user address
* @param tokenRedeemAmount The amount of tokens to redeem
* @return tokenAmounts The amount of tokens to redeems at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return totalQuoteSpent The total amount of QUOTE spent
* @return totalTokenRedeemed The total amount of tokens redeemed
* @return params The parameters for adding liquidity to the pool
*/
function redeemInformation(
ITokenManager manager,
address user,
uint128 tokenRedeemAmount
)
external
view
returns (
uint128[] memory tokenAmounts,
uint128[] memory quoteToRepayAmounts,
uint128 totalQuoteSpent,
uint128 totalTokenRedeemed,
IMaverickV2Pool.AddLiquidityParams memory params
);
/**
* @notice Gets information about redeeming tokens for a specific user
* where the user inputs the ticks and token amounts they want to redeem.
* @param manager The token manager contract
* @param user The user address
* @param ticks The ticks to redeem from
* @param tokenAmounts The amount of token to redeem from each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return totalQuoteSpent The total amount of QUOTE spent
* @return totalTokenRedeemed The total amount of tokens redeemed
* @return params The parameters for adding liquidity to the pool
*/
function redeemInformationByTick(
ITokenManager manager,
address user,
int32[] memory ticks,
uint128[] memory tokenAmounts
)
external
view
returns (
uint128[] memory quoteToRepayAmounts,
uint128 totalQuoteSpent,
uint128 totalTokenRedeemed,
IMaverickV2Pool.AddLiquidityParams memory params
);
/**
* @notice Estimates output from borrowing QUOTE from the pool and swap to ETH
* @param manager Token manager to estimate
* @param ethToQuotePool The maverick pool that has eth and quote.
* @param tokenBorrowAmount The desired amount of tokens to send as collateral
* @return ticks The ticks involved in the borrowing operation
* @return tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return tokenCollateralAmount The amount of tokens used as collateral
* @return quoteToRepayAmount The total amount of QUOTE to repay
* @return quoteToBorrowerAmount The amount of QUOTE sent to the borrower
* @return ethToBorrowerAmount The amount of ETH sent to the borrower
*/
function estimateBorrowQuoteToEth(
ITokenManager manager,
IMaverickV2Pool ethToQuotePool,
uint128 tokenBorrowAmount
)
external
returns (
int32[] memory ticks,
uint128[] memory tokenAmounts,
uint128[] memory quoteToRepayAmounts,
uint128 tokenCollateralAmount,
uint128 quoteToRepayAmount,
uint128 quoteToBorrowerAmount,
uint256 ethToBorrowerAmount
);
/**
* @notice Estimates the output of the redeem token collateral
* @param manager Token manager to estimate
* @param ethToQuotePool The maverick pool that has eth and quote.
* @param maxRedeemTokenAmount The max amount of tokens to redeem
* @return ticks The ticks involved in the redemption operation
* @return tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return totalQuoteSpent The total amount of QUOTE spent
* @return totalEthSpent The total amount of QUOTE spent
* @return totalTokenRedeemed The total amount of tokens redeemed
*/
function estimateRedeemTokenCollateralWithEth(
ITokenManager manager,
IMaverickV2Pool ethToQuotePool,
uint128 maxRedeemTokenAmount
)
external
returns (
int32[] memory ticks,
uint128[] memory tokenAmounts,
uint128[] memory quoteToRepayAmounts,
uint128 totalQuoteSpent,
uint256 totalEthSpent,
uint128 totalTokenRedeemed
);
/**
* @notice Estimates the output of the redeem token collateral
* @param manager Token manager to estimate
* @param ethToQuotePool The maverick pool that has eth and quote.
* @param ticks The ticks involved in the redemption operation
* @param tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return totalQuoteSpent The total amount of QUOTE spent
* @return totalEthSpent The total amount of QUOTE spent
* @return totalTokenRedeemed The total amount of tokens redeemed
*/
function estimateRedeemTokenCollateralWithEthByTick(
ITokenManager manager,
IMaverickV2Pool ethToQuotePool,
int32[] memory ticks,
uint128[] memory tokenAmounts
)
external
returns (
uint128[] memory quoteToRepayAmounts,
uint128 totalQuoteSpent,
uint256 totalEthSpent,
uint128 totalTokenRedeemed
);
/**
* @notice Gets information about borrowing tokens
* @param manager The token manager contract
* @param tokenBorrowAmount The amount of tokens to borrow
* @return ticks The ticks borrowed from
* @return tokenAmounts The token amounts at each tick
* @return quoteToRepayAmounts The QUOTE amounts to repay at each tick
* @return totalTokenCollateralAmount The total amount of tokens used as collateral
* @return params The parameters for removing liquidity from the pool
*/
function borrowInformation(
ITokenManager manager,
uint128 tokenBorrowAmount
)
external
view
returns (
int32[] memory ticks,
uint128[] memory tokenAmounts,
uint128[] memory quoteToRepayAmounts,
uint128 totalTokenCollateralAmount,
IMaverickV2Pool.RemoveLiquidityParams memory params
);
/**
* @notice Gets the tick and total number of ticks for a token manager's pool
* @param manager The token manager contract
* @return tick The current tick of the pool
*/
function ticksIntoPool(ITokenManager manager) external view returns (uint256 tick);
/**
* @notice Gets the borrowed amounts for a specific user in a token manager
* @param manager The token manager contract
* @param user The user address
* @return ticks The ticks where the user has borrowed amounts
* @return quoteAmounts The QUOTE amounts borrowed at each tick
* @return tokenAmounts The token amounts borrowed at each tick
* @return totalQuoteAmount The total QUOTE amount borrowed by the user
* @return totalTokenAmount The total token amount borrowed by the user
*/
function userBorrowedAmounts(
ITokenManager manager,
address user
)
external
view
returns (
int32[] memory ticks,
uint128[] memory quoteAmounts,
uint128[] memory tokenAmounts,
uint128 totalQuoteAmount,
uint128 totalTokenAmount
);
/**
* @notice Gets the total borrowed amounts in a token manager
* @param manager The token manager contract
* @return ticks The ticks where there are borrowed amounts
* @return quoteAmounts The total QUOTE amounts borrowed at each tick
* @return tokenAmounts The total token amounts borrowed at each tick
* @return totalQuoteAmount The total QUOTE amount borrowed
* @return totalTokenAmount The total token amount borrowed
*/
function totalBorrowedAmounts(
ITokenManager manager
)
external
view
returns (
int32[] memory ticks,
uint128[] memory quoteAmounts,
uint128[] memory tokenAmounts,
uint128 totalQuoteAmount,
uint128 totalTokenAmount
);
function lastTick() external view returns (int32);
function distribution() external view returns (IDistribution);
function borrowAllowed(bool tokenIsA, int32 tick, uint256 tickIntoPool) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.20;
/**
* @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
*/
interface IVotes {
/**
* @dev The signature used has expired.
*/
error VotesExpiredSignature(uint256 expiry);
/**
* @dev Emitted when an account changes their delegate.
*/
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
/**
* @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of voting units.
*/
event DelegateVotesChanged(address indexed delegate, uint256 previousVotes, uint256 newVotes);
/**
* @dev Returns the current amount of votes that `account` has.
*/
function getVotes(address account) external view returns (uint256);
/**
* @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*/
function getPastVotes(address account, uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
* vote.
*/
function getPastTotalSupply(uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the delegate that `account` has chosen.
*/
function delegates(address account) external view returns (address);
/**
* @dev Delegates votes from the sender to `delegatee`.
*/
function delegate(address delegatee) external;
/**
* @dev Delegates votes from signer to `delegatee`.
*/
function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC5805} from "@openzeppelin/contracts/interfaces/IERC5805.sol";
import {ILaunchFactory} from "./ILaunchFactory.sol";
import {ISwapper} from "./ISwapper.sol";
import {IMulticall} from "../v2-common/base/IMulticall.sol";
interface IVotingDistributor is IMulticall {
error InvalidEpoch(uint256 epoch);
error InvalidVeToken();
error InvalidVote(IERC20 token, uint256 weight, uint256 totalWeight, uint256 vote);
error NotFactoryToken(IERC20 token);
error SenderHasNoVotingPower(address sender, uint256 epoch);
error InvalidTargetOrder(IERC20 lastToken, IERC20 currentToken);
error SenderHasAlreadyVoted();
error TokenAlreadyDistributed(IERC20 token, uint256 epoch);
error VoterAlreadyCollected(address user, IERC20 token, uint256 epoch);
error NothingToDistribute();
error ZeroAmount();
error RolloverNotAllowed(uint256 voteForThisToken);
error PoolTwapGapTooBig(int256 tickDiffD8, int256 maxTickDiffD8);
error VotePeriodNotActive(uint256 timestamp, uint256 epochStart, uint256 epochEnd);
error VotePeriodNotEnded(uint256 timestamp, uint256 epochStart, uint256 epochEnd);
error EpochEnded(uint256 timestamp, uint256 epochStart, uint256 epochEnd);
error UseCollectVoteFee();
error MustCallFromEOA(address txOrigin, address msgSender);
event Vote(IERC20 indexed quoteToken, uint256 indexed epoch, address indexed voter, IERC20 token, uint256 vote);
event AddDistributionBudget(
IERC20 indexed quoteToken,
uint256 indexed epoch,
address indexed sender,
uint256 amount
);
event AddVoteIncentive(
IERC20 indexed quoteToken,
uint256 indexed epoch,
address indexed sender,
IERC20 token,
uint256 amount,
IERC20 incentiveToken
);
event RollUnvotedIncentive(
IERC20 indexed quoteToken,
uint256 indexed epoch,
IERC20 tokenToIncentivize,
IERC20 incentiveToken,
uint256 amount,
uint256 newEpoch
);
event Distribute(
IERC20 indexed quoteToken,
uint256 indexed epoch,
address indexed sender,
IERC20 token,
uint256 quoteTokenAmountDistributed,
uint256 tokenAmountBurned
);
event CollectVoteIncentive(
IERC20 indexed quoteToken,
uint256 indexed epoch,
address indexed voter,
IERC20 token,
uint256 amount,
IERC20 incentiveToken
);
event CollectVoteFee(
IERC20 indexed quoteToken,
uint256 indexed epoch,
address indexed voter,
IERC20 token,
uint256 quoteTokenAmount,
uint256 tokenAmount
);
/**
* @param startIndex The start index of the tokens to get.
* @param endIndex The end index of the tokens to get.
* @param incentiveStartIndex The start index of the incentive tokens to get.
* @param incentiveEndIndex The end index of the incentive tokens to get.
*/
struct IndexBounds {
uint256 startIndex;
uint256 endIndex;
uint256 incentiveStartIndex;
uint256 incentiveEndIndex;
}
struct ClaimData {
IERC20 token;
uint256 incentiveTokenCount;
IERC20[] incentiveTokens;
uint256[] incentiveAmounts;
uint256[] tokenBoughtAmounts;
bool[] hasCollected;
}
struct VoterData {
bool hasVoted;
uint128[] votes;
IERC20[] tokens;
}
struct TokenView {
IERC20 token;
uint256 votes;
bool hasDistributed;
IERC20[] incentiveTokens;
uint256[] voteIncentives;
uint256 incentiveTokenCount;
}
/**
* @notice Get the quote token used for distributions.
* @return The quote token.
*/
function quoteToken() external view returns (IERC20);
/**
* @notice Get the launch factory associated with this distributor.
* @return The launch factory.
*/
function factory() external view returns (ILaunchFactory);
/**
* @notice Get the voting escrow token used for voting power.
* @return The voting escrow token.
*/
function veToken() external view returns (IERC5805);
/**
* @notice Get the swapper contract used for token operations.
* @return The swapper contract.
*/
function swapper() external view returns (ISwapper);
/////////////////////////////////////
/// Voting
/////////////////////////////////////
/**
* @notice Vote for the given tokens with the given weights.
* @param voteTargets The tokens to vote for.
* @param weights The relative weight of each token.
*/
function vote(IERC20[] memory voteTargets, uint256[] memory weights) external;
/////////////////////////////////////
/// Epoch Budgets
/////////////////////////////////////
/**
* @notice Add a distribution budget for the given epoch. Function
* performs a transferFrom from msg.sender, so the send must first approve
* this contract for the amount.
* @param amount The amount of the budget.
* @param epoch The epoch to add the budget to.
*/
function addDistributionBudget(uint256 amount, uint256 epoch) external;
/**
* @notice Add a distribution budget for the current epoch. Function
* performs a transferFrom from msg.sender, so the send must first approve
* this contract for the amount.
* @param amount The amount of the budget.
*/
function addDistributionBudgetCurrentEpoch(uint256 amount) external;
/**
* @notice Add a vote incentive for the given token and epoch.
* @param tokenToIncentivize The token to incentivize.
* @param incentiveToken The incentive token.
* @param amount The amount of the incentive.
* @param epoch The epoch to add the incentive to.
*/
function addVoteIncentive(IERC20 tokenToIncentivize, IERC20 incentiveToken, uint256 amount, uint256 epoch) external;
/**
* @notice Add a vote incentive for the given token and current epoch.
* @param tokenToIncentivize The token to incentivize.
* @param incentiveToken The incentive token.
* @param amount The amount of the incentive.
*/
function addVoteIncentiveToCurrentEpoch(IERC20 tokenToIncentivize, IERC20 incentiveToken, uint256 amount) external;
/**
* @notice Rollover a vote incentive for the given token and epoch to the next epoch.
* @param tokenToIncentivize The token to incentivize.
* @param incentiveToken The incentive token.
* @param epoch The epoch the incentives were added to.
* @return amount The amount of the incentive.
* @return newEpoch The new epoch where the incentives will be distributed.
*/
function rollUnvotedIncentive(
IERC20 tokenToIncentivize,
IERC20 incentiveToken,
uint256 epoch
) external returns (uint256 amount, uint256 newEpoch);
/////////////////////////////////////
/// Post-Epoch Collections
/////////////////////////////////////
/**
* @notice Distribute the incentives for the given token and epoch.
* @param token The token to distribute incentives for.
* @param epoch The epoch to distribute incentives for.
* @return amountDistributed The amount of incentives distributed.
* @return amountBurned The amount of incentives burned.
*/
function distribute(IERC20 token, uint256 epoch) external returns (uint256 amountDistributed, uint256 amountBurned);
/**
* @notice Distribute the incentives for the given token and epoch.
* @param token The token to distribute incentives for.
* @param epoch The epoch to distribute incentives for.
* @return amountDistributed The amount of incentives distributed.
* @return amountBurned The amount of incentives burned.
* @return twaGapPasses Indicator of whether the pool twa is such that a
* swap will be executed.
*/
function distributeAmount(
IERC20 token,
uint256 epoch
) external returns (uint256 amountDistributed, uint256 amountBurned, bool twaGapPasses);
/**
* @notice Collect the vote incentive for the given token, incentive
* token, and epoch.
* @param token The token to collect the incentive for.
* @param incentiveToken The incentive token to collect.
* @param epoch The epoch to collect the incentive for.
* @return amount The amount of the incentive collected.
*/
function collectVoteIncentive(IERC20 token, IERC20 incentiveToken, uint256 epoch) external returns (uint256 amount);
/**
* @notice Collect the vote incentive for the given token, incentive
* token, and epoch when the incentive token is the quoteToken.
* @param token The token to collect the incentive for.
* @param epoch The epoch to collect the incentive for.
* @return amount The amount of the incentive.
* @return amountToken The amount of the token bought and send to sender.
*/
function collectVoteFee(
IERC20 token,
uint256 amountOutMinimum,
uint256 epoch
) external returns (uint256 amount, uint256 amountToken);
/**
* @notice View the vote incentive for the given token, incentive
* token, and epoch.
* @param user Address of user to check amounts for.
* @param token The token to collect the incentive for.
* @param incentiveToken The incentive token to collect.
* @param epoch The epoch to collect the incentive for.
* @return amount The amount of the incentive collected.
*/
function collectVoteIncentiveAmount(
address user,
IERC20 token,
IERC20 incentiveToken,
uint256 epoch
) external view returns (uint256 amount);
/**
* @notice View the vote incentive for the given token, incentive
* token, and epoch when the incentive token is the quoteToken.
* @param user Address of user to check amounts for.
* @param token The token to collect the incentive for.
* @param epoch The epoch to collect the incentive for.
* @return amount The amount of the incentive collected.
* @return amountToken The amount of the token bought and send to sender.
*/
function collectVoteFeeAmount(
address user,
IERC20 token,
uint256 epoch
) external returns (uint256 amount, uint256 amountToken);
/////////////////////////////////////
/// Epoch State Viewers
/////////////////////////////////////
/**
* @notice Get the checkpoint data for the given epoch.
* @param epoch The epoch to get the checkpoint data for.
* @param bounds Index bounds to search.
* @return budget The budget for the epoch.
* @return totalVote The total vote for the epoch.
* @return tokens The tokens for the epoch.
*/
function getCheckpointData(
uint256 epoch,
IndexBounds memory bounds
) external view returns (uint128 budget, uint128 totalVote, TokenView[] memory tokens, uint256 totalCount);
/**
* @notice Get the incentive data for the given epoch and token.
* @param epoch The epoch to get the incentive data for.
* @param token The token to get the incentive data for.
* @param startIndex The start index of the incentive tokens to get.
* @param endIndex The end index of the incentive tokens to get.
* @return incentiveTokens The incentive tokens for the epoch and token.
* @return voteIncentives The vote incentives for the epoch and token.
* @return totalCount The total count of incentive tokens for the epoch and token.
*/
function getIncentiveData(
IERC20 token,
uint256 epoch,
uint256 startIndex,
uint256 endIndex
) external view returns (IERC20[] memory incentiveTokens, uint256[] memory voteIncentives, uint256 totalCount);
/**
* @notice Get the claim data for the given user/epoch. Performs a pool
* quote so is not a view function, but does not alter state.
* @param user account to check
* @param epoch The epoch to get the checkpoint data for.
* @param bounds Index bounds to search.
*/
function getVoterClaimData(
address user,
uint256 epoch,
IndexBounds memory bounds
) external returns (ClaimData[] memory claimData, uint256 totalCount);
/**
* @notice Check if the given user has voted in the given epoch.
* @param user The user to check.
* @param epoch The epoch to check.
* @return hasVoted True if the user has voted, false otherwise.
*/
function hasVoted(address user, uint256 epoch) external view returns (bool);
/**
* @notice Get the voter data for the given user and epoch.
* @param user The user to get the voter data for.
* @param epoch The epoch to get the voter data for.
* @return voterData The voter data for the user and epoch.
*/
function getVoterData(address user, uint256 epoch) external view returns (VoterData memory voterData);
/////////////////////////////////////
/// Epoch Checkers and Helpers
/////////////////////////////////////
/**
* @notice Check if the given epoch is a valid epoch.
* @param epoch The epoch to check.
* @return _isEpoch True if the epoch is a valid epoch, false otherwise.
*/
function isEpoch(uint256 epoch) external pure returns (bool _isEpoch);
/**
* @notice Check if the given epoch is over.
* @param epoch The epoch to check.
* @return isOver True if the epoch is over, false otherwise.
*/
function epochIsOver(uint256 epoch) external view returns (bool isOver);
/**
* @notice Check if voting is active in the given epoch.
* @param epoch The epoch to check.
* @return isActive True if voting is active, false otherwise.
*/
function votingIsActive(uint256 epoch) external view returns (bool isActive);
/**
* @notice Get the end timestamp of the given epoch.
* @param epoch The epoch to get the end timestamp for.
* @return end The end timestamp of the epoch.
*/
function epochEnd(uint256 epoch) external pure returns (uint256 end);
/**
* @notice Get the current epoch.
*/
function currentEpoch() external view returns (uint256 epoch);
/**
* @notice Get the last epoch.
*/
function lastEpoch() external view returns (uint256 epoch);
/**
* @notice Get the next epoch.
*/
function nextEpoch() external view returns (uint256 epoch);
/**
* @notice Get the epoch period.
*/
// solhint-disable-next-line func-name-mixedcase
function EPOCH_PERIOD() external view returns (uint256);
/**
* @notice Get the pool TWA gap for the given token.
* @param token The token to get the pool TWA gap for.
* @return tickDiffD8 Tick difference in 8-decimal scale.
* @return isReady Bool indicator of whether the blocktimestamp is valid.
*/
function poolTwaGap(IERC20 token) external view returns (int256 tickDiffD8, bool isReady);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWETH9 is IERC20 {
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);
function deposit() external payable;
function withdraw(uint256) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @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 towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (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 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 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.
uint256 twos = denominator & (0 - denominator);
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 (unsignedRoundsUp(rounding) && 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
* towards zero.
*
* 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* 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 256, 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
// As the copyright holder of this work, Ubiquity Labs retains
// the right to distribute, use, and modify this code under any license of
// their choosing, in addition to the terms of the GPL-v2 or later.
pragma solidity ^0.8.25;
import {IMulticall} from "./IMulticall.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
// Modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/6ba452dea4258afe77726293435f10baf2bed265/contracts/utils/Multicall.sol
/*
* @notice Multicall
*/
abstract contract Multicall is IMulticall {
/**
* @notice This function allows multiple calls to different contract functions
* in a single transaction.
* @param data An array of encoded function call data.
* @return results An array of the results of the function calls.
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), data[i]);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @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.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @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
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
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
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
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
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
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
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
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
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
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
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
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
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
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
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
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
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
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
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
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
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
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
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
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
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
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
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
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
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
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
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
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
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
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
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
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
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
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
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
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
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
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
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
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
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
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
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
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
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
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
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
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
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
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
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
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
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
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
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
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
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
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
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @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
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @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
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @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
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @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
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @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
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @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
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @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
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @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
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @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
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @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
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @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
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @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
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @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
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @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
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @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
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @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
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @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
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @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
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @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
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @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
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @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
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @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
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @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
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @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
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @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
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @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
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @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
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @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
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @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
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @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
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {SafeCast as Cast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC5805} from "@openzeppelin/contracts/interfaces/IERC5805.sol";
import {IMaverickV2Pool} from "./v2-common/interfaces/IMaverickV2Pool.sol";
import {Multicall} from "./v2-common/base/Multicall.sol";
import {IVotingDistributor} from "./interfaces/IVotingDistributor.sol";
import {ILaunchFactory} from "./interfaces/ILaunchFactory.sol";
import {ISwapper} from "./interfaces/ISwapper.sol";
/**
* @notice This contract corresponds to a LaunchFactory and allows users to
* vote with a specified ve token on LaunchFactory tokens. The veVoters will
* get the fees that the given token pool generates, and the ve votes will
* direct bastToken emissions to the buy and burn tokens thereby enriching the
* meme token holders. There are three actors in the system:
*
* - Incentive Adders - adds emissions budget to this contract to be used to buy
* voted-upon tokens.
* - Voters - vote with their ve token balance to direct emissions to buy a
* token, in return, the voter gets fee from the token pool
* - Vote Incentivizers - add incentives to voters to encourage them to vote on a
* specific token
*
* @notice The lifecycle of an epoch is as follows:
*
* - Prior to or during an epoch, any Incentive Adder can permissionlessly add
* emissions budget to this contract to be distributed at the end of the epoch.
*
* - Each voting epoch is one week long where the end of the week is the epoch
* vote snapshot. The ve voting balance of each user will be used from this
* epoch-end timestamp. The voter can vote anytime during the epoch, but they
* can only cast their vote once.
*
* - During the epoch any party can permissionlessly add voting incentives.
*
* - At the end up the epoch, any user can disburse the emissions to a given
* token to buy the tokens. Likewise, any voter can claim both their share of
* the pool fees and any voting incentives.
*/
contract VotingDistributor is IVotingDistributor, Multicall, ReentrancyGuard {
using Cast for uint256;
using SafeERC20 for IERC20;
address internal constant BURN_ADDRESS = address(0xDEADBEEF);
uint256 public constant EPOCH_PERIOD = 3.5 days;
int256 public constant MAX_TICK_DIFF_D8 = 1.5e8;
IERC20 public immutable quoteToken;
ILaunchFactory public immutable factory;
IERC5805 public immutable veToken;
ISwapper public immutable swapper;
// checkpoints indexed by epoch start: time % EPOCH_PERIOD
mapping(uint256 epoch => CheckpointData) private checkpoints;
// data per epoch
struct CheckpointData {
mapping(address voter => VoterData) voterData;
mapping(IERC20 token => TokenData) tokenData;
// total quote budget for epoch
uint128 budget;
// total votes for epoch
uint128 totalVote;
// list of tokens with positive vote
IERC20[] tokens;
}
struct TokenData {
// vote for this token in this epoch
uint256 vote;
// whether quote budget has been sent to token
bool hasDistributed;
// token list of incentive tokens
IERC20[] incentiveTokens;
// amount of incentives
mapping(IERC20 incentiveToken => uint256 amount) voteIncentive;
// user vote amount
mapping(address voter => uint256 vote) userVote;
// whether user has collected incentives
mapping(address voter => mapping(IERC20 incentiveToken => bool hasCollected)) userHasCollected;
}
constructor(ILaunchFactory _factory, IERC5805 _veToken, ISwapper _swapper) {
quoteToken = _factory.quoteToken();
factory = _factory;
veToken = _veToken;
swapper = _swapper;
if (_veToken.clock() != block.timestamp.toUint48()) revert InvalidVeToken();
}
/////////////////////////////////////
/// Voting
/////////////////////////////////////
/// @inheritdoc IVotingDistributor
function vote(IERC20[] memory voteTargets, uint256[] memory weights) external nonReentrant {
uint256 epoch = currentEpoch();
_inVotePeriodCheck(epoch);
CheckpointData storage checkpoint = checkpoints[epoch];
VoterData storage voterData = checkpoint.voterData[msg.sender];
// ensure no double voting
if (voterData.hasVoted) revert SenderHasAlreadyVoted();
voterData.hasVoted = true;
// we know voting is active at this point
uint256 votingPower;
// get voting power of sender; includes any voting power delegated to
// this sender; voting power is ve pro rata at beginning of vote
// period
uint256 totalVote = veToken.getPastTotalSupply(epoch);
votingPower = Math.mulDiv(veToken.getPastVotes(msg.sender, epoch), 1e18, totalVote);
// make sure user has voting power
if (votingPower == 0) revert SenderHasNoVotingPower(msg.sender, epoch);
// compute total of relative weights user passed in
uint256 totalVoteWeight;
for (uint256 i; i < weights.length; i++) {
totalVoteWeight += weights[i];
}
// vote targets have to be sorted; start with zero so we can check sort
IERC20 lastToken = IERC20(address(0));
for (uint256 i; i < weights.length; i++) {
IERC20 token = voteTargets[i];
// ensure addresses are unique and sorted
if (token <= lastToken) revert InvalidTargetOrder(lastToken, token);
lastToken = token;
// check if token is from the factory
if (!factory.isFactoryToken(token)) revert NotFactoryToken(token);
// translate relative vote weights into votes
uint128 _vote = Math.mulDiv(weights[i], votingPower, totalVoteWeight).toUint128();
if (_vote == 0) revert InvalidVote(token, weights[i], totalVoteWeight, _vote);
// track voting
checkpoint.totalVote += _vote;
// track list for voter
voterData.votes.push(_vote);
voterData.tokens.push(token);
// track accounting data
TokenData storage tokenData = checkpoint.tokenData[token];
tokenData.userVote[msg.sender] += _vote;
// track global vote list
if (tokenData.vote == 0) checkpoint.tokens.push(token);
tokenData.vote += _vote;
emit Vote(quoteToken, epoch, msg.sender, token, _vote);
}
}
/////////////////////////////////////
/// Epoch Budgets
/////////////////////////////////////
/// @inheritdoc IVotingDistributor
function addDistributionBudget(uint256 amount, uint256 epoch) public checkEpoch(epoch) nonReentrant {
_addDistributionBudget(amount.toUint128(), epoch);
}
/// @inheritdoc IVotingDistributor
function addDistributionBudgetCurrentEpoch(uint256 amount) public nonReentrant {
_addDistributionBudget(amount.toUint128(), currentEpoch());
}
function _addDistributionBudget(uint128 amount, uint256 epoch) internal {
if (amount == 0) return;
// can only add if eppch is active or hasn't started
_epochNotEndedCheck(epoch);
CheckpointData storage checkpoint = checkpoints[epoch];
checkpoint.budget += amount;
quoteToken.safeTransferFrom(msg.sender, address(this), amount);
emit AddDistributionBudget(quoteToken, epoch, msg.sender, amount);
}
/// @inheritdoc IVotingDistributor
function addVoteIncentive(
IERC20 tokenToIncentivize,
IERC20 incentiveToken,
uint256 amount,
uint256 epoch
) public checkEpoch(epoch) nonReentrant {
_addVoteIncentive(tokenToIncentivize, incentiveToken, amount, epoch);
incentiveToken.safeTransferFrom(msg.sender, address(this), amount);
}
/// @inheritdoc IVotingDistributor
function addVoteIncentiveToCurrentEpoch(
IERC20 tokenToIncentivize,
IERC20 incentiveToken,
uint256 amount
) public nonReentrant {
_addVoteIncentive(tokenToIncentivize, incentiveToken, amount, currentEpoch());
incentiveToken.safeTransferFrom(msg.sender, address(this), amount);
}
/// @inheritdoc IVotingDistributor
function rollUnvotedIncentive(
IERC20 tokenToIncentivize,
IERC20 incentiveToken,
uint256 epoch
) public checkEpoch(epoch) nonReentrant returns (uint256 amount, uint256 newEpoch) {
// can only roll if vote period is over
_votePeriodEndedCheck(epoch);
TokenData storage tokenData = checkpoints[epoch].tokenData[tokenToIncentivize];
uint256 voteForThisToken = tokenData.vote;
if (voteForThisToken != 0) revert RolloverNotAllowed(voteForThisToken);
amount = tokenData.voteIncentive[incentiveToken];
if (amount == 0) revert ZeroAmount();
tokenData.voteIncentive[incentiveToken] = 0;
newEpoch = nextEpoch();
emit RollUnvotedIncentive(quoteToken, epoch, tokenToIncentivize, incentiveToken, amount, newEpoch);
_addVoteIncentive(tokenToIncentivize, incentiveToken, amount, newEpoch);
}
function _addVoteIncentive(
IERC20 tokenToIncentivize,
IERC20 incentiveToken,
uint256 amount,
uint256 epoch
) internal {
if (amount == 0) return;
// can only add if eppch is active or hasn't started
_epochNotEndedCheck(epoch);
TokenData storage tokenData = checkpoints[epoch].tokenData[tokenToIncentivize];
if (tokenData.voteIncentive[incentiveToken] == 0) tokenData.incentiveTokens.push(incentiveToken);
tokenData.voteIncentive[incentiveToken] += amount;
emit AddVoteIncentive(quoteToken, epoch, msg.sender, tokenToIncentivize, amount, incentiveToken);
}
/////////////////////////////////////
/// Post-Epoch Distribute
/////////////////////////////////////
/// @inheritdoc IVotingDistributor
function distributeAmount(
IERC20 token,
uint256 epoch
) public checkEpoch(epoch) returns (uint256 amountDistributed, uint256 amountBurned, bool twaGapPasses) {
// check voting is done
_votePeriodEndedCheck(epoch);
// check are not getting sandwiched
(, twaGapPasses) = poolTwaGap(token);
CheckpointData storage checkpoint = checkpoints[epoch];
TokenData storage tokenData = checkpoint.tokenData[token];
// check are not double distributing
if (tokenData.hasDistributed) revert TokenAlreadyDistributed(token, epoch);
// compute prorata
uint256 totalIncentivesForEpoch = checkpoint.budget;
uint256 totalVote = checkpoint.totalVote;
uint256 voteForThisToken = tokenData.vote;
if (totalVote == 0) revert NothingToDistribute();
amountDistributed = Math.mulDiv(totalIncentivesForEpoch, voteForThisToken, totalVote);
if (amountDistributed == 0) revert NothingToDistribute();
// buy token to burn address
amountBurned = swapper.buyTokenQuote(token, amountDistributed);
}
/// @inheritdoc IVotingDistributor
function distribute(
IERC20 token,
uint256 epoch
) public nonReentrant returns (uint256 amountDistributed, uint256 amountBurned) {
// check are not getting sandwiched
_poolTwaGapCheck(token);
(amountDistributed, , ) = distributeAmount(token, epoch);
checkpoints[epoch].tokenData[token].hasDistributed = true;
// buy token to burn address
quoteToken.forceApprove(address(swapper), amountDistributed);
(amountBurned, ) = swapper.buyTokenSpecifyRaffleRecipient(
BURN_ADDRESS,
token,
amountDistributed,
0,
msg.sender,
msg.sender
);
emit Distribute(quoteToken, epoch, msg.sender, token, amountDistributed, amountBurned);
}
/////////////////////////////////////
/// Post-Epoch Fee/Incentive Collection
/////////////////////////////////////
/// @inheritdoc IVotingDistributor
function collectVoteIncentive(
IERC20 token,
IERC20 incentiveToken,
uint256 epoch
) public nonReentrant returns (uint256 amount) {
if (incentiveToken == quoteToken) revert UseCollectVoteFee();
amount = collectVoteIncentiveAmount(msg.sender, token, incentiveToken, epoch);
checkpoints[epoch].tokenData[token].userHasCollected[msg.sender][incentiveToken] = true;
// send tokens to user
incentiveToken.safeTransfer(msg.sender, amount);
emit CollectVoteIncentive(quoteToken, epoch, msg.sender, token, amount, incentiveToken);
}
/// @inheritdoc IVotingDistributor
function collectVoteFee(
IERC20 token,
uint256 amountOutMinimum,
uint256 epoch
) public nonReentrant returns (uint256 amount, uint256 amountToken) {
amount = collectVoteIncentiveAmount(msg.sender, token, quoteToken, epoch);
checkpoints[epoch].tokenData[token].userHasCollected[msg.sender][quoteToken] = true;
// swap
quoteToken.safeTransfer(address(swapper), amount);
(amountToken, ) = swapper.buyTokenSpecifyRaffleRecipient(
msg.sender,
token,
amount,
amountOutMinimum,
msg.sender,
msg.sender
);
emit CollectVoteFee(quoteToken, epoch, msg.sender, token, amount, amountToken);
}
/// @inheritdoc IVotingDistributor
function collectVoteFeeAmount(
address user,
IERC20 token,
uint256 epoch
) public returns (uint256 amount, uint256 amountToken) {
amount = collectVoteIncentiveAmount(user, token, quoteToken, epoch);
amountToken = swapper.buyTokenQuote(token, amount);
}
/// @inheritdoc IVotingDistributor
function collectVoteIncentiveAmount(
address user,
IERC20 token,
IERC20 incentiveToken,
uint256 epoch
) public view checkEpoch(epoch) returns (uint256 amount) {
// check voting is done
_votePeriodEndedCheck(epoch);
CheckpointData storage checkpoint = checkpoints[epoch];
TokenData storage tokenData = checkpoint.tokenData[token];
// check are not double collecting
if (tokenData.userHasCollected[user][incentiveToken]) revert VoterAlreadyCollected(user, token, epoch);
// compute pro rata
uint256 totalIncentivesForEpoch = tokenData.voteIncentive[incentiveToken];
uint256 voteFromThisUser = tokenData.userVote[user];
uint256 voteForThisToken = tokenData.vote;
if (voteForThisToken == 0) revert NothingToDistribute();
amount = Math.mulDiv(totalIncentivesForEpoch, voteFromThisUser, voteForThisToken);
if (amount == 0) revert NothingToDistribute();
}
/////////////////////////////////////
/// Epoch State Viewers
/////////////////////////////////////
/// @inheritdoc IVotingDistributor
function getCheckpointData(
uint256 epoch,
IndexBounds memory bounds
)
public
view
checkEpoch(epoch)
returns (uint128 budget, uint128 totalVote, TokenView[] memory tokens, uint256 totalCount)
{
CheckpointData storage checkpoint = checkpoints[epoch];
budget = checkpoint.budget;
totalVote = checkpoint.totalVote;
totalCount = checkpoint.tokens.length;
bounds.endIndex = Math.min(totalCount, bounds.endIndex);
tokens = new TokenView[](bounds.endIndex - bounds.startIndex);
for (uint256 i = bounds.startIndex; i < bounds.endIndex; i++) {
tokens[i].token = checkpoint.tokens[i];
(tokens[i].incentiveTokens, tokens[i].voteIncentives, tokens[i].incentiveTokenCount) = getIncentiveData(
tokens[i].token,
epoch,
bounds.incentiveStartIndex,
bounds.incentiveEndIndex
);
TokenData storage tokenData = checkpoint.tokenData[tokens[i].token];
(tokens[i].votes, tokens[i].hasDistributed) = (tokenData.vote, tokenData.hasDistributed);
}
}
/// @inheritdoc IVotingDistributor
function getIncentiveData(
IERC20 token,
uint256 epoch,
uint256 startIndex,
uint256 endIndex
)
public
view
checkEpoch(epoch)
returns (IERC20[] memory incentiveTokens, uint256[] memory voteIncentives, uint256 totalCount)
{
TokenData storage tokenData = checkpoints[epoch].tokenData[token];
totalCount = tokenData.incentiveTokens.length;
endIndex = Math.min(totalCount, endIndex);
incentiveTokens = new IERC20[](endIndex - startIndex);
voteIncentives = new uint256[](endIndex - startIndex);
for (uint256 i = startIndex; i < endIndex; i++) {
incentiveTokens[i - startIndex] = tokenData.incentiveTokens[i];
voteIncentives[i - startIndex] = tokenData.voteIncentive[incentiveTokens[i - startIndex]];
}
}
/// @inheritdoc IVotingDistributor
function hasVoted(address user, uint256 epoch) public view checkEpoch(epoch) returns (bool) {
VoterData storage voterData = checkpoints[epoch].voterData[user];
return voterData.hasVoted;
}
function hasCollected(
address user,
IERC20 token,
IERC20 incentiveToken,
uint256 epoch
) public view checkEpoch(epoch) returns (bool) {
return checkpoints[epoch].tokenData[token].userHasCollected[user][incentiveToken];
}
/// @inheritdoc IVotingDistributor
function getVoterData(
address user,
uint256 epoch
) public view checkEpoch(epoch) returns (VoterData memory voterData) {
voterData = checkpoints[epoch].voterData[user];
}
function getVoterClaimData(
address user,
uint256 epoch,
IndexBounds memory bounds
) public checkEpoch(epoch) returns (ClaimData[] memory claimData, uint256 totalCount) {
VoterData storage voterData = checkpoints[epoch].voterData[user];
totalCount = voterData.tokens.length;
bounds.endIndex = Math.min(totalCount, bounds.endIndex);
claimData = new ClaimData[](bounds.endIndex - bounds.startIndex);
for (uint256 i = bounds.startIndex; i < bounds.endIndex; i++) {
claimData[i].token = voterData.tokens[i];
(claimData[i].incentiveTokens, , claimData[i].incentiveTokenCount) = getIncentiveData(
claimData[i].token,
epoch,
bounds.incentiveStartIndex,
bounds.incentiveEndIndex
);
uint256 loopLength = claimData[i].incentiveTokens.length;
claimData[i].hasCollected = new bool[](loopLength);
claimData[i].incentiveAmounts = new uint256[](loopLength);
claimData[i].tokenBoughtAmounts = new uint256[](loopLength);
for (uint256 k; k < loopLength; k++) {
IERC20 incentiveToken = claimData[i].incentiveTokens[k];
claimData[i].hasCollected[k] = hasCollected(user, claimData[i].token, incentiveToken, epoch);
if (claimData[i].hasCollected[k]) continue;
if (incentiveToken == quoteToken) {
(claimData[i].incentiveAmounts[k], claimData[i].tokenBoughtAmounts[k]) = collectVoteFeeAmount(
user,
claimData[i].token,
epoch
);
} else {
claimData[i].incentiveAmounts[k] = collectVoteIncentiveAmount(
user,
claimData[i].token,
incentiveToken,
epoch
);
}
}
}
}
/////////////////////////////////////
/// Epoch Checkers and Helpers
/////////////////////////////////////
modifier checkEpoch(uint256 epoch) {
if (!isEpoch(epoch)) revert InvalidEpoch(epoch);
_;
}
/// @inheritdoc IVotingDistributor
function isEpoch(uint256 epoch) public pure returns (bool _isEpoch) {
_isEpoch = epoch % EPOCH_PERIOD == 0;
}
/// @inheritdoc IVotingDistributor
function epochIsOver(uint256 epoch) public view returns (bool isOver) {
isOver = block.timestamp >= epochEnd(epoch);
}
/// @inheritdoc IVotingDistributor
function votingIsActive(uint256 epoch) public view returns (bool isActive) {
// vote period is `epoch` to `epoch + EPOCH_PERIOD
isActive = block.timestamp >= epoch && block.timestamp < epochEnd(epoch);
}
/// @inheritdoc IVotingDistributor
function epochEnd(uint256 epoch) public pure returns (uint256 end) {
end = epoch + EPOCH_PERIOD;
}
/// @inheritdoc IVotingDistributor
function currentEpoch() public view returns (uint256 epoch) {
epoch = (block.timestamp / EPOCH_PERIOD) * EPOCH_PERIOD;
}
/// @inheritdoc IVotingDistributor
function lastEpoch() public view returns (uint256 epoch) {
epoch = currentEpoch() - EPOCH_PERIOD;
}
/// @inheritdoc IVotingDistributor
function nextEpoch() public view returns (uint256 epoch) {
epoch = currentEpoch() + EPOCH_PERIOD;
}
function _inVotePeriodCheck(uint256 epoch) internal view {
if (!votingIsActive(epoch)) {
revert VotePeriodNotActive(block.timestamp, epoch, epochEnd(epoch));
}
}
function _votePeriodEndedCheck(uint256 epoch) internal view {
if (!epochIsOver(epoch)) {
revert VotePeriodNotEnded(block.timestamp, epoch, epochEnd(epoch));
}
}
function _epochNotEndedCheck(uint256 epoch) internal view {
if (epochIsOver(epoch)) {
revert EpochEnded(block.timestamp, epoch, epochEnd(epoch));
}
}
function _poolTwaGapCheck(IERC20 token) internal view {
// solhint-disable-next-line
if (tx.origin != msg.sender) revert MustCallFromEOA(tx.origin, msg.sender);
(int256 tickDiffD8, bool isReady) = poolTwaGap(token);
if (!isReady) {
revert PoolTwapGapTooBig(tickDiffD8, MAX_TICK_DIFF_D8);
}
}
/// @inheritdoc IVotingDistributor
function poolTwaGap(IERC20 token) public view returns (int256 tickDiffD8, bool isReady) {
IMaverickV2Pool pool = factory.managerFromToken(token).pool();
IMaverickV2Pool.State memory state = pool.getState();
int256 lastTwaD8 = state.lastTwaD8;
int256 activeTickD8 = state.activeTick * int256(1e8);
tickDiffD8 = lastTwaD8 - activeTickD8;
isReady = -MAX_TICK_DIFF_D8 <= tickDiffD8 && tickDiffD8 <= MAX_TICK_DIFF_D8;
}
}
{
"compilationTarget": {
"@fairlaunch/contracts/src/VotingDistributor.sol": "VotingDistributor"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 10
},
"remappings": []
}
[{"inputs":[{"internalType":"contract ILaunchFactory","name":"_factory","type":"address"},{"internalType":"contract IERC5805","name":"_veToken","type":"address"},{"internalType":"contract ISwapper","name":"_swapper","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"epochStart","type":"uint256"},{"internalType":"uint256","name":"epochEnd","type":"uint256"}],"name":"EpochEnded","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"InvalidEpoch","type":"error"},{"inputs":[{"internalType":"contract IERC20","name":"lastToken","type":"address"},{"internalType":"contract IERC20","name":"currentToken","type":"address"}],"name":"InvalidTargetOrder","type":"error"},{"inputs":[],"name":"InvalidVeToken","type":"error"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"uint256","name":"totalWeight","type":"uint256"},{"internalType":"uint256","name":"vote","type":"uint256"}],"name":"InvalidVote","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[{"internalType":"address","name":"txOrigin","type":"address"},{"internalType":"address","name":"msgSender","type":"address"}],"name":"MustCallFromEOA","type":"error"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"NotFactoryToken","type":"error"},{"inputs":[],"name":"NothingToDistribute","type":"error"},{"inputs":[{"internalType":"int256","name":"tickDiffD8","type":"int256"},{"internalType":"int256","name":"maxTickDiffD8","type":"int256"}],"name":"PoolTwapGapTooBig","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"voteForThisToken","type":"uint256"}],"name":"RolloverNotAllowed","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SenderHasAlreadyVoted","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"SenderHasNoVotingPower","type":"error"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"TokenAlreadyDistributed","type":"error"},{"inputs":[],"name":"UseCollectVoteFee","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"epochStart","type":"uint256"},{"internalType":"uint256","name":"epochEnd","type":"uint256"}],"name":"VotePeriodNotActive","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"epochStart","type":"uint256"},{"internalType":"uint256","name":"epochEnd","type":"uint256"}],"name":"VotePeriodNotEnded","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"VoterAlreadyCollected","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"quoteToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AddDistributionBudget","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"quoteToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"contract IERC20","name":"incentiveToken","type":"address"}],"name":"AddVoteIncentive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"quoteToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"quoteTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"CollectVoteFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"quoteToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"contract IERC20","name":"incentiveToken","type":"address"}],"name":"CollectVoteIncentive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"quoteToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"quoteTokenAmountDistributed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmountBurned","type":"uint256"}],"name":"Distribute","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"quoteToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"contract IERC20","name":"tokenToIncentivize","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"incentiveToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newEpoch","type":"uint256"}],"name":"RollUnvotedIncentive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"quoteToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"vote","type":"uint256"}],"name":"Vote","type":"event"},{"inputs":[],"name":"EPOCH_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TICK_DIFF_D8","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"addDistributionBudget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addDistributionBudgetCurrentEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenToIncentivize","type":"address"},{"internalType":"contract IERC20","name":"incentiveToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"addVoteIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenToIncentivize","type":"address"},{"internalType":"contract IERC20","name":"incentiveToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addVoteIncentiveToCurrentEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"collectVoteFee","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountToken","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"collectVoteFeeAmount","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountToken","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"contract IERC20","name":"incentiveToken","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"collectVoteIncentive","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"contract IERC20","name":"incentiveToken","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"collectVoteIncentiveAmount","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"distribute","outputs":[{"internalType":"uint256","name":"amountDistributed","type":"uint256"},{"internalType":"uint256","name":"amountBurned","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"distributeAmount","outputs":[{"internalType":"uint256","name":"amountDistributed","type":"uint256"},{"internalType":"uint256","name":"amountBurned","type":"uint256"},{"internalType":"bool","name":"twaGapPasses","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"epochEnd","outputs":[{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"epochIsOver","outputs":[{"internalType":"bool","name":"isOver","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract ILaunchFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"components":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"},{"internalType":"uint256","name":"incentiveStartIndex","type":"uint256"},{"internalType":"uint256","name":"incentiveEndIndex","type":"uint256"}],"internalType":"struct IVotingDistributor.IndexBounds","name":"bounds","type":"tuple"}],"name":"getCheckpointData","outputs":[{"internalType":"uint128","name":"budget","type":"uint128"},{"internalType":"uint128","name":"totalVote","type":"uint128"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"bool","name":"hasDistributed","type":"bool"},{"internalType":"contract IERC20[]","name":"incentiveTokens","type":"address[]"},{"internalType":"uint256[]","name":"voteIncentives","type":"uint256[]"},{"internalType":"uint256","name":"incentiveTokenCount","type":"uint256"}],"internalType":"struct IVotingDistributor.TokenView[]","name":"tokens","type":"tuple[]"},{"internalType":"uint256","name":"totalCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"getIncentiveData","outputs":[{"internalType":"contract IERC20[]","name":"incentiveTokens","type":"address[]"},{"internalType":"uint256[]","name":"voteIncentives","type":"uint256[]"},{"internalType":"uint256","name":"totalCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"components":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"},{"internalType":"uint256","name":"incentiveStartIndex","type":"uint256"},{"internalType":"uint256","name":"incentiveEndIndex","type":"uint256"}],"internalType":"struct IVotingDistributor.IndexBounds","name":"bounds","type":"tuple"}],"name":"getVoterClaimData","outputs":[{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"incentiveTokenCount","type":"uint256"},{"internalType":"contract IERC20[]","name":"incentiveTokens","type":"address[]"},{"internalType":"uint256[]","name":"incentiveAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenBoughtAmounts","type":"uint256[]"},{"internalType":"bool[]","name":"hasCollected","type":"bool[]"}],"internalType":"struct IVotingDistributor.ClaimData[]","name":"claimData","type":"tuple[]"},{"internalType":"uint256","name":"totalCount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getVoterData","outputs":[{"components":[{"internalType":"bool","name":"hasVoted","type":"bool"},{"internalType":"uint128[]","name":"votes","type":"uint128[]"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"}],"internalType":"struct IVotingDistributor.VoterData","name":"voterData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"contract IERC20","name":"incentiveToken","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"hasCollected","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"hasVoted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"isEpoch","outputs":[{"internalType":"bool","name":"_isEpoch","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"lastEpoch","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextEpoch","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"poolTwaGap","outputs":[{"internalType":"int256","name":"tickDiffD8","type":"int256"},{"internalType":"bool","name":"isReady","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenToIncentivize","type":"address"},{"internalType":"contract IERC20","name":"incentiveToken","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"rollUnvotedIncentive","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"newEpoch","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapper","outputs":[{"internalType":"contract ISwapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"veToken","outputs":[{"internalType":"contract IERC5805","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"voteTargets","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"votingIsActive","outputs":[{"internalType":"bool","name":"isActive","type":"bool"}],"stateMutability":"view","type":"function"}]