// 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: 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;
// factory contraints on pools
uint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%
uint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%
uint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%
uint64 constant MIN_LOOKBACK = 1 seconds;
uint64 constant MAX_TICK_SPACING = 10_000;
// pool constraints
uint8 constant NUMBER_OF_KINDS = 4;
int32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));
uint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale
int32 constant MAX_TICK_32 = int32(int256(MAX_TICK));
int32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));
uint256 constant MAX_BINS_TO_MERGE = 3;
uint128 constant MINIMUM_LIQUIDITY = 1e8;
// accessor named constants
uint8 constant ALL_KINDS_MASK = 0xF; // 0b1111
uint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000
uint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000
uint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111
// named values
address constant MERGED_LP_BALANCE_ADDRESS = address(0);
uint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;
uint128 constant ONE = 1e18;
uint128 constant ONE_SQUARED = 1e36;
int256 constant INT256_ONE = 1e18;
uint256 constant ONE_D8 = 1e8;
uint256 constant ONE_D3 = 1e3;
int40 constant INT_ONE_D8 = 1e8;
int40 constant HALF_TICK_D8 = 0.5e8;
uint8 constant DEFAULT_DECIMALS = 18;
uint256 constant DEFAULT_SCALE = 1;
bytes constant EMPTY_PRICE_BREAKS = hex"010000000000000000000000";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IMaverickV2Pool} from "@maverick/v2-common/contracts/interfaces/IMaverickV2Pool.sol";
import {IMulticall} from "@maverick/v2-common/contracts/base/IMulticall.sol";
import {IChecks} from "../base/IChecks.sol";
interface IBoostedPositionBase is IERC20Metadata, IChecks, IMulticall {
/**
* @notice BP Pool.
*/
function pool() external view returns (IMaverickV2Pool pool_);
/**
* @notice BP Bin kind (static, right, left, both).
*/
function kind() external view returns (uint8 kind_);
/**
* @notice Number of bins in the BP.
*/
function binCount() external view returns (uint8 binCount_);
/**
* @notice Liquidity balance in BP bins since last mint/burn operation.
*/
function getBinBalances() external view returns (uint128[] memory binBalances_);
/**
* @notice Liquidity balance in given BP bin since last mint/burn
* operation.
*/
function binBalances(uint256 index) external view returns (uint128 binBalance);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IMaverickV2Pool} from "@maverick/v2-common/contracts/interfaces/IMaverickV2Pool.sol";
interface IChecks {
error PositionExceededPriceBounds(uint256 sqrtPrice, uint256 minSqrtPrice, uint256 maxSqrtPrice);
error PositionDeadlinePassed(uint256 deadline, uint256 blockTimestamp);
/**
* @notice Function to check if the price of a pool is within specified bounds.
* @param pool The MaverickV2Pool contract to check.
* @param minSqrtPrice The minimum acceptable square root price.
* @param maxSqrtPrice The maximum acceptable square root price.
*/
function checkSqrtPrice(IMaverickV2Pool pool, uint256 minSqrtPrice, uint256 maxSqrtPrice) external payable;
/**
* @notice Function to check if a given deadline has passed.
* @param deadline The timestamp representing the deadline.
*/
function checkDeadline(uint256 deadline) external payable;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// 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/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// 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/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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
interface IHistoricalBalance {
/**
* @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);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IBoostedPositionBase} from "../boostedpositionbase/IBoostedPositionBase.sol";
interface IMaverickV2BoostedPosition is IBoostedPositionBase {
event BoostedPositionMigrateBinLiquidity(uint32 currentBinId, uint32 newBinId, uint128 newBinBalance);
error BoostedPositionTooLittleLiquidityAdded(uint256 binIdIndex, uint32 binId, uint128 required, uint128 available);
error BoostedPositionMovementBinNotMigrated();
/**
* @notice Mints BP LP position to recipient. User has to add liquidity to
* BP contract before making this call as this mint function simply assigns
* any new liquidity that this BP possesses in the pool to the recipient.
* Accordingly, this function should only be called in the same transaction
* where liquidity has been added to a pool as part of a multicall or
* through a router/manager contract.
*/
function mint(address recipient) external returns (uint256 deltaSupply);
/**
* @notice Burns BP LP positions and redeems the underlying A/B token to the recipient.
*/
function burn(address recipient, uint256 amount) external returns (uint256 tokenAOut, uint256 tokenBOut);
/**
* @notice Migrates all underlying movement-mode liquidity from a merged
* bin to the active parent of the merged bin. For Static BPs, this
* function is a no-op and never needs to be called.
*/
function migrateBinLiquidityToRoot() external;
/**
* @notice Array of ticks where the underlying BP liquidity exists.
*/
function getTicks() external view returns (int32[] memory ticks);
/**
* @notice Array of relative pool bin LP balance of the bins in the BP.
*/
function getRatios() external view returns (uint128[] memory ratios_);
/**
* @notice Array of BP binIds. Will revert if the BP is a movement mode
* and the underlying bin is merged.
*/
function getBinIds() external view returns (uint32[] memory binIds_);
/**
* @notice Array of BP binIds. Will not revert if the BP is a movement mode
* and the underlying bin is merged. For statis BPs, this returns the same
* value as `getBinIds`.
*/
function getRawBinIds() external view returns (uint32[] memory);
/**
* @notice Removes excess liquidity from the binId[0] bin and sends to
* recipient. Skimming is desirable if there is more than one bin in the BP
* and the skimmable amount is non-zero.
* Skimming amount is only applicable if the number of bins is more than
* one. For single-bin BPs, a user can effectively "skim" by minting BP
* tokens to themselves.
*/
function skim(address recipient) external returns (uint256 tokenAOut, uint256 tokenBOut);
/**
* @notice Returns the amount of binIds[0] LP balance that is skimmable in
* the BP. If this number is non-zero, it is desirable to skim before
* minting to ensure that the ratio solvency checks pass. Checking the
* skimmable amount is only applicable if the number of bins is more than
* one. For single-bin BPs, a user can effectively "skim" by minting BP
* tokens to themselves.
*/
function skimmableAmount() external view returns (uint128 amount);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IMaverickV2Factory} from "@maverick/v2-common/contracts/interfaces/IMaverickV2Factory.sol";
import {IMaverickV2Pool} from "@maverick/v2-common/contracts/interfaces/IMaverickV2Pool.sol";
import {IMaverickV2BoostedPosition} from "./IMaverickV2BoostedPosition.sol";
interface IMaverickV2BoostedPositionFactory {
error BoostedPositionFactoryNotFactoryPool();
error BoostedPositionPermissionedLiquidityPool();
error BoostedPositionFactoryKindNotSupportedByPool(uint8 poolKinds, uint8 kind);
error BoostedPositionFactoryInvalidRatioZero(uint128 ratioZero);
error BoostedPositionFactoryInvalidLengths(uint256 ratioLength, uint256 binIdsLength);
error BoostedPositionFactoryInvalidLengthForKind(uint8 kind, uint256 ratiosLength);
error BoostedPositionFactoryBinIdsNotSorted(uint256 index, uint32 lastBinId, uint32 thisBinId);
error BoostedPositionFactoryInvalidBinKind(uint8 inputKind, uint8 binKind, uint32 binId);
event CreateBoostedPosition(
IMaverickV2Pool pool,
uint32[] binIds,
uint128[] ratios,
uint8 kind,
IMaverickV2BoostedPosition boostedPosition
);
/**
* @notice Creates BP from the specified input parameters. Requirements:
*
* - Pool must be from pool factory
* - BP kind must be supported by the pool
* - BinIds have to be sorted in ascending order
* - ratios[0] must be 1e18; ratios are specified in D18 scale
* - ratio and binId arrays have to be the same length
* - movement-mode BPs can only have one binId
* - static-mode BPs can have at most 24 binIds
*/
function createBoostedPosition(
IMaverickV2Pool pool,
uint32[] memory binIds,
uint128[] memory ratios,
uint8 kind
) external returns (IMaverickV2BoostedPosition boostedPosition);
/**
* @notice Look up BPs by range of indexes.
*/
function lookup(uint256 startIndex, uint256 endIndex) external view returns (IMaverickV2BoostedPosition[] memory);
/**
* @notice Returns count of all BPs deployed by the factory.
*/
function boostedPositionsCount() external view returns (uint256 count);
/**
* @notice Look up BPs by range of indexes for a given pool.
*/
function lookup(
IMaverickV2Pool pool,
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2BoostedPosition[] memory);
/**
* @notice Returns count of all BPs deployed by the factory for a given
* pool.
*/
function boostedPositionsByPoolCount(IMaverickV2Pool pool) external view returns (uint256 count);
/**
* @notice Returns whether or not input BP was created by this factory.
*/
function isFactoryBoostedPosition(IMaverickV2BoostedPosition) external returns (bool);
/**
* @notice Pool factory that all BPs pool must be deployed from.
*/
function poolFactory() external returns (IMaverickV2Factory);
}
// 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
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IMaverickV2RewardFactory} from "./IMaverickV2RewardFactory.sol";
import {IMaverickV2Reward} from "./IMaverickV2Reward.sol";
import {IMaverickV2VotingEscrow} from "./IMaverickV2VotingEscrow.sol";
import {IMaverickV2IncentiveMatcherCaller} from "./IMaverickV2IncentiveMatcherCaller.sol";
interface IMaverickV2IncentiveMatcher {
error IncentiveMatcherInvalidEpoch(uint256 epoch);
error IncentiveMatcherNotRewardFactoryContract(IMaverickV2Reward rewardContract);
error IncentiveMatcherEpochHasNotEnded(uint256 currentTime, uint256 epochEnd);
error IncentiveMatcherVotePeriodNotActive(uint256 currentTime, uint256 voteStart, uint256 voteEnd);
error IncentiveMatcherVetoPeriodNotActive(uint256 currentTime, uint256 vetoStart, uint256 vetoEnd);
error IncentiveMatcherVetoPeriodHasNotEnded(uint256 currentTime, uint256 voteEnd);
error IncentiveMatcherSenderHasAlreadyVoted();
error IncentiveMatcherSenderHasNoVotingPower(address voter, uint256 voteSnapshotTimestamp);
error IncentiveMatcherInvalidTargetOrder(IMaverickV2Reward lastReward, IMaverickV2Reward voteReward);
error IncentiveMatcherInvalidVote(
IMaverickV2Reward rewardContract,
uint256 voteWeights,
uint256 totalVoteWeight,
uint256 vote
);
error IncentiveMatcherEpochAlreadyDistributed(uint256 epoch, IMaverickV2Reward rewardContract);
error IncentiveMatcherEpochHasEnded(uint256 epoch);
error IncentiveMatcherRewardDoesNotHaveVeStakingOption();
error IncentiveMatcherMatcherAlreadyVetoed(address matcher, IMaverickV2Reward rewardContract, uint256 epoch);
error IncentiveMatcherMatcherHasNoBudget(address user, uint256 epoch);
error IncentiveMatcherZeroAmount();
error IncentiveMatcherRewardInvalidIncentiveToken(uint256 epoch, address matcher, IERC20 token);
error IncentiveMatcherTokenMultipliersAlreadySet(uint256 epoch);
error IncentiveMatcherNotPermissionedCaller(address caller, address permissionedCaller);
event BudgetAdded(uint256 indexed epoch, address indexed matcher, uint256 matchAmount, uint256 voteAmount);
event BudgetRolledOver(
address indexed matcher,
uint256 matchRolloverAmount,
uint256 voteRolloverAmount,
uint256 matchedEpoch,
uint256 newEpoch
);
event IncentiveAdded(uint256 indexed epoch, IMaverickV2Reward indexed rewardContract, uint256 amount);
event Vote(uint256 indexed epoch, address indexed voter, IMaverickV2Reward indexed rewardContract, uint256 vote);
event Distribute(
uint256 indexed epoch,
IMaverickV2Reward indexed rewardContract,
address indexed matcher,
IERC20 _baseToken,
uint256 totalMatch,
uint256 voteMatch,
uint256 incentiveMatch
);
event Veto(
uint256 indexed epoch,
address indexed matcher,
IMaverickV2Reward indexed rewardContract,
uint256 voteProductDeduction,
uint256 externalIncentivesDeduction
);
event NonBaseTokenIncentiveAdded(
uint256 indexed epoch,
IMaverickV2Reward indexed rewardContract,
uint256 amount,
IERC20 token
);
event SetTokenMultipliers(uint256 indexed epoch, address indexed matcher, TokenMultiplier[] tokenMultipliers);
event NonBaseTokenIncentiveApplied(
uint256 indexed epoch,
address indexed matcher,
IMaverickV2Reward indexed rewardContract,
uint256 nonBaseAmount,
uint256 adjustedAmount,
uint256 tokenMultiplierIndex,
IERC20 token
);
struct TokenMultiplier {
IERC20 token;
uint256 multiplier;
}
struct MatchRewardData {
uint128 nonBaseExternalIncentives;
uint128 nonBaseVoteProduct;
bool hasDistributed;
bool hasVetoed;
bool hasAccountedForNonBaseIncentives;
}
struct EpochInformation {
uint128 votes;
uint128 voteProduct;
uint128 externalIncentives;
bool tracked;
}
struct RewardData {
IMaverickV2Reward rewardContract;
EpochInformation rewardInformation;
}
struct MatcherData {
uint128 matchBudget;
uint128 voteBudget;
uint128 externalIncentivesDeduction;
uint128 voteProductDeduction;
uint128 nonBaseExternalIncentives;
uint128 nonBaseVoteProduct;
TokenMultiplier[] tokenMultipliers;
}
struct NonBaseIncentives {
IERC20 token;
IMaverickV2Reward rewardContract;
uint256 incentiveAmount;
}
/**
* @notice This function retrieves checkpoint data for a specific epoch.
* @param epoch The epoch for which to retrieve checkpoint data.
* @return matchBudget The amount of match tokens budgeted for the epoch.
* @return voteBudget The amount of vote tokens budgeted for the epoch.
* @return epochTotals Struct with total votes, incentives, and pro rata product
*/
function checkpointData(
uint256 epoch
) external view returns (uint128 matchBudget, uint128 voteBudget, EpochInformation memory epochTotals);
/**
* @notice This function retrieves match budget checkpoint data for a specific epoch.
* @param epoch The epoch for which to retrieve checkpoint data.
* @param user Address of user who's budget to return.
* @return matchBudget The amount of match tokens budgeted for the epoch by this user.
* @return voteBudget The amount of vote tokens budgeted for the epoch by this user.
*/
function checkpointMatcherBudget(
uint256 epoch,
address user
) external view returns (uint128 matchBudget, uint128 voteBudget);
/**
* @notice This function retrieves the nonBase incentives for a set of reward contracts.
* @param epoch The epoch for which to retrieve checkpoint data.
* @param tokens Incentive tokens to check.
* @param rewardContracts Array of address of the reward contracts to check.
* @return nonBaseIncentives Array of Structs of incentive information. If
* a given reward contract/token pair has no incentives, then there will be
* no entry for that pair in the returned array.
*/
function checkpointNonBaseIncentiveInfo(
uint256 epoch,
IERC20[] memory tokens,
IMaverickV2Reward[] memory rewardContracts
) external view returns (NonBaseIncentives[] memory nonBaseIncentives);
/**
* @notice Returns data about a given matcher in an epoch.
* @param epoch The epoch for which to retrieve checkpoint data.
* @param matcher Address of matcher.
* @return matcherData Bugdet and deductions amounts for the epoch/matcher.
* The returned struct has a field for nonBaseExternalIncentives, but this
* value only tracks the amount of non-base incentives have have been
* "applied" such that a user called `applyNonBaseTokenMatch`. Prior to
* that, the amount of nonBase incentives is available through the
* `checkpointNonBaseIncentiveInfo` view function.
*/
function checkpointMatcherData(
uint256 epoch,
address matcher
) external view returns (MatcherData memory matcherData);
/**
* @notice This function retrieves checkpoint data for a specific reward contract within an epoch.
* @param epoch The epoch for which to retrieve checkpoint data.
* @param rewardContract The address of the reward contract.
* @return rewardData Includes votesByReward - The total number of votes
* cast for the reward contract in the epoch; and
* externalIncentivesByReward - The total amount of external incentives
* added for the reward contract in the epoch.
*/
function checkpointRewardData(
uint256 epoch,
IMaverickV2Reward rewardContract
) external view returns (RewardData memory rewardData);
/**
* @notice Returns the count of activeRewards for a given epoch.
*/
function activeRewardsCount(uint256 epoch) external view returns (uint256 count);
/**
* @notice Returns the count of budget matchers for a given epoch.
*/
function matchersCount(uint256 epoch) external view returns (uint256 count);
/**
* @notice Returns paginated list of all matchers for an epoch between the
* input indexes.
* @param epoch The epoch for which to retrieve data.
* @param startIndex The start index of the pagination.
* @param endIndex The end index of the pagination.
* @return returnElements Matcher addresses.
* @return matcherData Struct of information about each matcher for this epoch.
*/
function matchers(
uint256 epoch,
uint256 startIndex,
uint256 endIndex
) external view returns (address[] memory returnElements, MatcherData[] memory matcherData);
/**
* @notice This function retrieves checkpoint data for all active rewards
* contracts. User can paginate through the list by setting the input
* index values.
* @param epoch The epoch for which to retrieve checkpoint data.
* @param startIndex The start index of the pagination.
* @param endIndex The end index of the pagination.
* @return returnElements For each active Rewards with incentives, includes
* votesByReward - The total number of votes cast for the reward contract
* in the epoch; and externalIncentivesByReward - The total amount of
* external incentives added for the reward contract in the epoch.
*/
function activeRewards(
uint256 epoch,
uint256 startIndex,
uint256 endIndex
) external view returns (RewardData[] memory returnElements);
/**
* @notice View function that returns the amount of excess budget that cam be rolling over.
* @param epoch The epoch of the budget contribution.
* @param matcher Matcher address that added the match budget.
* @return matchRolloverAmount The amount of match tokens that will be rolled over.
* @return voteRolloverAmount The amount of vote tokens that will be rolled over.
*/
function getRolloverBudget(
uint256 epoch,
address matcher
) external view returns (uint256 matchRolloverAmount, uint256 voteRolloverAmount);
/**
* @notice This function checks if a given epoch is valid.
* @param epoch The epoch to check.
* @return _isEpoch True if the epoch input is a valid epoch, False otherwise.
*/
function isEpoch(uint256 epoch) external pure returns (bool _isEpoch);
/**
* @notice This function retrieves the number of the most recently completed epoch.
* @return epoch The number of the last epoch.
*/
function lastEpoch() external view returns (uint256 epoch);
/**
* @notice This function checks if a specific epoch has ended.
* @param epoch The epoch to check.
* @return isOver True if the epoch has ended, False otherwise.
*/
function epochIsOver(uint256 epoch) external view returns (bool isOver);
/**
* @notice This function checks if the vetoing period is active for a specific epoch.
* @param epoch The epoch to check.
* @return isActive True if the vetoing period is active, False otherwise.
*/
function vetoingIsActive(uint256 epoch) external view returns (bool isActive);
/**
* @notice This function checks if the voting period is active for a specific epoch.
* @param epoch The epoch to check.
* @return isActive True if the voting period is active, False otherwise.
*/
function votingIsActive(uint256 epoch) external view returns (bool isActive);
/**
* @notice This function retrieves the current epoch number.
* @return epoch The current epoch number.
*/
function currentEpoch() external view returns (uint256 epoch);
/**
* @notice Returns the timestamp when voting starts. This is also the
* voting snapshot timestamp where the voting power for users is determined
* for that epoch.
* @param epoch The epoch to check.
*/
function votingStart(uint256 epoch) external pure returns (uint256 start);
/**
* @notice This function checks if a specific reward contract has a veToken staking option.
* @notice For a rewards contract to be eligible for matching, the rewards
* contract must have the baseToken's ve contract as a locking option.
* @param rewardContract The address of the reward contract.
*/
function rewardHasVeCheck(IMaverickV2Reward rewardContract) external view;
/**
* @notice This function allows adding a new budget to the matcher contract.
* @notice called by protocol to add base token budget to an epoch that
* will be used for matching incentives. Can be called anytime before or
* during the epoch.
* @param matchBudget The amount of match tokens to add.
* @param voteBudget The amount of vote tokens to add.
* @param epoch The epoch for which the budget is added.
*/
function addMatchingBudget(uint128 matchBudget, uint128 voteBudget, uint256 epoch) external;
/**
* @notice This function allows adding a new incentive to the system.
* @notice Called by protocol to add incentives to a given rewards contract.
* @param rewardContract The address of the reward contract for the incentive.
* @param amount The total amount of the incentive.
* @param _duration The desired duration (in seconds) for which this incentive will be active.
* @return duration The actual duration (in seconds) for which this incentive will be active.
*/
function addIncentives(
IMaverickV2Reward rewardContract,
uint128 amount,
uint256 _duration
) external returns (uint256 duration);
/**
* @notice This function allows adding a new incentive to the system.
* @notice Called by the permissioned caller to add incentives to a given
* rewards contract.
* @param rewardContract The address of the reward contract for the incentive.
* @param amount The total amount of the incentive.
* @param token The token that the user will incentivize with.
*/
function permissionedAddIncentives(IMaverickV2Reward rewardContract, uint128 amount, IERC20 token) external;
/**
* @notice This function allows adding a new incentive in a non base token.
* @notice Called by protocol to add incentives to a given rewards contract.
* @param rewardContract The address of the reward contract for the incentive.
* @param amount The total amount of the incentive.
* @param _duration The desired duration (in seconds) for which this incentive will be active.
* @param token The token that the user will incentivize with.
* @return duration The actual duration (in seconds) for which this incentive will be active.
*/
function addNonBaseTokenIncentives(
IMaverickV2Reward rewardContract,
uint128 amount,
uint256 _duration,
IERC20 token
) external returns (uint256 duration);
/**
* @notice This function is available after the epoch ends and before any
* vetos are applied. This function applies a matcher's non-base token
* multiplier to non-base token incentives.
* @param rewardContract The address of the reward contract for the incentive.
* @param matcher The address of the matcher whose budget is getting distributed.
* @param tokenMultiplierIndex The index of the
* `checkpointMultipliers` struct corresponding to the desired token.
* @param epoch The epoch for which to apply non-base multiplier.
* @return adjustedAmount The amount of base token credited to the matching accounting.
*/
function applyNonBaseTokenMatch(
IMaverickV2Reward rewardContract,
address matcher,
uint256 tokenMultiplierIndex,
uint256 epoch
) external returns (uint128 adjustedAmount);
/**
* @notice This function sets the non-base token multipliers for a given
* epoch where the calling address is the budget matcher. Each matcher can
* have their own set of unique multipliers.
* @param _tokenMultipliers Array of (token,multiplier) structs.
* @param epoch The epoch for which the budget is added.
*/
function setTokenMultipliers(TokenMultiplier[] memory _tokenMultipliers, uint256 epoch) external;
/**
* @notice This function allows a user to cast a vote for specific reward contracts.
* @notice Called by ve token holders to vote for rewards contracts in a
* given epoch. voteTargets have to be passed in ascending sort order as a
* unique set of values. weights are relative values that are scales by the
* user's voting power.
* @param voteTargets An array of addresses for the reward contracts to vote for.
* @param weights An array of weights for each vote target.
*/
function vote(IMaverickV2Reward[] memory voteTargets, uint256[] memory weights) external;
/**
* @notice This function allows casting a veto on a specific reward contract for an epoch.
* @notice Veto a given rewards contract. If a rewards contract is vetoed,
* it will not receive any matching incentives. Rewards contracts can only
* be vetoed in the VETO_PERIOD seconds after the end of the epoch.
* @param rewardContract The address of the reward contract to veto.
*/
function veto(
IMaverickV2Reward rewardContract
) external returns (uint128 voteProductDeduction, uint128 externalIncentivesDeduction);
/**
* @notice This function allows distributing incentives for a specific reward contract in a particular epoch.
* @notice Called by any user to distribute matching incentives to a given
* reward contract for a given epoch. Call is only functional after the
* vetoing period for the epoch is over.
* @param rewardContract The address of the reward contract to distribute incentives for.
* @param matcher The address of the matcher whose budget is getting distributed.
* @param epoch The epoch for which to distribute incentives.
* @return totalMatch Total amount of matching tokens distributed.
* @return incentiveMatch Amount of match from incentive matching.
* @return voteMatch Amount of match from vote matching.
*/
function distribute(
IMaverickV2Reward rewardContract,
address matcher,
uint256 epoch
) external returns (uint256 totalMatch, uint256 incentiveMatch, uint256 voteMatch);
/**
* @notice This function allows rolling over excess budget from a previous
* epoch to a new epoch.
* @dev Excess vote match budget amounts that have not been distributed
* will not rollover and will become permanently locked. To avoid this, a
* matcher should call distribute on all rewards contracts before calling
* rollover.
* @param matchedEpoch The epoch from which to roll over the budget.
* @param newEpoch The epoch to which to roll over the budget.
* @return matchRolloverAmount The amount of match tokens rolled over.
* @return voteRolloverAmount The amount of vote tokens rolled over.
*/
function rolloverExcessBudget(
uint256 matchedEpoch,
uint256 newEpoch
) external returns (uint256 matchRolloverAmount, uint256 voteRolloverAmount);
/**
* @notice This function retrieves the epoch period length.
*/
// solhint-disable-next-line func-name-mixedcase
function EPOCH_PERIOD() external view returns (uint256);
/**
* @notice This function retrieves the period length of the epoch before
* voting starts. After an epoch begins, there is a window of time where
* voting is not possible which is the value this function returns.
*/
// solhint-disable-next-line func-name-mixedcase
function PRE_VOTE_PERIOD() external view returns (uint256);
/**
* @notice This function retrieves the vetoing period length.
*/
// solhint-disable-next-line func-name-mixedcase
function VETO_PERIOD() external view returns (uint256);
/**
* @notice The function retrieves the notify period length, which is the
* amount of time in seconds during which the matching reward will be
* distributed through the rewards contract.
*/
// solhint-disable-next-line func-name-mixedcase
function NOTIFY_PERIOD() external view returns (uint256);
/**
* @notice Permissioned caller address that can call the
* permissionedAddIncentives function. This value is set by the
* IncentiveMatcherFactory on deploy.
*/
function permissionedCaller() external view returns (IMaverickV2IncentiveMatcherCaller);
/**
* @notice This function retrieves the base token used by the IncentiveMatcher contract.
* @return The address of the base token.
*/
function baseToken() external view returns (IERC20);
/**
* @notice This function retrieves the address of the MaverickV2RewardFactory contract.
* @return The address of the MaverickV2RewardFactory contract.
*/
function rewardFactory() external view returns (IMaverickV2RewardFactory);
/**
* @notice This function retrieves the address of the veToken contract.
* @return The address of the veToken contract.
*/
function veToken() external view returns (IMaverickV2VotingEscrow);
/**
* @notice This function checks if a specific user has voted in a particular epoch.
* @param user The address of the user.
* @param epoch The epoch to check.
* @return True if the user has voted, False otherwise.
*/
function hasVoted(address user, uint256 epoch) external view returns (bool);
/**
* @notice This function checks if a specific matcher has cast a veto on a reward contract for an epoch.
* @param matcher The address of the IncentiveMatcher contract.
* @param rewardContract The address of the reward contract.
* @param epoch The epoch to check.
* @return True if the matcher has cast a veto, False otherwise.
*/
function hasVetoed(address matcher, IMaverickV2Reward rewardContract, uint256 epoch) external view returns (bool);
/**
* @notice This function checks if incentives have been distributed for a specific reward contract in an epoch.
* @param matcher The address of the IncentiveMatcher contract.
* @param rewardContract The address of the reward contract.
* @param epoch The epoch to check.
* @return True if incentives have been distributed, False otherwise.
*/
function hasDistributed(
address matcher,
IMaverickV2Reward rewardContract,
uint256 epoch
) external view returns (bool);
/**
* @notice This function calculates the end timestamp for a specific epoch.
* @param epoch The epoch for which to calculate the end timestamp.
* @return end The end timestamp of the epoch.
*/
function epochEnd(uint256 epoch) external pure returns (uint256 end);
/**
* @notice This function calculates the end timestamp for the vetoing period of a specific epoch.
* @param epoch The epoch for which to calculate the vetoing period end timestamp.
* @return end The end timestamp of the vetoing period for the epoch.
*/
function vetoingEnd(uint256 epoch) external pure returns (uint256 end);
/**
* @notice This function checks if the vetoing period is over for a specific epoch.
* @param epoch The epoch to check.
* @return isOver True if the vetoing period has ended for the given epoch, False otherwise.
*/
function vetoingIsOver(uint256 epoch) external view returns (bool isOver);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IMaverickV2Reward} from "./IMaverickV2Reward.sol";
import {IMaverickV2IncentiveMatcher} from "./IMaverickV2IncentiveMatcher.sol";
interface IMaverickV2IncentiveMatcherCaller {
error IncentiveMatcherCallerMatchersNotSorted(
uint256 index,
IMaverickV2IncentiveMatcher lastMatcher,
IMaverickV2IncentiveMatcher incentiveMatcher
);
function addIncentives(
IMaverickV2Reward rewardContract,
uint128 amount,
uint256 _duration,
IERC20 token,
IMaverickV2IncentiveMatcher[] memory incentiveMatchers
) external returns (uint256 duration);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IMaverickV2VotingEscrow} from "./IMaverickV2VotingEscrow.sol";
import {IMaverickV2RewardFactory} from "./IMaverickV2RewardFactory.sol";
import {IMaverickV2IncentiveMatcher} from "./IMaverickV2IncentiveMatcher.sol";
import {IMaverickV2VotingEscrowFactory} from "./IMaverickV2VotingEscrowFactory.sol";
import {IMaverickV2IncentiveMatcherCaller} from "./IMaverickV2IncentiveMatcherCaller.sol";
interface IMaverickV2IncentiveMatcherFactory {
error VotingEscrowTokenDoesNotExists(IERC20 baseToken);
event CreateIncentiveMatcher(
IERC20 baseToken,
IMaverickV2VotingEscrow veToken,
IMaverickV2IncentiveMatcher incentiveMatcher
);
struct IncentiveMatcherParameters {
IERC20 baseToken;
IMaverickV2VotingEscrow veToken;
IMaverickV2RewardFactory factory;
}
function incentiveMatcherParameters()
external
view
returns (IERC20 baseToken, IMaverickV2VotingEscrow veToken, IMaverickV2RewardFactory factory);
/**
* @notice This function retrieves the address of the
* MaverickV2IncentiveMatcherCaller which is permissioned to make
* addIncentive calls to IncentiveMatcher contracts.
*/
function permissionedCaller() external view returns (IMaverickV2IncentiveMatcherCaller);
/**
* @notice This function retrieves the address of the MaverickV2VotingEscrowFactory contract.
* @return The address of the MaverickV2VotingEscrowFactory contract.
*/
function veFactory() external view returns (IMaverickV2VotingEscrowFactory);
/**
* @notice This function retrieves the address of the MaverickV2RewardFactory contract.
* @return The address of the MaverickV2RewardFactory contract.
*/
function rewardFactory() external view returns (IMaverickV2RewardFactory);
/**
* @notice This function checks if the current contract is a factory contract for IncentiveMatchers.
* @param incentiveMatcher The address of the corresponding IncentiveMatcher contract.
* @return isFactoryContract True if the contract is a factory contract, False otherwise.
*/
function isFactoryIncentiveMatcher(
IMaverickV2IncentiveMatcher incentiveMatcher
) external view returns (bool isFactoryContract);
/**
* @notice This function retrieves the address of the IncentiveMatcher
* contract associated with the current veToken.
* @param veToken The voting escrow token to look up.
* @return incentiveMatcher The address of the corresponding IncentiveMatcher contract.
*/
function incentiveMatcherForVe(
IMaverickV2VotingEscrow veToken
) external view returns (IMaverickV2IncentiveMatcher incentiveMatcher);
/**
* @notice This function creates a new IncentiveMatcher contract for a
* given base token. The basetoken is required to have a deployed ve token
* before incentive matcher can be created. If no ve token exists, this
* function will revert. A ve token can be created with the ve token
* factory: `veFactory()`.
* @param baseToken The base token for the new IncentiveMatcher.
* @return veToken The voting escrow token for the IncentiveMatcher.
* @return incentiveMatcher The address of the newly created IncentiveMatcher contract.
*/
function createIncentiveMatcher(
IERC20 baseToken
) external returns (IMaverickV2VotingEscrow veToken, IMaverickV2IncentiveMatcher incentiveMatcher);
/**
* @notice This function retrieves a list of existing IncentiveMatcher contracts.
* @param startIndex The starting index of the list to retrieve.
* @param endIndex The ending index of the list to retrieve.
* @return returnElements An array of IncentiveMatcher contracts within the specified range.
*/
function incentiveMatchers(
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2IncentiveMatcher[] memory returnElements);
/**
* @notice This function returns the total number of existing IncentiveMatcher contracts.
*/
function incentiveMatchersCount() external view returns (uint256 count);
}
// 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 {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {INft} from "@maverick/v2-supplemental/contracts/positionbase/INft.sol";
import {IMulticall} from "@maverick/v2-common/contracts/base/IMulticall.sol";
import {IMaverickV2VotingEscrow} from "./IMaverickV2VotingEscrow.sol";
import {IMaverickV2RewardVault} from "./IMaverickV2RewardVault.sol";
import {IRewardAccounting} from "../rewardbase/IRewardAccounting.sol";
interface IMaverickV2Reward is INft, IMulticall, IRewardAccounting {
event NotifyRewardAmount(
address sender,
IERC20 rewardTokenAddress,
uint256 amount,
uint256 duration,
uint256 rewardRate
);
event GetReward(
address sender,
uint256 tokenId,
address recipient,
uint8 rewardTokenIndex,
uint256 stakeDuration,
IERC20 rewardTokenAddress,
RewardOutput rewardOutput,
uint256 lockupId
);
event UnStake(address indexed sender, uint256 indexed tokenId, address indexed recipient, uint256 amount);
event Stake(address indexed sender, uint256 indexed tokenId, uint256 amount);
error RewardDurationOutOfBounds(uint256 duration, uint256 minDuration, uint256 maxDuration);
error RewardZeroAmount();
error RewardNotValidRewardToken(IERC20 rewardTokenAddress);
error RewardNotValidIndex(uint8 index);
error RewardTokenCannotBeStakingToken(IERC20 stakingToken);
error RewardTransferNotSupported();
error RewardNotApprovedGetter(uint256 tokenId, address approved, address getter);
error RewardUnboostedTimePeriodNotMet(uint256 timestamp, uint256 minTimestamp);
struct RewardInfo {
// Timestamp of when the rewards finish
uint256 finishAt;
// Minimum of last updated time and reward finish time
uint256 updatedAt;
// Reward to be paid out per second
uint256 rewardRate;
// Escrowed rewards
uint256 escrowedReward;
// Sum of (reward rate * dt * 1e18 / total supply)
uint256 rewardPerTokenStored;
// Reward Token to be emitted
IERC20 rewardToken;
// ve locking contract
IMaverickV2VotingEscrow veRewardToken;
// amount available to push to ve as incentive
uint128 unboostedAmount;
// timestamp of unboosted push
uint256 lastUnboostedPushTimestamp;
}
struct ContractInfo {
// Reward Name
string name;
// Reward Symbol
string symbol;
// total supply staked
uint256 totalSupply;
// staking token
IERC20 stakingToken;
}
struct EarnedInfo {
// earned
uint256 earned;
// reward token
IERC20 rewardToken;
}
struct RewardOutput {
uint256 amount;
bool asVe;
IMaverickV2VotingEscrow veContract;
}
// solhint-disable-next-line func-name-mixedcase
function MAX_DURATION() external view returns (uint256);
// solhint-disable-next-line func-name-mixedcase
function MIN_DURATION() external view returns (uint256);
/**
* @notice This function retrieves the minimum time gap in seconds that
* must have elasped between calls to `pushUnboostedToVe()`.
*/
// solhint-disable-next-line func-name-mixedcase
function UNBOOSTED_MIN_TIME_GAP() external view returns (uint256);
/**
* @notice This function retrieves the address of the token used for
* staking in this reward contract.
* @return The address of the staking token (IERC20).
*/
function stakingToken() external view returns (IERC20);
/**
* @notice This function retrieves the address of the MaverickV2RewardVault
* contract associated with this reward contract.
* @return The address of the IMaverickV2RewardVault contract.
*/
function vault() external view returns (IMaverickV2RewardVault);
/**
* @notice This function retrieves information about all available reward tokens for this reward contract.
* @return info An array of RewardInfo structs containing details about each reward token.
*/
function rewardInfo() external view returns (RewardInfo[] memory info);
/**
* @notice This function retrieves information about all available reward
* tokens and overall contract details for this reward contract.
* @return info An array of RewardInfo structs containing details about each reward token.
* @return _contractInfo A ContractInfo struct containing overall contract details.
*/
function contractInfo() external view returns (RewardInfo[] memory info, ContractInfo memory _contractInfo);
/**
* @notice This function calculates the total amount of all earned rewards
* for a specific tokenId across all reward tokens.
* @param tokenId The address of the tokenId for which to calculate earned rewards.
* @return earnedInfo An array of EarnedInfo structs containing details about earned rewards for each supported token.
*/
function earned(uint256 tokenId) external view returns (EarnedInfo[] memory earnedInfo);
/**
* @notice This function calculates the total amount of earned rewards for
* a specific tokenId for a particular reward token.
* @param tokenId The address of the tokenId for which to calculate earned rewards.
* @param rewardTokenAddress The address of the specific reward token.
* @return amount The total amount of earned rewards for the specified token.
*/
function earned(uint256 tokenId, IERC20 rewardTokenAddress) external view returns (uint256);
/**
* @notice This function retrieves the internal index associated with a specific reward token address.
* @param rewardToken The address of the reward token to get the index for.
* @return rewardTokenIndex The internal index of the token within the reward contract (uint8).
*/
function tokenIndex(IERC20 rewardToken) external view returns (uint8 rewardTokenIndex);
/**
* @notice This function retrieves the total number of supported reward tokens in this reward contract.
* @return count The total number of reward tokens (uint256).
*/
function rewardTokenCount() external view returns (uint256);
/**
* @notice This function transfers a specified amount of reward tokens from
* the caller to distribute them over a defined duration. The caller will
* need to approve this rewards contract to make the transfer on the
* caller's behalf. See `notifyRewardAmount` for details of how the
* duration is set by the rewards contract.
* @param rewardToken The address of the reward token to transfer.
* @param duration The duration (in seconds) over which to distribute the rewards.
* @param amount The amount of reward tokens to transfer.
* @return _duration The duration in seconds that the incentives will be distributed over.
*/
function transferAndNotifyRewardAmount(
IERC20 rewardToken,
uint256 duration,
uint256 amount
) external returns (uint256 _duration);
/**
* @notice This function notifies the vault to distribute a previously
* transferred amount of reward tokens over a defined duration. (Assumes
* tokens are already in the contract).
* @dev The duration of the distribution may not be the same as the input
* duration. If this notify amount is less than the amount already pending
* disbursement, then this new amount will be distributed as the same rate
* as the existing rate and that will dictate the duration. Alternatively,
* if the amount is more than the pending disbursement, then the input
* duration will be honored and all pending disbursement tokens will also be
* distributed at this newly set rate.
* @param rewardToken The address of the reward token to distribute.
* @param duration The duration (in seconds) over which to distribute the rewards.
* @return _duration The duration in seconds that the incentives will be distributed over.
*/
function notifyRewardAmount(IERC20 rewardToken, uint256 duration) external returns (uint256 _duration);
/**
* @notice This function transfers a specified amount of staking tokens
* from the caller to the staking `vault()` and stakes them on the
* recipient's behalf. The user has to approve this reward contract to
* transfer the staking token on their behalf for this function not to
* revert.
* @param tokenId Nft tokenId to stake for the staked tokens.
* @param _amount The amount of staking tokens to transfer and stake.
* @return amount The amount of staking tokens staked. May differ from
* input if there were unstaked tokens in the vault prior to this call.
* @return stakedTokenId TokenId where liquidity was staked to. This may
* differ from the input tokenIf if the input `tokenId=0`.
*/
function transferAndStake(
uint256 tokenId,
uint256 _amount
) external returns (uint256 amount, uint256 stakedTokenId);
/**
* @notice This function stakes the staking tokens to the specified
* tokenId. If `tokenId=0` is passed in, then this function will look up
* the caller's tokenIds and stake to the zero-index tokenId. If the user
* does not yet have a staking NFT tokenId, this function will mint one for
* the sender and stake to that newly-minted tokenId.
*
* @dev The amount staked is derived by looking at the new balance on
* the `vault()`. So, for staking to yield a non-zero balance, the user
* will need to have transfered the `stakingToken()` to the `vault()` prior
* to calling `stake`. Note, tokens sent to the reward contract instead
* of the vault will not be stakable and instead will be eligible to be
* disbursed as rewards to stakers. This is an advanced usage function.
* If in doubt about the mechanics of staking, use `transferAndStake()`
* instead.
* @param tokenId The address of the tokenId whose tokens to stake.
* @return amount The amount of staking tokens staked (uint256).
* @return stakedTokenId TokenId where liquidity was staked to. This may
* differ from the input tokenIf if the input `tokenId=0`.
*/
function stake(uint256 tokenId) external returns (uint256 amount, uint256 stakedTokenId);
/**
* @notice This function initiates unstaking of a specified amount of
* staking tokens for the caller and sends them to a recipient.
* @param tokenId The address of the tokenId whose tokens to unstake.
* @param amount The amount of staking tokens to unstake (uint256).
*/
function unstakeToOwner(uint256 tokenId, uint256 amount) external;
/**
* @notice This function initiates unstaking of a specified amount of
* staking tokens on behalf of a specific tokenId and sends them to a recipient.
* @dev To unstakeFrom, the caller must have an approval allowance of at
* least `amount`. Approvals follow the ERC-721 approval interface.
* @param tokenId The address of the tokenId whose tokens to unstake.
* @param recipient The address to which the unstaked tokens will be sent.
* @param amount The amount of staking tokens to unstake (uint256).
*/
function unstake(uint256 tokenId, address recipient, uint256 amount) external;
/**
* @notice This function retrieves the claimable reward for a specific
* reward token and stake duration for the caller.
* @param tokenId The address of the tokenId whose reward to claim.
* @param rewardTokenIndex The internal index of the reward token.
* @param stakeDuration The duration (in seconds) for which the rewards were staked.
* @return rewardOutput A RewardOutput struct containing details about the claimable reward.
*/
function getRewardToOwner(
uint256 tokenId,
uint8 rewardTokenIndex,
uint256 stakeDuration
) external returns (RewardOutput memory rewardOutput);
/**
* @notice This function retrieves the claimable reward for a specific
* reward token, stake duration, and lockup ID for the caller.
* @param tokenId The address of the tokenId whose reward to claim.
* @param rewardTokenIndex The internal index of the reward token.
* @param stakeDuration The duration (in seconds) for which the rewards were staked.
* @param lockupId The unique identifier for the specific lockup (optional).
* @return rewardOutput A RewardOutput struct containing details about the claimable reward.
*/
function getRewardToOwnerForExistingVeLockup(
uint256 tokenId,
uint8 rewardTokenIndex,
uint256 stakeDuration,
uint256 lockupId
) external returns (RewardOutput memory);
/**
* @notice This function retrieves the claimable reward for a specific
* reward token and stake duration for a specified tokenId and sends it to
* a recipient. If the reward is staked in the corresponding veToken, a
* new lockup in the ve token will be created.
* @param tokenId The address of the tokenId whose reward to claim.
* @param recipient The address to which the claimed reward will be sent.
* @param rewardTokenIndex The internal index of the reward token.
* @param stakeDuration The duration (in seconds) for which the rewards
* will be staked in the ve contract.
* @return rewardOutput A RewardOutput struct containing details about the claimable reward.
*/
function getReward(
uint256 tokenId,
address recipient,
uint8 rewardTokenIndex,
uint256 stakeDuration
) external returns (RewardOutput memory);
/**
* @notice This function retrieves a list of all supported tokens in the reward contract.
* @param includeStakingToken A flag indicating whether to include the staking token in the list.
* @return tokens An array of IERC20 token addresses.
*/
function tokenList(bool includeStakingToken) external view returns (IERC20[] memory tokens);
/**
* @notice This function retrieves the veToken contract associated with a
* specific index within the reward contract.
* @param index The index of the veToken to retrieve.
* @return output The IMaverickV2VotingEscrow contract associated with the index.
*/
function veTokenByIndex(uint8 index) external view returns (IMaverickV2VotingEscrow output);
/**
* @notice This function retrieves the reward token contract associated
* with a specific index within the reward contract.
* @param index The index of the reward token to retrieve.
* @return output The IERC20 contract associated with the index.
*/
function rewardTokenByIndex(uint8 index) external view returns (IERC20 output);
/**
* @notice This function calculates the boosted amount an tokenId would
* receive based on their veToken balance and stake duration.
* @param tokenId The address of the tokenId for which to calculate the boosted amount.
* @param veToken The IMaverickV2VotingEscrow contract representing the veToken used for boosting.
* @param rawAmount The raw (unboosted) amount.
* @param stakeDuration The duration (in seconds) for which the rewards would be staked.
* @return earnedAmount The boosted amount the tokenId would receive (uint256).
* @return asVe A boolean indicating whether the boosted amount is
* staked in the veToken (true) or is disbursed without ve staking required (false).
*/
function boostedAmount(
uint256 tokenId,
IMaverickV2VotingEscrow veToken,
uint256 rawAmount,
uint256 stakeDuration
) external view returns (uint256 earnedAmount, bool asVe);
/**
* @notice This function is used to push unboosted rewards to the veToken
* contract. This unboosted reward amount is then distributed to the
* veToken holders. This function will revert if less than
* `UNBOOSTED_MIN_TIME_GAP()` seconds have passed since the last call.
* @param rewardTokenIndex The internal index of the reward token.
* @return amount The amount of unboosted rewards pushed (uint128).
* @return timepoint The timestamp associated with the pushed rewards (uint48).
* @return batchIndex The batch index for the pushed rewards (uint256).
*/
function pushUnboostedToVe(
uint8 rewardTokenIndex
) external returns (uint128 amount, uint48 timepoint, uint256 batchIndex);
/**
* @notice Mints an NFT stake to a user. This NFT will not possesses any
* assets until a user `stake`s asset to the NFT tokenId as part of a
* separate call.
* @param recipient The address that owns the output NFT
*/
function mint(address recipient) external returns (uint256 tokenId);
/**
* @notice Mints an NFT stake to caller. This NFT will not possesses any
* assets until a user `stake`s asset to the NFT tokenId as part of a
* separate call.
*/
function mintToSender() external returns (uint256 tokenId);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IMaverickV2BoostedPositionFactory} from "@maverick/v2-supplemental/contracts/interfaces/IMaverickV2BoostedPositionFactory.sol";
import {IMaverickV2VotingEscrowFactory} from "./IMaverickV2VotingEscrowFactory.sol";
import {IMaverickV2VotingEscrow} from "./IMaverickV2VotingEscrow.sol";
import {IMaverickV2Reward} from "./IMaverickV2Reward.sol";
interface IMaverickV2RewardFactory {
error RewardFactoryNotFactoryBoostedPosition();
error RewardFactoryTooManyRewardTokens();
error RewardFactoryRewardAndVeLengthsAreNotEqual();
error RewardFactoryInvalidVeBaseTokenPair();
error RewardFactoryTokensNotSorted(uint256 index, IERC20 lastRewardToken, IERC20 rewardToken);
event CreateRewardsContract(
IERC20 stakeToken,
IERC20[] rewardTokens,
IMaverickV2VotingEscrow[] veTokens,
IMaverickV2Reward rewardsContract,
bool isFactoryBoostedPosition
);
/**
* @notice This function creates a new MaverickV2Reward contract associated
* with a specific stake token contract and set of reward and voting
* escrow tokens.
* @param stakeToken Token to be staked in reward contract; e.g. a boosted position contract.
* @param rewardTokens An array of IERC20 token addresses representing the available reward tokens.
* @param veTokens An array of IMaverickV2VotingEscrow contract addresses
* representing the associated veTokens for boosting.
* @return rewardsContract The newly created IMaverickV2Reward contract.
*/
function createRewardsContract(
IERC20 stakeToken,
IERC20[] memory rewardTokens,
IMaverickV2VotingEscrow[] memory veTokens
) external returns (IMaverickV2Reward rewardsContract);
/**
* @notice This function retrieves the address of the MaverickV2BoostedPositionFactory contract.
* @return factory The address of the IMaverickV2BoostedPositionFactory contract.
*/
function boostedPositionFactory() external returns (IMaverickV2BoostedPositionFactory);
/**
* @notice This function retrieves the address of the MaverickV2VotingEscrowFactory contract.
* @return factory The address of the IMaverickV2VotingEscrowFactory contract.
*/
function votingEscrowFactory() external returns (IMaverickV2VotingEscrowFactory);
/**
* @notice This function checks if a provided IMaverickV2Reward contract is
* a valid contract created by this factory.
* @param reward The IMaverickV2Reward contract to check.
* @return isFactoryContract True if the contract is a valid factory-created reward contract, False otherwise.
*/
function isFactoryContract(IMaverickV2Reward reward) external returns (bool);
/**
* @notice This function retrieves a list of all MaverickV2Reward contracts
* associated with a specific staking token contract within a specified
* range.
* @param stakeToken Lookup token.
* @param startIndex The starting index of the list to retrieve.
* @param endIndex The ending index of the list to retrieve.
* @return rewardsContract An array of IMaverickV2Reward contracts
* associated with the BoostedPosition within the specified range.
*/
function rewardsForStakeToken(
IERC20 stakeToken,
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2Reward[] memory rewardsContract);
/**
* @notice Returns the number of reward contracts this factory has deployed
* for a given staking token.
*/
function rewardsForStakeTokenCount(IERC20 stakeToken) external view returns (uint256 count);
/**
* @notice This function retrieves a list of all MaverickV2Reward contracts within a specified range.
* @param startIndex The starting index of the list to retrieve.
* @param endIndex The ending index of the list to retrieve.
* @return rewardsContract An array of IMaverickV2Reward contracts within the specified range.
*/
function rewards(
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2Reward[] memory rewardsContract);
/**
* @notice Returns the number of reward contracts this factory has deployed.
*/
function rewardsCount() external view returns (uint256 count);
/**
* @notice This function retrieves a list of all MaverickV2Reward contracts
* within a specified range that have a staking token that is a boosted
* position from the maverick boosted position contract.
* @param startIndex The starting index of the list to retrieve.
* @param endIndex The ending index of the list to retrieve.
* @return rewardsContract An array of IMaverickV2Reward contracts within the specified range.
*/
function boostedPositionRewards(
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2Reward[] memory);
/**
* @notice Returns the number of reward contracts where the staking token
* is a booste position that this factory has deployed.
*/
function boostedPositionRewardsCount() external view returns (uint256 count);
/**
* @notice This function retrieves a list of all MaverickV2Reward contracts
* within a specified range that have a staking token that is not a boosted
* position from the maverick boosted position contract.
* @param startIndex The starting index of the list to retrieve.
* @param endIndex The ending index of the list to retrieve.
* @return rewardsContract An array of IMaverickV2Reward contracts within the specified range.
*/
function nonBoostedPositionRewards(
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2Reward[] memory);
/**
* @notice Returns the number of reward contracts where the staking token
* is not a booste position that this factory has deployed.
*/
function nonBoostedPositionRewardsCount() external view returns (uint256 count);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IMaverickV2RewardVault {
error RewardVaultUnauthorizedAccount(address caller, address owner);
/**
* @notice This function allows the owner of the reward vault to withdraw a
* specified amount of staking tokens to a recipient address. If non-owner
* calls this function, it will revert.
* @param recipient The address to which the withdrawn staking tokens will be sent.
* @param amount The amount of staking tokens to withdraw.
*/
function withdraw(address recipient, uint256 amount) external;
/**
* @notice This function retrieves the address of the owner of the reward
* vault contract.
*/
function owner() external view returns (address);
/**
* @notice This function retrieves the address of the ERC20 token used for
* staking within the reward vault.
*/
function stakingToken() external view returns (IERC20);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC6372} from "@openzeppelin/contracts/interfaces/IERC6372.sol";
import {IHistoricalBalance} from "../votingescrowbase/IHistoricalBalance.sol";
interface IMaverickV2VotingEscrowBase is IVotes, IHistoricalBalance {
error VotingEscrowTransferNotSupported();
error VotingEscrowInvalidAddress(address);
error VotingEscrowInvalidAmount(uint256);
error VotingEscrowInvalidDuration(uint256 duration, uint256 minDuration, uint256 maxDuration);
error VotingEscrowInvalidEndTime(uint256 newEnd, uint256 oldEnd);
error VotingEscrowStakeStillLocked(uint256 currentTime, uint256 endTime);
error VotingEscrowStakeAlreadyRedeemed();
error VotingEscrowNotApprovedExtender(address account, address extender, uint256 lockupId);
error VotingEscrowIncentiveAlreadyClaimed(address account, uint256 batchIndex);
error VotingEscrowNoIncentivesToClaim(address account, uint256 batchIndex);
error VotingEscrowInvalidExtendIncentiveToken(IERC20 incentiveToken);
error VotingEscrowNoSupplyAtTimepoint();
error VotingEscrowIncentiveTimepointInFuture(uint256 timestamp, uint256 claimTimepoint);
event Stake(address indexed user, uint256 lockupId, Lockup);
event Unstake(address indexed user, uint256 lockupId, Lockup);
event ExtenderApproval(address staker, address extender, uint256 lockupId, bool newState);
event ClaimIncentiveBatch(uint256 batchIndex, address account, uint256 claimAmount);
event CreateNewIncentiveBatch(
address user,
uint256 amount,
uint256 timepoint,
uint256 stakeDuration,
IERC20 incentiveToken
);
struct Lockup {
uint128 amount;
uint128 end;
uint256 votes;
}
struct ClaimInformation {
bool timepointInPast;
bool hasClaimed;
uint128 claimAmount;
}
struct BatchInformation {
uint128 totalIncentives;
uint128 stakeDuration;
uint48 claimTimepoint;
IERC20 incentiveToken;
}
struct TokenIncentiveTotals {
uint128 totalIncentives;
uint128 claimedIncentives;
}
// solhint-disable-next-line func-name-mixedcase
function MIN_STAKE_DURATION() external returns (uint256 duration);
// solhint-disable-next-line func-name-mixedcase
function MAX_STAKE_DURATION() external returns (uint256 duration);
// solhint-disable-next-line func-name-mixedcase
function YEAR_BASE() external returns (uint256);
/**
* @notice This function retrieves the address of the ERC20 token used as the base token for staking and rewards.
* @return baseToken The address of the IERC20 base token contract.
*/
function baseToken() external returns (IERC20);
/**
* @notice This function retrieves the starting timestamp. This may be used
* for reward calculations or other time-based logic.
*/
function startTimestamp() external returns (uint256 timestamp);
/**
* @notice This function retrieves the details of a specific lockup for a given staker and lockup index.
* @param staker The address of the staker for which to retrieve the lockup details.
* @param index The index of the lockup within the staker's lockup history.
* @return lockup A Lockup struct containing details about the lockup (see struct definition for details).
*/
function getLockup(address staker, uint256 index) external view returns (Lockup memory lockup);
/**
* @notice This function retrieves the total number of lockups associated with a specific staker.
* @param staker The address of the staker for which to retrieve the lockup count.
* @return count The total number of lockups for the staker.
*/
function lockupCount(address staker) external view returns (uint256 count);
/**
* @notice This function simulates a lockup scenario, providing details about the resulting lockup structure for a specified amount and duration.
* @param amount The amount of tokens to be locked.
* @param duration The duration of the lockup period.
* @return lockup A Lockup struct containing details about the simulated lockup (see struct definition for details).
*/
function previewVotes(uint128 amount, uint256 duration) external view returns (Lockup memory lockup);
/**
* @notice This function grants approval for a designated extender contract to manage a specific lockup on behalf of the staker.
* @param extender The address of the extender contract to be approved.
* @param lockupId The ID of the lockup for which to grant approval.
*/
function approveExtender(address extender, uint256 lockupId) external;
/**
* @notice This function revokes approval previously granted to an extender contract for managing a specific lockup.
* @param extender The address of the extender contract whose approval is being revoked.
* @param lockupId The ID of the lockup for which to revoke approval.
*/
function revokeExtender(address extender, uint256 lockupId) external;
/**
* @notice This function checks whether a specific account has been approved by a staker to manage a particular lockup through an extender contract.
* @param account The address of the account to check for approval (may be the extender or another account).
* @param extender The address of the extender contract for which to check approval.
* @param lockupId The ID of the lockup to verify approval for.
* @return isApproved True if the account is approved for the lockup, False otherwise (bool).
*/
function isApprovedExtender(address account, address extender, uint256 lockupId) external view returns (bool);
/**
* @notice This function extends the lockup period for the caller (msg.sender) for a specified lockup ID, adding a new duration and amount.
* @param lockupId The ID of the lockup to be extended.
* @param duration The additional duration to extend the lockup by.
* @param amount The additional amount of tokens to be locked.
* @return newLockup A Lockup struct containing details about the newly extended lockup (see struct definition for details).
*/
function extendForSender(
uint256 lockupId,
uint256 duration,
uint128 amount
) external returns (Lockup memory newLockup);
/**
* @notice This function extends the lockup period for a specified account, adding a new duration and amount. The caller (msg.sender) must be authorized to manage the lockup through an extender contract.
* @param account The address of the account whose lockup is being extended.
* @param lockupId The ID of the lockup to be extended.
* @param duration The additional duration to extend the lockup by.
* @param amount The additional amount of tokens to be locked.
* @return newLockup A Lockup struct containing details about the newly extended lockup (see struct definition for details).
*/
function extendForAccount(
address account,
uint256 lockupId,
uint256 duration,
uint128 amount
) external returns (Lockup memory newLockup);
/**
* @notice This function merges multiple lockups associated with the caller
* (msg.sender) into a single new lockup.
* @param lockupIds An array containing the IDs of the lockups to be merged.
* @return newLockup A Lockup struct containing details about the newly merged lockup (see struct definition for details).
*/
function merge(uint256[] memory lockupIds) external returns (Lockup memory newLockup);
/**
* @notice This function unstakes the specified lockup ID for the caller (msg.sender), returning the details of the unstaked lockup.
* @param lockupId The ID of the lockup to be unstaked.
* @param to The address to which the unstaked tokens should be sent (optional, defaults to msg.sender).
* @return lockup A Lockup struct containing details about the unstaked lockup (see struct definition for details).
*/
function unstake(uint256 lockupId, address to) external returns (Lockup memory lockup);
/**
* @notice This function is a simplified version of `unstake` that automatically sends the unstaked tokens to the caller (msg.sender).
* @param lockupId The ID of the lockup to be unstaked.
* @return lockup A Lockup struct containing details about the unstaked lockup (see struct definition for details).
*/
function unstakeToSender(uint256 lockupId) external returns (Lockup memory lockup);
/**
* @notice This function stakes a specified amount of tokens for the caller
* (msg.sender) for a defined duration.
* @param amount The amount of tokens to be staked.
* @param duration The duration of the lockup period.
* @return lockup A Lockup struct containing details about the newly
* created lockup (see struct definition for details).
*/
function stakeToSender(uint128 amount, uint256 duration) external returns (Lockup memory lockup);
/**
* @notice This function stakes a specified amount of tokens for a defined
* duration, allowing the caller (msg.sender) to specify an optional
* recipient for the staked tokens.
* @param amount The amount of tokens to be staked.
* @param duration The duration of the lockup period.
* @param to The address to which the staked tokens will be credited (optional, defaults to msg.sender).
* @return lockup A Lockup struct containing details about the newly
* created lockup (see struct definition for details).
*/
function stake(uint128 amount, uint256 duration, address to) external returns (Lockup memory);
/**
* @notice This function retrieves the total incentive information for a specific ERC-20 token.
* @param token The address of the ERC20 token for which to retrieve incentive totals.
* @return totals A TokenIncentiveTotals struct containing details about
* the token's incentives (see struct definition for details).
*/
function incentiveTotals(IERC20 token) external view returns (TokenIncentiveTotals memory);
/**
* @notice This function retrieves the total number of created incentive batches.
* @return count The total number of incentive batches.
*/
function incentiveBatchCount() external view returns (uint256);
/**
* @notice This function retrieves claim information for a specific account and incentive batch index.
* @param account The address of the account for which to retrieve claim information.
* @param batchIndex The index of the incentive batch for which to retrieve
* claim information.
* @return claimInformation A ClaimInformation struct containing details about the
* account's claims for the specified batch (see struct definition for
* details).
* @return batchInformation A BatchInformation struct containing details about the
* specified batch (see struct definition for details).
*/
function claimAndBatchInformation(
address account,
uint256 batchIndex
) external view returns (ClaimInformation memory claimInformation, BatchInformation memory batchInformation);
/**
* @notice This function retrieves batch information for a incentive batch index.
* @param batchIndex The index of the incentive batch for which to retrieve
* claim information.
* @return info A BatchInformation struct containing details about the
* specified batch (see struct definition for details).
*/
function incentiveBatchInformation(uint256 batchIndex) external view returns (BatchInformation memory info);
/**
* @notice This function allows claiming rewards from a specific incentive
* batch while simultaneously extending a lockup with the claimed tokens.
* @param batchIndex The index of the incentive batch from which to claim rewards.
* @param lockupId The ID of the lockup to be extended with the claimed tokens.
* @return lockup A Lockup struct containing details about the updated
* lockup after extension (see struct definition for details).
* @return claimAmount The amount of tokens claimed from the incentive batch.
*/
function claimFromIncentiveBatchAndExtend(
uint256 batchIndex,
uint256 lockupId
) external returns (Lockup memory lockup, uint128 claimAmount);
/**
* @notice This function allows claiming rewards from a specific incentive
* batch, without extending any lockups.
* @param batchIndex The index of the incentive batch from which to claim rewards.
* @return lockup A Lockup struct containing details about the user's
* lockup that might have been affected by the claim (see struct definition
* for details).
* @return claimAmount The amount of tokens claimed from the incentive batch.
*/
function claimFromIncentiveBatch(uint256 batchIndex) external returns (Lockup memory lockup, uint128 claimAmount);
/**
* @notice This function creates a new incentive batch for a specified amount
* of incentive tokens, timepoint, stake duration, and associated ERC-20
* token. An incentive batch is a reward of incentives put up by the
* caller at a certain timepoint. The incentive batch is claimable by ve
* holders after the timepoint has passed. The ve holders will receive
* their incentive pro rata of their vote balance (`pastbalanceOf`) at that
* timepoint. The incentivizer can specify that users have to stake the
* resulting incentive for a given `stakeDuration` number of seconds.
* `stakeDuration` can either be zero, meaning that no staking is required
* on redemption, or can be a number between `MIN_STAKE_DURATION()` and
* `MAX_STAKE_DURATION()`.
* @param amount The total amount of incentive tokens to be distributed in the batch.
* @param timepoint The timepoint at which the incentive batch starts accruing rewards.
* @param stakeDuration The duration of the lockup period required to be
* eligible for the incentive batch rewards.
* @param incentiveToken The address of the ERC20 token used for the incentive rewards.
* @return index The index of the newly created incentive batch.
*/
function createIncentiveBatch(
uint128 amount,
uint48 timepoint,
uint128 stakeDuration,
IERC20 incentiveToken
) external returns (uint256 index);
}
interface IMaverickV2VotingEscrow is IMaverickV2VotingEscrowBase, IERC20Metadata, IERC6372 {}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IMaverickV2VotingEscrow} from "./IMaverickV2VotingEscrow.sol";
interface IMaverickV2VotingEscrowFactory {
error VotingEscrowTokenAlreadyExists(IERC20 baseToken, IMaverickV2VotingEscrow veToken);
event CreateVotingEscrow(IERC20 baseToken, IMaverickV2VotingEscrow veToken);
/**
* @notice This function retrieves the address of the legacy Maverick V1
* Voting Escrow (veMAV) token. The address will be zero for blockchains
* where this contract is deployed that do not have a legacy MAV contract
* deployed.
* @return legacyVeMav The address of the IERC20 legacy veMav token.
*/
function legacyVeMav() external view returns (IERC20);
/**
* @notice This function checks whether a provided IMaverickV2VotingEscrow
* contract address was created by this factory.
* @param veToken The address of the IMaverickV2VotingEscrow contract to be checked.
* @return isFactoryToken True if the veToken was created by this factory, False otherwise (bool).
*/
function isFactoryToken(IMaverickV2VotingEscrow veToken) external view returns (bool);
/**
* @notice This function creates a new Maverick V2 Voting Escrow (veToken)
* contract for a specified ERC20 base token.
* @dev Once the ve contract is created, it will call `name()` and
* `symbol()` on the `baseToken`. If those functions do not exist, the ve
* creation will revert.
* @param baseToken The address of the ERC-20 token to be used as the base token for the new veToken.
* @return veToken The address of the newly created IMaverickV2VotingEscrow contract.
*/
function createVotingEscrow(IERC20 baseToken) external returns (IMaverickV2VotingEscrow veToken);
/**
* @notice This function retrieves a paginated list of existing Maverick V2
* Voting Escrow (veToken) contracts within a specified index range.
* @param startIndex The starting index for the desired range of veTokens.
* @param endIndex The ending index for the desired range of veTokens.
* @return votingEscrows An array of IMaverickV2VotingEscrow addresses
* representing the veTokens within the specified range.
*/
function votingEscrows(
uint256 startIndex,
uint256 endIndex
) external view returns (IMaverickV2VotingEscrow[] memory votingEscrows);
/**
* @notice This function retrieves the total number of deployed Maverick V2
* Voting Escrow (veToken) contracts.
* @return count The total number of veTokens.
*/
function votingEscrowsCount() external view returns (uint256 count);
/**
* @notice This function retrieves the address of the existing Maverick V2
* Voting Escrow (veToken) contract associated with a specific ERC20 base
* token.
* @param baseToken The address of the ERC-20 base token for which to retrieve the veToken address.
* @return veToken The address of the IMaverickV2VotingEscrow contract
* associated with the base token, or the zero address if none exists.
*/
function veForBaseToken(IERC20 baseToken) external view returns (IMaverickV2VotingEscrow veToken);
/**
* @notice This function retrieves the default base token used for creating
* new voting escrow contracts. This state variable is only used
* temporarily when a new veToken is deployed.
* @return baseToken The address of the default ERC-20 base token.
*/
function baseTokenParameter() external returns (IERC20);
}
// 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 {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
interface INft is IERC721Enumerable {
/**
* @notice Check if an NFT exists for a given owner and index.
*/
function tokenOfOwnerByIndexExists(address owner, uint256 index) external view returns (bool);
/**
* @notice Return Id of the next token minted.
*/
function nextTokenId() external view returns (uint256 nextTokenId_);
/**
* @notice Check if the caller has access to a specific NFT by tokenId.
*/
function checkAuthorized(address spender, uint256 tokenId) external view returns (address owner);
/**
* @notice List of tokenIds by owner.
*/
function tokenIdsOfOwner(address owner) external view returns (uint256[] memory tokenIds);
/**
* @notice Get the token URI for a given tokenId.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
interface IRewardAccounting {
error InsufficientBalance(uint256 tokenId, uint256 currentBalance, uint256 value);
/**
* @notice Balance of stake for a given `tokenId` account.
*/
function stakeBalanceOf(uint256 tokenId) external view returns (uint256 balance);
/**
* @notice Sum of all balances across all tokenIds.
*/
function stakeTotalSupply() external view returns (uint256 supply);
}
// 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
// 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 {Math as OzMath} from "@openzeppelin/contracts/utils/math/Math.sol";
import {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from "./Constants.sol";
/**
* @notice Math functions.
*/
library Math {
/**
* @notice Returns the lesser of two values.
* @param x First uint256 value.
* @param y Second uint256 value.
*/
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly ("memory-safe") {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/**
* @notice Returns the lesser of two uint128 values.
* @param x First uint128 value.
* @param y Second uint128 value.
*/
function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {
assembly ("memory-safe") {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/**
* @notice Returns the lesser of two int256 values.
* @param x First int256 value.
* @param y Second int256 value.
*/
function min(int256 x, int256 y) internal pure returns (int256 z) {
assembly ("memory-safe") {
z := xor(x, mul(xor(x, y), slt(y, x)))
}
}
/**
* @notice Returns the greater of two uint256 values.
* @param x First uint256 value.
* @param y Second uint256 value.
*/
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly ("memory-safe") {
z := xor(x, mul(xor(x, y), gt(y, x)))
}
}
/**
* @notice Returns the greater of two int256 values.
* @param x First int256 value.
* @param y Second int256 value.
*/
function max(int256 x, int256 y) internal pure returns (int256 z) {
assembly ("memory-safe") {
z := xor(x, mul(xor(x, y), sgt(y, x)))
}
}
/**
* @notice Returns the greater of two uint128 values.
* @param x First uint128 value.
* @param y Second uint128 value.
*/
function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {
assembly ("memory-safe") {
z := xor(x, mul(xor(x, y), gt(y, x)))
}
}
/**
* @notice Thresholds a value to be within the specified bounds.
* @param value The value to bound.
* @param lowerLimit The minimum allowable value.
* @param upperLimit The maximum allowable value.
*/
function boundValue(
uint256 value,
uint256 lowerLimit,
uint256 upperLimit
) internal pure returns (uint256 outputValue) {
outputValue = min(max(value, lowerLimit), upperLimit);
}
/**
* @notice Returns the difference between two uint128 values or zero if the result would be negative.
* @param x The minuend.
* @param y The subtrahend.
*/
function clip128(uint128 x, uint128 y) internal pure returns (uint128) {
unchecked {
return x < y ? 0 : x - y;
}
}
/**
* @notice Returns the difference between two uint256 values or zero if the result would be negative.
* @param x The minuend.
* @param y The subtrahend.
*/
function clip(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
return x < y ? 0 : x - y;
}
}
/**
* @notice Divides one uint256 by another, rounding down to the nearest
* integer.
* @param x The dividend.
* @param y The divisor.
*/
function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivFloor(x, ONE, y);
}
/**
* @notice Divides one uint256 by another, rounding up to the nearest integer.
* @param x The dividend.
* @param y The divisor.
*/
function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivCeil(x, ONE, y);
}
/**
* @notice Multiplies two uint256 values and then divides by ONE, rounding down.
* @param x The multiplicand.
* @param y The multiplier.
*/
function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {
return OzMath.mulDiv(x, y, ONE);
}
/**
* @notice Multiplies two uint256 values and then divides by ONE, rounding up.
* @param x The multiplicand.
* @param y The multiplier.
*/
function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivCeil(x, y, ONE);
}
/**
* @notice Calculates the multiplicative inverse of a uint256, rounding down.
* @param x The value to invert.
*/
function invFloor(uint256 x) internal pure returns (uint256) {
unchecked {
return ONE_SQUARED / x;
}
}
/**
* @notice Calculates the multiplicative inverse of a uint256, rounding up.
* @param denominator The value to invert.
*/
function invCeil(uint256 denominator) internal pure returns (uint256 z) {
assembly ("memory-safe") {
// divide z - 1 by the denominator and add 1.
z := add(div(sub(ONE_SQUARED, 1), denominator), 1)
}
}
/**
* @notice Multiplies two uint256 values and divides by a third, rounding down.
* @param x The multiplicand.
* @param y The multiplier.
* @param k The divisor.
*/
function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {
result = OzMath.mulDiv(x, y, max(1, k));
}
/**
* @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.
* @param x The multiplicand.
* @param y The multiplier.
* @param k The divisor.
*/
function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {
result = mulDivFloor(x, y, k);
if (mulmod(x, y, max(1, k)) != 0) result = result + 1;
}
/**
* @notice Multiplies two uint256 values and divides by a third, rounding
* down. Will revert if `x * y` is larger than `type(uint256).max`.
* @param x The first operand for multiplication.
* @param y The second operand for multiplication.
* @param denominator The divisor after multiplication.
*/
function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {
assembly ("memory-safe") {
// Store x * y in z for now.
z := mul(x, y)
if iszero(denominator) {
denominator := 1
}
if iszero(or(iszero(x), eq(div(z, x), y))) {
revert(0, 0)
}
// Divide z by the denominator.
z := div(z, denominator)
}
}
/**
* @notice Multiplies two uint256 values and divides by a third, rounding
* up. Will revert if `x * y` is larger than `type(uint256).max`.
* @param x The first operand for multiplication.
* @param y The second operand for multiplication.
* @param denominator The divisor after multiplication.
*/
function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {
assembly ("memory-safe") {
// Store x * y in z for now.
z := mul(x, y)
if iszero(denominator) {
denominator := 1
}
if iszero(or(iszero(x), eq(div(z, x), y))) {
revert(0, 0)
}
// First, divide z - 1 by the denominator and add 1.
// We allow z - 1 to underflow if z is 0, because we multiply the
// end result by 0 if z is zero, ensuring we return 0 if z is zero.
z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
}
}
/**
* @notice Multiplies a uint256 by another and divides by a constant,
* rounding down. Will revert if `x * y` is larger than
* `type(uint256).max`.
* @param x The multiplicand.
* @param y The multiplier.
*/
function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, ONE);
}
/**
* @notice Divides a uint256 by another, rounding down the result. Will
* revert if `x * 1e18` is larger than `type(uint256).max`.
* @param x The dividend.
* @param y The divisor.
*/
function divDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, ONE, y);
}
/**
* @notice Divides a uint256 by another, rounding up the result. Will
* revert if `x * 1e18` is larger than `type(uint256).max`.
* @param x The dividend.
* @param y The divisor.
*/
function divUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, ONE, y);
}
/**
* @notice Scales a number based on a difference in decimals from a default.
* @param decimals The new decimal precision.
*/
function scale(uint8 decimals) internal pure returns (uint256) {
unchecked {
if (decimals == DEFAULT_DECIMALS) {
return DEFAULT_SCALE;
} else {
return 10 ** (DEFAULT_DECIMALS - decimals);
}
}
}
/**
* @notice Adjusts a scaled amount to the token decimal scale.
* @param amount The scaled amount.
* @param scaleFactor The scaling factor to adjust by.
* @param ceil Whether to round up (true) or down (false).
*/
function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {
unchecked {
if (scaleFactor == DEFAULT_SCALE || amount == 0) {
return amount;
} else {
if (!ceil) return amount / scaleFactor;
assembly ("memory-safe") {
z := add(div(sub(amount, 1), scaleFactor), 1)
}
}
}
}
/**
* @notice Adjusts a token amount to the D18 AMM scale.
* @param amount The amount in token scale.
* @param scaleFactor The scale factor for adjustment.
*/
function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {
if (scaleFactor == DEFAULT_SCALE) {
return amount;
} else {
return amount * scaleFactor;
}
}
/**
* @notice Returns the absolute value of a signed 32-bit integer.
* @param x The integer to take the absolute value of.
*/
function abs32(int32 x) internal pure returns (uint32) {
unchecked {
return uint32(x < 0 ? -x : x);
}
}
/**
* @notice Returns the absolute value of a signed 256-bit integer.
* @param x The integer to take the absolute value of.
*/
function abs(int256 x) internal pure returns (uint256) {
unchecked {
return uint256(x < 0 ? -x : x);
}
}
/**
* @notice Calculates the integer square root of a uint256 rounded down.
* @param x The number to take the square root of.
*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
// from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol
assembly ("memory-safe") {
let y := x
z := 181
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
z := shr(18, mul(z, add(y, 65536)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := sub(z, lt(div(x, z), z))
}
}
/**
* @notice Computes the floor of a D8-scaled number as an int32, ignoring
* potential overflow in the cast.
* @param val The D8-scaled number.
*/
function floorD8Unchecked(int256 val) internal pure returns (int32) {
int32 val32;
bool check;
unchecked {
val32 = int32(val / INT_ONE_D8);
check = (val < 0 && val % INT_ONE_D8 != 0);
}
return check ? val32 - 1 : val32;
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.25;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {SafeCast as Cast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {Math as OzMath} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Math} from "@maverick/v2-common/contracts/libraries/Math.sol";
import {Multicall} from "@maverick/v2-common/contracts/base/Multicall.sol";
import {IMaverickV2Reward} from "./interfaces/IMaverickV2Reward.sol";
import {IMaverickV2IncentiveMatcher} from "./interfaces/IMaverickV2IncentiveMatcher.sol";
import {IMaverickV2IncentiveMatcherCaller} from "./interfaces/IMaverickV2IncentiveMatcherCaller.sol";
import {IMaverickV2VotingEscrow} from "./interfaces/IMaverickV2VotingEscrow.sol";
import {IMaverickV2IncentiveMatcherFactory} from "./interfaces/IMaverickV2IncentiveMatcherFactory.sol";
import {IMaverickV2RewardFactory} from "./interfaces/IMaverickV2RewardFactory.sol";
/**
* @notice IncentiveMatcher contract corresponds to a ve token and manages
* incentive matching for incentives related to that ve token. This contract
* allows protocols to provide matching incentives to Maverick Reward Contracts
* and allows ve holders to vote their token to increase the match in a Reward
* Contract. Reward contracts are typically staked with a Boosted Position
* (BP) token, but the Reward contracts can be staked with any ERC-20 token.
*
* IncentiveMatcher has a concept of a matching epoch and the following actors:
*
* - BP incentive adder
* - Matching budget adder
* - Voter
*
* @notice The lifecycle of an epoch is as follows:
*
* - Anytime before or during an epoch, any party can permissionlessly add a
* matching and/or voting incentive budget to an epoch. These incentives will
* boost incentives added to any BPs during the epoch.
* - During the epoch any party can permissionlessly add incentives to BPs.
* These incentives are eligible to be boosted through matching and voting.
* - During the voting portion of the epoch, any ve holder can cast their ve
* vote for eligible BPs.
* - At the end of the epoch, there is a vetoing period where any user who
* provided matching incentive budget can choose to veto a BP from being
* matched by their portion of the matching budget.
* - At the end of the vetoing period, the matching rewards are eligible for
* distribution. Any user can permissionlessly call `distribute` for a given
* BP and epoch. This call will compute the matching boost for the BP and then
* send the BP reward contract the matching amount, which will in turn
* distribute the reward to the BP LPs.
*
* @notice Budget matchers can choose to match both the reward contract's base
* token as well as other "non-base tokens" the rewards contract supports. For
* non-base tokens, the matcher who added the budget will need to specify a
* rate or "multiplier" that converts a given amount of non-base tokens to an
* amount of base tokens. During the veto period, any user can call a function
* to apply this match to the non-base token incentives thereby ensure those
* incentives are matched.
*/
contract MaverickV2IncentiveMatcher is IMaverickV2IncentiveMatcher, ReentrancyGuard, Multicall {
using Cast for uint256;
using SafeERC20 for IERC20;
/// @inheritdoc IMaverickV2IncentiveMatcher
uint256 public constant EPOCH_PERIOD = 14 days;
/// @inheritdoc IMaverickV2IncentiveMatcher
uint256 public constant PRE_VOTE_PERIOD = 7 days;
/// @inheritdoc IMaverickV2IncentiveMatcher
uint256 public constant VETO_PERIOD = 2 days;
/// @inheritdoc IMaverickV2IncentiveMatcher
uint256 public constant NOTIFY_PERIOD = 14 days;
/// @inheritdoc IMaverickV2IncentiveMatcher
IERC20 public immutable baseToken;
/// @inheritdoc IMaverickV2IncentiveMatcher
IMaverickV2RewardFactory public immutable rewardFactory;
/// @inheritdoc IMaverickV2IncentiveMatcher
IMaverickV2VotingEscrow public immutable veToken;
/// @inheritdoc IMaverickV2IncentiveMatcher
IMaverickV2IncentiveMatcherCaller public immutable permissionedCaller;
// checkpoints indexed by epoch start: time % EPOCH_PERIOD
mapping(uint256 epoch => CheckpointData) private checkpoints;
// data per epoch
struct CheckpointData {
// accumulator for matchbudget of this epoch
uint128 matchBudget;
// accumulator for votebudget of this epoch
uint128 voteBudget;
// totals for vote product, votes, and incentives added
EpochInformation dataTotals;
// per contract data for vote product, votes, and incentives added
mapping(IMaverickV2Reward => EpochInformation) dataByReward;
// amount that each matcher has sent as budget for an epoch and tracking of veto deductions
mapping(address matcher => MatcherData) matcherData;
// array of rewards active that have external incentives this epoch
IMaverickV2Reward[] activeRewards;
// array of addresses that have provided matching budget
address[] matchers;
// tracks whether a matcher hasDistributed and hasVetoed this epoch
mapping(address matcher => mapping(IMaverickV2Reward reward => MatchRewardData)) matchReward;
// tracks whether user has voted this epoch
mapping(address voter => bool) hasVoted;
// tracks non-base token amounts
mapping(IERC20 token => mapping(IMaverickV2Reward reward => uint128)) nonBaseTokenExternalIncentive;
}
constructor() {
(baseToken, veToken, rewardFactory) = IMaverickV2IncentiveMatcherFactory(msg.sender)
.incentiveMatcherParameters();
permissionedCaller = IMaverickV2IncentiveMatcherFactory(msg.sender).permissionedCaller();
}
/////////////////////////////////////
/// Epoch Checkers and Helpers
/////////////////////////////////////
modifier checkEpoch(uint256 epoch) {
if (!isEpoch(epoch)) revert IncentiveMatcherInvalidEpoch(epoch);
_;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function checkpointMatcherBudget(
uint256 epoch,
address matcher
) public view checkEpoch(epoch) returns (uint128 matchBudget, uint128 voteBudget) {
CheckpointData storage checkpoint = checkpoints[epoch];
MatcherData storage matcherData = checkpoint.matcherData[matcher];
(matchBudget, voteBudget) = (matcherData.matchBudget, matcherData.voteBudget);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function checkpointMatcherData(
uint256 epoch,
address matcher
) public view checkEpoch(epoch) returns (MatcherData memory matcherData) {
CheckpointData storage checkpoint = checkpoints[epoch];
matcherData = checkpoint.matcherData[matcher];
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function checkpointRewardData(
uint256 epoch,
IMaverickV2Reward rewardContract
) public view checkEpoch(epoch) returns (RewardData memory rewardData) {
rewardData.rewardInformation = checkpoints[epoch].dataByReward[rewardContract];
rewardData.rewardContract = rewardContract;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function checkpointNonBaseIncentiveInfo(
uint256 epoch,
IERC20[] memory tokens,
IMaverickV2Reward[] memory rewardContracts
) public view checkEpoch(epoch) returns (NonBaseIncentives[] memory nonBaseIncentives) {
CheckpointData storage checkpoint = checkpoints[epoch];
nonBaseIncentives = new NonBaseIncentives[](rewardContracts.length * tokens.length);
uint256 count;
for (uint256 t; t < tokens.length; t++) {
for (uint256 r; r < rewardContracts.length; r++) {
uint256 amount = checkpoint.nonBaseTokenExternalIncentive[tokens[t]][rewardContracts[r]];
if (amount != 0) {
nonBaseIncentives[count] = NonBaseIncentives(tokens[t], rewardContracts[r], amount);
count++;
}
}
}
assembly ("memory-safe") {
mstore(nonBaseIncentives, count)
}
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function activeRewardsCount(uint256 epoch) public view checkEpoch(epoch) returns (uint256 count) {
CheckpointData storage checkpoint = checkpoints[epoch];
count = checkpoint.activeRewards.length;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function activeRewards(
uint256 epoch,
uint256 startIndex,
uint256 endIndex
) public view checkEpoch(epoch) returns (RewardData[] memory returnElements) {
CheckpointData storage checkpoint = checkpoints[epoch];
endIndex = Math.min(checkpoint.activeRewards.length, endIndex);
returnElements = new RewardData[](endIndex - startIndex);
for (uint256 i = startIndex; i < endIndex; i++) {
returnElements[i - startIndex] = checkpointRewardData(epoch, checkpoint.activeRewards[i]);
}
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function matchersCount(uint256 epoch) public view checkEpoch(epoch) returns (uint256 count) {
CheckpointData storage checkpoint = checkpoints[epoch];
count = checkpoint.matchers.length;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function matchers(
uint256 epoch,
uint256 startIndex,
uint256 endIndex
) public view checkEpoch(epoch) returns (address[] memory returnElements, MatcherData[] memory matcherData) {
CheckpointData storage checkpoint = checkpoints[epoch];
endIndex = Math.min(checkpoint.matchers.length, endIndex);
returnElements = new address[](endIndex - startIndex);
matcherData = new MatcherData[](endIndex - startIndex);
for (uint256 i = startIndex; i < endIndex; i++) {
returnElements[i - startIndex] = checkpoint.matchers[i];
matcherData[i - startIndex] = checkpointMatcherData(epoch, returnElements[i - startIndex]);
}
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function checkpointData(
uint256 epoch
)
public
view
checkEpoch(epoch)
returns (uint128 matchBudget, uint128 voteBudget, EpochInformation memory epochTotals)
{
CheckpointData storage checkpoint = checkpoints[epoch];
(matchBudget, voteBudget, epochTotals) = (checkpoint.matchBudget, checkpoint.voteBudget, checkpoint.dataTotals);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function hasVoted(address user, uint256 epoch) public view checkEpoch(epoch) returns (bool) {
CheckpointData storage checkpoint = checkpoints[epoch];
return checkpoint.hasVoted[user];
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function hasVetoed(address matcher, IMaverickV2Reward rewardContract, uint256 epoch) public view returns (bool) {
CheckpointData storage checkpoint = checkpoints[epoch];
MatchRewardData storage matchReward = checkpoint.matchReward[matcher][rewardContract];
return matchReward.hasVetoed;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function hasDistributed(
address matcher,
IMaverickV2Reward rewardContract,
uint256 epoch
) public view returns (bool) {
CheckpointData storage checkpoint = checkpoints[epoch];
MatchRewardData storage matchReward = checkpoint.matchReward[matcher][rewardContract];
return matchReward.hasDistributed;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function isEpoch(uint256 epoch) public pure returns (bool _isEpoch) {
_isEpoch = epoch % EPOCH_PERIOD == 0;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function epochIsOver(uint256 epoch) public view returns (bool isOver) {
isOver = block.timestamp >= epochEnd(epoch);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function vetoingIsActive(uint256 epoch) public view returns (bool isActive) {
// veto period is `epoch + EPOCH_PERIOD` to `epoch + EPOCH_PERIOD +
// VETO_PERIOD
isActive = epochIsOver(epoch) && block.timestamp < vetoingEnd(epoch);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function votingIsActive(uint256 epoch) public view returns (bool isActive) {
// vote period is `epoch + PRE_VOTE_PERIOD` to `epoch + EPOCH_PERIOD
isActive = block.timestamp >= votingStart(epoch) && block.timestamp < epochEnd(epoch);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function vetoingIsOver(uint256 epoch) public view returns (bool isOver) {
isOver = block.timestamp >= vetoingEnd(epoch);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function votingStart(uint256 epoch) public pure returns (uint256 start) {
start = epoch + PRE_VOTE_PERIOD;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function epochEnd(uint256 epoch) public pure returns (uint256 end) {
end = epoch + EPOCH_PERIOD;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function vetoingEnd(uint256 epoch) public pure returns (uint256 end) {
end = epochEnd(epoch) + VETO_PERIOD;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function currentEpoch() public view returns (uint256 epoch) {
epoch = (block.timestamp / EPOCH_PERIOD) * EPOCH_PERIOD;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function lastEpoch() public view returns (uint256 epoch) {
epoch = currentEpoch() - EPOCH_PERIOD;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function rewardHasVeCheck(IMaverickV2Reward rewardContract) public view {
uint8 index = rewardContract.tokenIndex(baseToken);
// only need to check address zero as reward contract is a factory
// contract and the factory ensures that any non-zero ve contract is
// the ve contract for the base token
if (address(rewardContract.veTokenByIndex(index)) == address(0))
revert IncentiveMatcherRewardDoesNotHaveVeStakingOption();
}
/////////////////////////////////////
/// User Actions
/////////////////////////////////////
function _addBudget(uint128 matchBudget, uint128 voteBudget, uint256 epoch) private {
uint256 totalMatch = matchBudget + voteBudget;
if (totalMatch == 0) revert IncentiveMatcherZeroAmount();
if (epochIsOver(epoch)) revert IncentiveMatcherEpochHasEnded(epoch);
CheckpointData storage checkpoint = checkpoints[epoch];
// track budget totals
checkpoint.matchBudget += matchBudget;
checkpoint.voteBudget += voteBudget;
MatcherData storage matcherData = checkpoint.matcherData[msg.sender];
// add matcher to list
if (matcherData.matchBudget == 0 && matcherData.voteBudget == 0) checkpoint.matchers.push(msg.sender);
// increment budget for this matcher
matcherData.matchBudget += matchBudget;
matcherData.voteBudget += voteBudget;
emit BudgetAdded(epoch, msg.sender, matchBudget, voteBudget);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function addMatchingBudget(
uint128 matchBudget,
uint128 voteBudget,
uint256 epoch
) public checkEpoch(epoch) nonReentrant {
_addBudget(matchBudget, voteBudget, epoch);
uint256 totalMatch = matchBudget + voteBudget;
baseToken.safeTransferFrom(msg.sender, address(this), totalMatch);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function setTokenMultipliers(
TokenMultiplier[] memory _tokenMultipliers,
uint256 epoch
) public checkEpoch(epoch) nonReentrant {
if (epochIsOver(epoch)) revert IncentiveMatcherEpochHasEnded(epoch);
MatcherData storage matchData = checkpoints[epoch].matcherData[msg.sender];
if (matchData.tokenMultipliers.length != 0) revert IncentiveMatcherTokenMultipliersAlreadySet(epoch);
for (uint256 k; k < _tokenMultipliers.length; k++) {
matchData.tokenMultipliers.push(_tokenMultipliers[k]);
}
emit SetTokenMultipliers(epoch, msg.sender, _tokenMultipliers);
}
function _addBaseIncentives(IMaverickV2Reward rewardContract, uint128 amount) internal {
// check reward is factory
if (!rewardFactory.isFactoryContract(rewardContract))
revert IncentiveMatcherNotRewardFactoryContract(rewardContract);
rewardHasVeCheck(rewardContract);
if (amount == 0) revert IncentiveMatcherZeroAmount();
uint256 epoch = currentEpoch();
CheckpointData storage checkpoint = checkpoints[epoch];
EpochInformation storage rewardTotals = checkpoint.dataTotals;
EpochInformation storage rewardValues = checkpoint.dataByReward[rewardContract];
if (!rewardValues.tracked) checkpoint.activeRewards.push(rewardContract);
rewardValues.tracked = true;
uint128 existing = rewardValues.externalIncentives;
rewardValues.externalIncentives = existing + amount;
rewardTotals.externalIncentives += amount;
_updateVoteProducts(rewardValues, rewardTotals);
emit IncentiveAdded(epoch, rewardContract, amount);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function addIncentives(
IMaverickV2Reward rewardContract,
uint128 amount,
uint256 _duration
) public nonReentrant returns (uint256 duration) {
_addBaseIncentives(rewardContract, amount);
baseToken.safeTransferFrom(msg.sender, address(rewardContract), amount);
duration = rewardContract.notifyRewardAmount(baseToken, _duration);
}
function _addNonBaseIncentives(IMaverickV2Reward rewardContract, uint128 amount, IERC20 token) internal {
// check reward is factory
if (!rewardFactory.isFactoryContract(rewardContract))
revert IncentiveMatcherNotRewardFactoryContract(rewardContract);
rewardHasVeCheck(rewardContract);
if (amount == 0) revert IncentiveMatcherZeroAmount();
uint256 epoch = currentEpoch();
CheckpointData storage checkpoint = checkpoints[epoch];
EpochInformation storage rewardValues = checkpoint.dataByReward[rewardContract];
if (!rewardValues.tracked) checkpoint.activeRewards.push(rewardContract);
rewardValues.tracked = true;
uint128 existing = checkpoint.nonBaseTokenExternalIncentive[token][rewardContract];
checkpoint.nonBaseTokenExternalIncentive[token][rewardContract] = existing + amount;
emit NonBaseTokenIncentiveAdded(epoch, rewardContract, amount, token);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function addNonBaseTokenIncentives(
IMaverickV2Reward rewardContract,
uint128 amount,
uint256 _duration,
IERC20 token
) public nonReentrant returns (uint256 duration) {
_addNonBaseIncentives(rewardContract, amount, token);
token.safeTransferFrom(msg.sender, address(rewardContract), amount);
// reverts if token is not valid for reward contract
duration = rewardContract.notifyRewardAmount(token, _duration);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function permissionedAddIncentives(IMaverickV2Reward rewardContract, uint128 amount, IERC20 token) public {
if (msg.sender != address(permissionedCaller))
revert IncentiveMatcherNotPermissionedCaller(msg.sender, address(permissionedCaller));
if (token == baseToken) {
_addBaseIncentives(rewardContract, amount);
} else {
_addNonBaseIncentives(rewardContract, amount, token);
}
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function applyNonBaseTokenMatch(
IMaverickV2Reward rewardContract,
address matcher,
uint256 tokenMultiplierIndex,
uint256 epoch
) public nonReentrant checkEpoch(epoch) returns (uint128 adjustedAmount) {
if (!epochIsOver(epoch)) revert IncentiveMatcherEpochHasNotEnded(block.timestamp, epochEnd(epoch));
CheckpointData storage checkpoint = checkpoints[epoch];
MatchRewardData storage matchReward = checkpoint.matchReward[matcher][rewardContract];
if (matchReward.hasVetoed) revert IncentiveMatcherMatcherAlreadyVetoed(matcher, rewardContract, epoch);
if (matchReward.hasDistributed) revert IncentiveMatcherEpochAlreadyDistributed(epoch, rewardContract);
// get multiplier
TokenMultiplier memory tokenMultiplier = checkpoint.matcherData[matcher].tokenMultipliers[tokenMultiplierIndex];
// zero multiplier indicates that this matcher is not matching this token
if (tokenMultiplier.multiplier == 0)
revert IncentiveMatcherRewardInvalidIncentiveToken(epoch, matcher, tokenMultiplier.token);
// retrieve the nonBase amount and scale it by the multiplier
uint128 nonBaseAmount = checkpoint.nonBaseTokenExternalIncentive[tokenMultiplier.token][rewardContract];
// zero amount indicates there was no incentives from this token added
if (nonBaseAmount == 0) revert IncentiveMatcherZeroAmount();
MatcherData storage matcherData = checkpoint.matcherData[matcher];
if (matcherData.voteBudget == 0 && matcherData.matchBudget == 0)
revert IncentiveMatcherMatcherHasNoBudget(matcher, epoch);
adjustedAmount = Math.mulDown(nonBaseAmount, tokenMultiplier.multiplier).toUint128();
{
// update incentive amounts with credit for the non-base token
EpochInformation storage rewardValues = checkpoint.dataByReward[rewardContract];
EpochInformation storage rewardTotals = checkpoint.dataTotals;
// matcherData has totals
matcherData.nonBaseExternalIncentives += adjustedAmount;
// matchReward has per-reward amounts
matchReward.nonBaseExternalIncentives += adjustedAmount;
// votes * (base incentive + non base incentives)
uint128 voteProduct_ = Math
.mulDown(rewardValues.externalIncentives + matchReward.nonBaseExternalIncentives, rewardValues.votes)
.toUint128();
// subtract old value and add new value
matcherData.nonBaseVoteProduct =
Math.max128(rewardTotals.voteProduct, matcherData.nonBaseVoteProduct) -
Math.max128(matchReward.nonBaseVoteProduct, rewardValues.voteProduct) +
voteProduct_;
// matchReward has reward/matcher accounting
matchReward.nonBaseVoteProduct = voteProduct_;
}
emit NonBaseTokenIncentiveApplied(
epoch,
matcher,
rewardContract,
nonBaseAmount,
adjustedAmount,
tokenMultiplierIndex,
tokenMultiplier.token
);
}
function _inVetoPeriodCheck(uint256 epoch) internal view {
// check vote period is over
if (!vetoingIsActive(epoch)) {
revert IncentiveMatcherVetoPeriodNotActive(block.timestamp, epochEnd(epoch), vetoingEnd(epoch));
}
}
function _inVotePeriodCheck(uint256 epoch) internal view {
if (!votingIsActive(epoch)) {
revert IncentiveMatcherVotePeriodNotActive(block.timestamp, votingStart(epoch), epochEnd(epoch));
}
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function vote(IMaverickV2Reward[] memory voteTargets, uint256[] memory weights) external nonReentrant {
uint256 epoch = currentEpoch();
_inVotePeriodCheck(epoch);
CheckpointData storage checkpoint = checkpoints[epoch];
if (checkpoint.hasVoted[msg.sender]) revert IncentiveMatcherSenderHasAlreadyVoted();
checkpoint.hasVoted[msg.sender] = 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 as beginning of vote
// period
uint256 startTimestamp = votingStart(epoch);
votingPower = Math.divFloor(
veToken.getPastVotes(msg.sender, startTimestamp),
veToken.getPastTotalSupply(startTimestamp)
);
if (votingPower == 0) revert IncentiveMatcherSenderHasNoVotingPower(msg.sender, votingStart(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
IMaverickV2Reward lastReward = IMaverickV2Reward(address(0));
for (uint256 i; i < weights.length; i++) {
IMaverickV2Reward rewardContract = voteTargets[i];
// ensure addresses are unique and sorted
if (rewardContract <= lastReward) revert IncentiveMatcherInvalidTargetOrder(lastReward, rewardContract);
lastReward = rewardContract;
// no need to check if factory reward because we check that in the
// addIncentives call; a user can vote for a non-factory address
// and that vote will essentially be a wasted vote. users can view
// the elegible rewards contracts with a view call before they vote
// to enusre they are voting on a active rewardcontract
// translate relative vote weights into votes
uint128 _vote = OzMath.mulDiv(weights[i], votingPower, totalVoteWeight).toUint128();
if (_vote == 0) revert IncentiveMatcherInvalidVote(rewardContract, weights[i], totalVoteWeight, _vote);
EpochInformation storage rewardValues = checkpoint.dataByReward[rewardContract];
EpochInformation storage rewardTotals = checkpoint.dataTotals;
rewardValues.votes += _vote;
rewardTotals.votes += _vote;
_updateVoteProducts(rewardValues, rewardTotals);
emit Vote(epoch, msg.sender, rewardContract, _vote);
}
}
/**
* @notice The vote budget allocation is distributed pro rata of a "weight"
* that is assigned to each reward contract. The weight is the product of
* the incentive addition and vote, or `W_i = E_i * V_i`, where E_i is the
* external incentives for the ith contract, V_i is the vote for the ith
* contract.
*
* @notice As either the external incentives or vote amounts change, this
* function must be called in order to track both the sum weight,
* sum W_i, and the individual weights, W_i. To do this efficiently, this
* matcher contract tracks both the sum weight and the individual
* weight value for each contract. When there is an update to either the
* vote or external incentives, this function subtracts the current
* contract weight value from the sum and adds the new weight value.
*/
function _updateVoteProducts(
EpochInformation storage rewardValues,
EpochInformation storage rewardTotals
) internal {
// if the vote elements in the pro rata computation are zero, this is a no-op function
if (rewardTotals.externalIncentives == 0 || rewardTotals.votes == 0 || rewardValues.votes == 0) return;
// need to track pro rata incentive product and the sum product.
uint128 voteProduct_ = Math.mulDown(rewardValues.externalIncentives, rewardValues.votes).toUint128();
rewardTotals.voteProduct = rewardTotals.voteProduct - rewardValues.voteProduct + voteProduct_;
rewardValues.voteProduct = voteProduct_;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function veto(
IMaverickV2Reward rewardContract
) public nonReentrant returns (uint128 voteProductDeduction, uint128 externalIncentivesDeduction) {
uint256 epoch = lastEpoch();
_inVetoPeriodCheck(epoch);
CheckpointData storage checkpoint = checkpoints[epoch];
MatchRewardData storage matchReward = checkpoint.matchReward[msg.sender][rewardContract];
if (matchReward.hasVetoed) revert IncentiveMatcherMatcherAlreadyVetoed(msg.sender, rewardContract, epoch);
matchReward.hasVetoed = true;
MatcherData storage matcherData = checkpoint.matcherData[msg.sender];
if (matcherData.voteBudget == 0 && matcherData.matchBudget == 0)
revert IncentiveMatcherMatcherHasNoBudget(msg.sender, epoch);
EpochInformation storage rewardValues = checkpoint.dataByReward[rewardContract];
// if there is non-base token incentives, the non-base vote product
// amount supercedes the base token voteProduct accounting, if not,
// then the non-base value will be zero, so the max is used to decided
// which value to apply as the deduction.
voteProductDeduction = Math.max128(rewardValues.voteProduct, matchReward.nonBaseVoteProduct);
matcherData.voteProductDeduction += voteProductDeduction;
externalIncentivesDeduction = rewardValues.externalIncentives + matchReward.nonBaseExternalIncentives;
matcherData.externalIncentivesDeduction += externalIncentivesDeduction;
emit Veto(epoch, msg.sender, rewardContract, voteProductDeduction, externalIncentivesDeduction);
}
function _checkVetoPeriodEnded(uint256 epoch) internal view {
if (!vetoingIsOver(epoch)) revert IncentiveMatcherVetoPeriodHasNotEnded(block.timestamp, vetoingEnd(epoch));
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function distribute(
IMaverickV2Reward rewardContract,
address matcher,
uint256 epoch
) public checkEpoch(epoch) nonReentrant returns (uint256 totalMatch, uint256 incentiveMatch, uint256 voteMatch) {
_checkVetoPeriodEnded(epoch);
CheckpointData storage checkpoint = checkpoints[epoch];
MatchRewardData storage matchReward = checkpoint.matchReward[matcher][rewardContract];
if (matchReward.hasDistributed) revert IncentiveMatcherEpochAlreadyDistributed(epoch, rewardContract);
matchReward.hasDistributed = true;
if (!matchReward.hasVetoed) {
// only need to compute matches for non-vetoed contracts
EpochInformation storage rewardTotals = checkpoint.dataTotals;
EpochInformation storage rewardValues = checkpoint.dataByReward[rewardContract];
MatcherData storage matcherData = checkpoint.matcherData[matcher];
// subtract the vetoed amount of incentives
uint256 adjustedRewardIncentives = (rewardTotals.externalIncentives +
matcherData.nonBaseExternalIncentives) - matcherData.externalIncentivesDeduction;
if (adjustedRewardIncentives > 0) {
uint256 externalIncentives = rewardValues.externalIncentives + matchReward.nonBaseExternalIncentives;
// compute how much this reward gets matched;
// need to check if we have enough for full match or if we have to pro rate
uint256 matchBudget = matcherData.matchBudget;
if (matchBudget >= adjustedRewardIncentives) {
// straight match
incentiveMatch = externalIncentives;
} else {
// pro rate the match,
incentiveMatch = OzMath.mulDiv(matchBudget, externalIncentives, adjustedRewardIncentives);
}
}
// subtract the vote deduction. if there are non-base incentives,
// then the matcherData value is non-zero and includes the base
// token voteProduct amount. in this case, use the max of the two
// values.
uint256 adjustedVoteProduct = Math.max(rewardTotals.voteProduct, matcherData.nonBaseVoteProduct) -
matcherData.voteProductDeduction;
if (adjustedVoteProduct > 0)
voteMatch = OzMath.mulDiv(
matcherData.voteBudget,
Math.max(matchReward.nonBaseVoteProduct, rewardValues.voteProduct),
adjustedVoteProduct
);
totalMatch = voteMatch + incentiveMatch;
}
if (totalMatch > 0) {
// send match to reward and notify
baseToken.safeTransfer(address(rewardContract), totalMatch);
rewardContract.notifyRewardAmount(baseToken, NOTIFY_PERIOD);
}
emit Distribute(epoch, rewardContract, matcher, baseToken, totalMatch, voteMatch, incentiveMatch);
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function getRolloverBudget(
uint256 epoch,
address matcher
) public view checkEpoch(epoch) returns (uint256 matchRolloverAmount, uint256 voteRolloverAmount) {
// can only rollover after vetoing ended
_checkVetoPeriodEnded(epoch);
CheckpointData storage checkpoint = checkpoints[epoch];
EpochInformation storage rewardTotals = checkpoint.dataTotals;
MatcherData storage matcherData = checkpoint.matcherData[matcher];
// this matcher's budget - (external incentives - excluded)
matchRolloverAmount = Math.clip(
matcherData.matchBudget,
(matcherData.nonBaseExternalIncentives + rewardTotals.externalIncentives) -
matcherData.externalIncentivesDeduction
);
uint256 effectiveVoteProduct = Math.max(matcherData.nonBaseVoteProduct, rewardTotals.voteProduct) -
matcherData.voteProductDeduction;
// if there was zero pro rata product, then none of the vote budget was
// allocated and all of it can be rolled over. else, voteRollerAmount
// remains zero.
if (effectiveVoteProduct == 0) voteRolloverAmount = matcherData.voteBudget;
}
/// @inheritdoc IMaverickV2IncentiveMatcher
function rolloverExcessBudget(
uint256 matchedEpoch,
uint256 newEpoch
)
public
checkEpoch(matchedEpoch)
checkEpoch(newEpoch)
returns (uint256 matchRolloverAmount, uint256 voteRolloverAmount)
{
(matchRolloverAmount, voteRolloverAmount) = getRolloverBudget(matchedEpoch, msg.sender);
// delete budget account so user can not rollover twice.
delete checkpoints[matchedEpoch].matcherData[msg.sender];
emit BudgetRolledOver(msg.sender, matchRolloverAmount, voteRolloverAmount, matchedEpoch, newEpoch);
// add budgets to new epoch; checks that new epoch is not over yet
_addBudget(matchRolloverAmount.toUint128(), voteRolloverAmount.toUint128(), newEpoch);
}
}
// 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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
{
"compilationTarget": {
"@maverick/v2-rewards/contracts/MaverickV2IncentiveMatcher.sol": "MaverickV2IncentiveMatcher"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 5500
},
"remappings": [],
"viaIR": true
}
[{"inputs":[],"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":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"}],"name":"IncentiveMatcherEpochAlreadyDistributed","type":"error"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"IncentiveMatcherEpochHasEnded","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentTime","type":"uint256"},{"internalType":"uint256","name":"epochEnd","type":"uint256"}],"name":"IncentiveMatcherEpochHasNotEnded","type":"error"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"IncentiveMatcherInvalidEpoch","type":"error"},{"inputs":[{"internalType":"contract IMaverickV2Reward","name":"lastReward","type":"address"},{"internalType":"contract IMaverickV2Reward","name":"voteReward","type":"address"}],"name":"IncentiveMatcherInvalidTargetOrder","type":"error"},{"inputs":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"uint256","name":"voteWeights","type":"uint256"},{"internalType":"uint256","name":"totalVoteWeight","type":"uint256"},{"internalType":"uint256","name":"vote","type":"uint256"}],"name":"IncentiveMatcherInvalidVote","type":"error"},{"inputs":[{"internalType":"address","name":"matcher","type":"address"},{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"IncentiveMatcherMatcherAlreadyVetoed","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"IncentiveMatcherMatcherHasNoBudget","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"permissionedCaller","type":"address"}],"name":"IncentiveMatcherNotPermissionedCaller","type":"error"},{"inputs":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"}],"name":"IncentiveMatcherNotRewardFactoryContract","type":"error"},{"inputs":[],"name":"IncentiveMatcherRewardDoesNotHaveVeStakingOption","type":"error"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"matcher","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"IncentiveMatcherRewardInvalidIncentiveToken","type":"error"},{"inputs":[],"name":"IncentiveMatcherSenderHasAlreadyVoted","type":"error"},{"inputs":[{"internalType":"address","name":"voter","type":"address"},{"internalType":"uint256","name":"voteSnapshotTimestamp","type":"uint256"}],"name":"IncentiveMatcherSenderHasNoVotingPower","type":"error"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"IncentiveMatcherTokenMultipliersAlreadySet","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentTime","type":"uint256"},{"internalType":"uint256","name":"voteEnd","type":"uint256"}],"name":"IncentiveMatcherVetoPeriodHasNotEnded","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentTime","type":"uint256"},{"internalType":"uint256","name":"vetoStart","type":"uint256"},{"internalType":"uint256","name":"vetoEnd","type":"uint256"}],"name":"IncentiveMatcherVetoPeriodNotActive","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentTime","type":"uint256"},{"internalType":"uint256","name":"voteStart","type":"uint256"},{"internalType":"uint256","name":"voteEnd","type":"uint256"}],"name":"IncentiveMatcherVotePeriodNotActive","type":"error"},{"inputs":[],"name":"IncentiveMatcherZeroAmount","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"matcher","type":"address"},{"indexed":false,"internalType":"uint256","name":"matchAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"voteAmount","type":"uint256"}],"name":"BudgetAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"matcher","type":"address"},{"indexed":false,"internalType":"uint256","name":"matchRolloverAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"voteRolloverAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"matchedEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newEpoch","type":"uint256"}],"name":"BudgetRolledOver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"indexed":true,"internalType":"address","name":"matcher","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"_baseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalMatch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"voteMatch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"incentiveMatch","type":"uint256"}],"name":"Distribute","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"IncentiveAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"}],"name":"NonBaseTokenIncentiveAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"matcher","type":"address"},{"indexed":true,"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonBaseAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"adjustedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenMultiplierIndex","type":"uint256"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"}],"name":"NonBaseTokenIncentiveApplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"matcher","type":"address"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"multiplier","type":"uint256"}],"indexed":false,"internalType":"struct IMaverickV2IncentiveMatcher.TokenMultiplier[]","name":"tokenMultipliers","type":"tuple[]"}],"name":"SetTokenMultipliers","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"matcher","type":"address"},{"indexed":true,"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"voteProductDeduction","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"externalIncentivesDeduction","type":"uint256"}],"name":"Veto","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"contract IMaverickV2Reward","name":"rewardContract","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":"NOTIFY_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRE_VOTE_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VETO_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"activeRewards","outputs":[{"components":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"components":[{"internalType":"uint128","name":"votes","type":"uint128"},{"internalType":"uint128","name":"voteProduct","type":"uint128"},{"internalType":"uint128","name":"externalIncentives","type":"uint128"},{"internalType":"bool","name":"tracked","type":"bool"}],"internalType":"struct IMaverickV2IncentiveMatcher.EpochInformation","name":"rewardInformation","type":"tuple"}],"internalType":"struct IMaverickV2IncentiveMatcher.RewardData[]","name":"returnElements","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"activeRewardsCount","outputs":[{"internalType":"uint256","name":"count","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint256","name":"_duration","type":"uint256"}],"name":"addIncentives","outputs":[{"internalType":"uint256","name":"duration","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"matchBudget","type":"uint128"},{"internalType":"uint128","name":"voteBudget","type":"uint128"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"addMatchingBudget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint256","name":"_duration","type":"uint256"},{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"addNonBaseTokenIncentives","outputs":[{"internalType":"uint256","name":"duration","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"address","name":"matcher","type":"address"},{"internalType":"uint256","name":"tokenMultiplierIndex","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"applyNonBaseTokenMatch","outputs":[{"internalType":"uint128","name":"adjustedAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"checkpointData","outputs":[{"internalType":"uint128","name":"matchBudget","type":"uint128"},{"internalType":"uint128","name":"voteBudget","type":"uint128"},{"components":[{"internalType":"uint128","name":"votes","type":"uint128"},{"internalType":"uint128","name":"voteProduct","type":"uint128"},{"internalType":"uint128","name":"externalIncentives","type":"uint128"},{"internalType":"bool","name":"tracked","type":"bool"}],"internalType":"struct IMaverickV2IncentiveMatcher.EpochInformation","name":"epochTotals","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"matcher","type":"address"}],"name":"checkpointMatcherBudget","outputs":[{"internalType":"uint128","name":"matchBudget","type":"uint128"},{"internalType":"uint128","name":"voteBudget","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"matcher","type":"address"}],"name":"checkpointMatcherData","outputs":[{"components":[{"internalType":"uint128","name":"matchBudget","type":"uint128"},{"internalType":"uint128","name":"voteBudget","type":"uint128"},{"internalType":"uint128","name":"externalIncentivesDeduction","type":"uint128"},{"internalType":"uint128","name":"voteProductDeduction","type":"uint128"},{"internalType":"uint128","name":"nonBaseExternalIncentives","type":"uint128"},{"internalType":"uint128","name":"nonBaseVoteProduct","type":"uint128"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"multiplier","type":"uint256"}],"internalType":"struct IMaverickV2IncentiveMatcher.TokenMultiplier[]","name":"tokenMultipliers","type":"tuple[]"}],"internalType":"struct IMaverickV2IncentiveMatcher.MatcherData","name":"matcherData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"contract IMaverickV2Reward[]","name":"rewardContracts","type":"address[]"}],"name":"checkpointNonBaseIncentiveInfo","outputs":[{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"uint256","name":"incentiveAmount","type":"uint256"}],"internalType":"struct IMaverickV2IncentiveMatcher.NonBaseIncentives[]","name":"nonBaseIncentives","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"}],"name":"checkpointRewardData","outputs":[{"components":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"components":[{"internalType":"uint128","name":"votes","type":"uint128"},{"internalType":"uint128","name":"voteProduct","type":"uint128"},{"internalType":"uint128","name":"externalIncentives","type":"uint128"},{"internalType":"bool","name":"tracked","type":"bool"}],"internalType":"struct IMaverickV2IncentiveMatcher.EpochInformation","name":"rewardInformation","type":"tuple"}],"internalType":"struct IMaverickV2IncentiveMatcher.RewardData","name":"rewardData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"address","name":"matcher","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"distribute","outputs":[{"internalType":"uint256","name":"totalMatch","type":"uint256"},{"internalType":"uint256","name":"incentiveMatch","type":"uint256"},{"internalType":"uint256","name":"voteMatch","type":"uint256"}],"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":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"matcher","type":"address"}],"name":"getRolloverBudget","outputs":[{"internalType":"uint256","name":"matchRolloverAmount","type":"uint256"},{"internalType":"uint256","name":"voteRolloverAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"matcher","type":"address"},{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"hasDistributed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"matcher","type":"address"},{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"hasVetoed","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":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"matchers","outputs":[{"internalType":"address[]","name":"returnElements","type":"address[]"},{"components":[{"internalType":"uint128","name":"matchBudget","type":"uint128"},{"internalType":"uint128","name":"voteBudget","type":"uint128"},{"internalType":"uint128","name":"externalIncentivesDeduction","type":"uint128"},{"internalType":"uint128","name":"voteProductDeduction","type":"uint128"},{"internalType":"uint128","name":"nonBaseExternalIncentives","type":"uint128"},{"internalType":"uint128","name":"nonBaseVoteProduct","type":"uint128"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"multiplier","type":"uint256"}],"internalType":"struct IMaverickV2IncentiveMatcher.TokenMultiplier[]","name":"tokenMultipliers","type":"tuple[]"}],"internalType":"struct IMaverickV2IncentiveMatcher.MatcherData[]","name":"matcherData","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"matchersCount","outputs":[{"internalType":"uint256","name":"count","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":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"permissionedAddIncentives","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"permissionedCaller","outputs":[{"internalType":"contract IMaverickV2IncentiveMatcherCaller","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardFactory","outputs":[{"internalType":"contract IMaverickV2RewardFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"}],"name":"rewardHasVeCheck","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"matchedEpoch","type":"uint256"},{"internalType":"uint256","name":"newEpoch","type":"uint256"}],"name":"rolloverExcessBudget","outputs":[{"internalType":"uint256","name":"matchRolloverAmount","type":"uint256"},{"internalType":"uint256","name":"voteRolloverAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"multiplier","type":"uint256"}],"internalType":"struct IMaverickV2IncentiveMatcher.TokenMultiplier[]","name":"_tokenMultipliers","type":"tuple[]"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"setTokenMultipliers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"veToken","outputs":[{"internalType":"contract IMaverickV2VotingEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IMaverickV2Reward","name":"rewardContract","type":"address"}],"name":"veto","outputs":[{"internalType":"uint128","name":"voteProductDeduction","type":"uint128"},{"internalType":"uint128","name":"externalIncentivesDeduction","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"vetoingEnd","outputs":[{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"vetoingIsActive","outputs":[{"internalType":"bool","name":"isActive","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"vetoingIsOver","outputs":[{"internalType":"bool","name":"isOver","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IMaverickV2Reward[]","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"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"votingStart","outputs":[{"internalType":"uint256","name":"start","type":"uint256"}],"stateMutability":"pure","type":"function"}]