// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
return _roleMembers[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
return _roleMembers[role].length();
}
/**
* @dev Overload {_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
_roleMembers[role].add(account);
}
/**
* @dev Overload {_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
_roleMembers[role].remove(account);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @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, it is bubbled up by this
* function (like regular Solidity function calls).
*
* 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.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @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`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @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;
}
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "../interfaces/utils/ICounter.sol";
contract Counter is ICounter {
/// @inheritdoc ICounter
uint256 public value = 0;
/// @inheritdoc ICounter
address public owner;
/// @inheritdoc ICounter
address public immutable operator;
/// @inheritdoc ICounter
address public immutable token;
/// @inheritdoc ICounter
address public immutable farm;
constructor(
address owner_,
address operator_,
address token_,
address farm_
) {
owner = owner_;
operator = operator_;
token = token_;
farm = farm_;
}
/// @inheritdoc ICounter
function transferOwnership(address newOwner) external {
require(msg.sender == owner, "Counter: not owner");
owner = newOwner;
}
/// @inheritdoc ICounter
function add(
uint256 additionalValue,
address token_,
address farm_
) external {
require(msg.sender == operator, "Counter: not operator");
require(token_ == token, "Counter: invalid token");
require(farm_ == farm, "Counter: invalid farm");
value += additionalValue;
}
/// @inheritdoc ICounter
function reset() external {
require(msg.sender == owner, "Counter: not owner");
value = 0;
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
/// @notice This is a default access control with 3 roles:
///
/// - ADMIN: allowed to do anything
/// - ADMIN_DELEGATE: allowed to do anything except assigning ADMIN and ADMIN_DELEGATE roles
/// - OPERATOR: low-privileged role, generally keeper or some other bot
contract DefaultAccessControl is AccessControlEnumerable {
error Forbidden();
error AddressZero();
bytes32 public constant OPERATOR = keccak256("operator");
bytes32 public constant ADMIN_ROLE = keccak256("admin");
bytes32 public constant ADMIN_DELEGATE_ROLE = keccak256("admin_delegate");
/// @notice Creates a new contract.
/// @param admin Admin of the contract
constructor(address admin) {
if (admin == address(0)) revert AddressZero();
_grantRole(OPERATOR, admin);
_grantRole(ADMIN_ROLE, admin);
_setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
_setRoleAdmin(ADMIN_DELEGATE_ROLE, ADMIN_ROLE);
_setRoleAdmin(OPERATOR, ADMIN_DELEGATE_ROLE);
}
// ------------------------- EXTERNAL, VIEW ------------------------------
/// @notice Checks if the address is ADMIN or ADMIN_DELEGATE.
/// @param sender Adddress to check
/// @return `true` if sender is an admin, `false` otherwise
function isAdmin(address sender) public view returns (bool) {
return
hasRole(ADMIN_ROLE, sender) || hasRole(ADMIN_DELEGATE_ROLE, sender);
}
/// @notice Checks if the address is OPERATOR.
/// @param sender Adddress to check
/// @return `true` if sender is an admin, `false` otherwise
function isOperator(address sender) public view returns (bool) {
return hasRole(OPERATOR, sender);
}
// ------------------------- INTERNAL, VIEW ------------------------------
function _requireAdmin() internal view {
if (!isAdmin(msg.sender)) {
revert Forbidden();
}
}
function _requireAtLeastOperator() internal view {
if (!isAdmin(msg.sender) && !isOperator(msg.sender)) {
revert Forbidden();
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.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}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* 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.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => 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 override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override 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 override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override 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 `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` 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 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
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 `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `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.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` 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.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
// diff: original lib works under 0.7.6 with overflows enabled
unchecked {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
// diff: original uint256 twos = -denominator & denominator;
uint256 twos = uint256(-int256(denominator)) & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivRoundingUp(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
// diff: original lib works under 0.7.6 with overflows enabled
unchecked {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
/**
* @title IAmmDepositWithdrawModule Interface
* @dev Interface for depositing into and withdrawing from Automated Market Maker (AMM) liquidity pools.
* Provides functionality to manage liquidity by depositing and withdrawing tokens in a controlled manner.
*/
interface IAmmDepositWithdrawModule {
/**
* @dev Deposits specified amounts of token0 and token1 into the AMM pool for a given tokenId.
* This operation increases the liquidity in the pool corresponding to the tokenId.
*
* @param tokenId The ID of the AMM position token.
* @param amount0 The amount of token0 to deposit.
* @param amount1 The amount of token1 to deposit.
* @param from The address from which the tokens will be transferred.
* @return actualAmount0 The actual amount of token0 that was deposited.
* @return actualAmount1 The actual amount of token1 that was deposited.
*
* @notice The caller must have previously approved this contract to spend the specified
* amounts of token0 and token1 on their behalf.
*/
function deposit(
uint256 tokenId,
uint256 amount0,
uint256 amount1,
address from
) external returns (uint256 actualAmount0, uint256 actualAmount1);
/**
* @dev Withdraws a specified amount of liquidity from a position identified by tokenId and
* transfers the corresponding amounts of token0 and token1 to a recipient address. This operation
* reduces the liquidity in the pool and collects tokens from the position associated with the tokenId.
*
* @param tokenId The ID of the AMM position token from which liquidity is to be withdrawn.
* @param liquidity The amount of liquidity to withdraw.
* @param to The address to which the withdrawn tokens will be transferred.
* @return actualAmount0 The actual amount of token0 that was collected and transferred.
* @return actualAmount1 The actual amount of token1 that was collected and transferred.
*
* @notice This function will collect tokens from position associated with the specified tokenId.
*/
function withdraw(
uint256 tokenId,
uint256 liquidity,
address to
) external returns (uint256 actualAmount0, uint256 actualAmount1);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
/**
* @title IAmmModule Interface
* @dev Interface for interacting with a specific Automated Market Maker (AMM) protocol,
* including functionalities for staking and collecting rewards through pre and post rebalance hooks.
*/
interface IAmmModule {
/**
* @dev Struct representing an AMM position.
* Contains details about the liquidity position in an AMM pool.
*/
struct AmmPosition {
address token0; // Address of the first token in the AMM pair
address token1; // Address of the second token in the AMM pair
uint24 property; // Represents a fee or tickSpacing property
int24 tickLower; // Lower tick of the position
int24 tickUpper; // Upper tick of the position
uint128 liquidity; // Liquidity of the position
}
/**
* @dev Validates protocol parameters.
* @param params The protocol parameters to be validated.
*/
function validateProtocolParams(bytes memory params) external view;
/**
* @dev Validates callback parameters.
* @param params The callback parameters to be validated.
*/
function validateCallbackParams(bytes memory params) external view;
/**
* @dev Calculates token amounts for a given liquidity amount in a position.
* @param liquidity Liquidity amount.
* @param sqrtPriceX96 Square root of the current price in the pool.
* @param tickLower Lower tick of the position.
* @param tickUpper Upper tick of the position.
* @return amount0 Amount of token0.
* @return amount1 Amount of token1.
*/
function getAmountsForLiquidity(
uint128 liquidity,
uint160 sqrtPriceX96,
int24 tickLower,
int24 tickUpper
) external pure returns (uint256 amount0, uint256 amount1);
/**
* @dev Returns the Total Value Locked (TVL) for a token and liquidity pool state.
* @param tokenId Token ID.
* @param sqrtRatioX96 Square root of the current tick value in the pool.
* @param callbackParams Callback function parameters.
* @param protocolParams Protocol-specific parameters.
* @return amount0 Amount of token0 locked.
* @return amount1 Amount of token1 locked.
*/
function tvl(
uint256 tokenId,
uint160 sqrtRatioX96,
bytes memory callbackParams,
bytes memory protocolParams
) external view returns (uint256 amount0, uint256 amount1);
/**
* @dev Retrieves the AMM position for a given token ID.
* @param tokenId Token ID.
* @return AmmPosition struct with position details.
*/
function getAmmPosition(
uint256 tokenId
) external view returns (AmmPosition memory);
/**
* @dev Returns the pool address for given tokens and property.
* @param token0 First token address.
* @param token1 Second token address.
* @param property Pool property - fee or tickSpacing.
* @return Pool address.
*/
function getPool(
address token0,
address token1,
uint24 property
) external view returns (address);
/**
* @dev Retrieves the property of a pool.
* @param pool Pool address.
* @return Property value of the pool.
*/
function getProperty(address pool) external view returns (uint24);
/**
* @dev Hook called before rebalancing a token or before any deposit/withdraw actions.
* @param tokenId Token ID being rebalanced.
* @param callbackParams Callback parameters.
* @param protocolParams Protocol-specific parameters.
*/
function beforeRebalance(
uint256 tokenId,
bytes memory callbackParams,
bytes memory protocolParams
) external;
/**
* @dev Hook called after rebalancing a token or before any deposit/withdraw actions.
* @param tokenId Token ID rebalanced.
* @param callbackParams Callback parameters.
* @param protocolParams Protocol-specific parameters.
*/
function afterRebalance(
uint256 tokenId,
bytes memory callbackParams,
bytes memory protocolParams
) external;
/**
* @dev Transfers a token ERC721 from one address to another.
* @param from Address to transfer from.
* @param to Address to transfer to.
* @param tokenId Token ID to be transferred.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Returns the address of the position manager.
*/
function positionManager() external view returns (address);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "./IVoter.sol";
/// @title The interface for the CL Factory
/// @notice The CL Factory facilitates creation of CL pools and control over the protocol fees
interface ICLFactory {
/// @notice Emitted when the owner of the factory is changed
/// @param oldOwner The owner before the owner was changed
/// @param newOwner The owner after the owner was changed
event OwnerChanged(address indexed oldOwner, address indexed newOwner);
/// @notice Emitted when the swapFeeManager of the factory is changed
/// @param oldFeeManager The swapFeeManager before the swapFeeManager was changed
/// @param newFeeManager The swapFeeManager after the swapFeeManager was changed
event SwapFeeManagerChanged(
address indexed oldFeeManager,
address indexed newFeeManager
);
/// @notice Emitted when the swapFeeModule of the factory is changed
/// @param oldFeeModule The swapFeeModule before the swapFeeModule was changed
/// @param newFeeModule The swapFeeModule after the swapFeeModule was changed
event SwapFeeModuleChanged(
address indexed oldFeeModule,
address indexed newFeeModule
);
/// @notice Emitted when the unstakedFeeManager of the factory is changed
/// @param oldFeeManager The unstakedFeeManager before the unstakedFeeManager was changed
/// @param newFeeManager The unstakedFeeManager after the unstakedFeeManager was changed
event UnstakedFeeManagerChanged(
address indexed oldFeeManager,
address indexed newFeeManager
);
/// @notice Emitted when the unstakedFeeModule of the factory is changed
/// @param oldFeeModule The unstakedFeeModule before the unstakedFeeModule was changed
/// @param newFeeModule The unstakedFeeModule after the unstakedFeeModule was changed
event UnstakedFeeModuleChanged(
address indexed oldFeeModule,
address indexed newFeeModule
);
/// @notice Emitted when the defaultUnstakedFee of the factory is changed
/// @param oldUnstakedFee The defaultUnstakedFee before the defaultUnstakedFee was changed
/// @param newUnstakedFee The defaultUnstakedFee after the unstakedFeeModule was changed
event DefaultUnstakedFeeChanged(
uint24 indexed oldUnstakedFee,
uint24 indexed newUnstakedFee
);
/// @notice Emitted when a pool is created
/// @param token0 The first token of the pool by address sort order
/// @param token1 The second token of the pool by address sort order
/// @param tickSpacing The minimum number of ticks between initialized ticks
/// @param pool The address of the created pool
event PoolCreated(
address indexed token0,
address indexed token1,
int24 indexed tickSpacing,
address pool
);
/// @notice Emitted when a new tick spacing is enabled for pool creation via the factory
/// @param tickSpacing The minimum number of ticks between initialized ticks for pools
/// @param fee The default fee for a pool created with a given tickSpacing
event TickSpacingEnabled(int24 indexed tickSpacing, uint24 indexed fee);
/// @notice The voter contract, used to create gauges
/// @return The address of the voter contract
function voter() external view returns (IVoter);
/// @notice The address of the pool implementation contract used to deploy proxies / clones
/// @return The address of the pool implementation contract
function poolImplementation() external view returns (address);
/// @notice Returns the current owner of the factory
/// @dev Can be changed by the current owner via setOwner
/// @return The address of the factory owner
function owner() external view returns (address);
/// @notice Returns the current swapFeeManager of the factory
/// @dev Can be changed by the current swap fee manager via setSwapFeeManager
/// @return The address of the factory swapFeeManager
function swapFeeManager() external view returns (address);
/// @notice Returns the current swapFeeModule of the factory
/// @dev Can be changed by the current swap fee manager via setSwapFeeModule
/// @return The address of the factory swapFeeModule
function swapFeeModule() external view returns (address);
/// @notice Returns the current unstakedFeeManager of the factory
/// @dev Can be changed by the current unstaked fee manager via setUnstakedFeeManager
/// @return The address of the factory unstakedFeeManager
function unstakedFeeManager() external view returns (address);
/// @notice Returns the current unstakedFeeModule of the factory
/// @dev Can be changed by the current unstaked fee manager via setUnstakedFeeModule
/// @return The address of the factory unstakedFeeModule
function unstakedFeeModule() external view returns (address);
/// @notice Returns the current defaultUnstakedFee of the factory
/// @dev Can be changed by the current unstaked fee manager via setDefaultUnstakedFee
/// @return The default Unstaked Fee of the factory
function defaultUnstakedFee() external view returns (uint24);
/// @notice Returns a default fee for a tick spacing.
/// @dev Use getFee for the most up to date fee for a given pool.
/// A tick spacing can never be removed, so this value should be hard coded or cached in the calling context
/// @param tickSpacing The enabled tick spacing. Returns 0 if not enabled
/// @return fee The default fee for the given tick spacing
function tickSpacingToFee(
int24 tickSpacing
) external view returns (uint24 fee);
/// @notice Returns a list of enabled tick spacings. Used to iterate through pools created by the factory
/// @dev Tick spacings cannot be removed. Tick spacings are not ordered
/// @return List of enabled tick spacings
function tickSpacings() external view returns (int24[] memory);
/// @notice Returns the pool address for a given pair of tokens and a tick spacing, or address 0 if it does not exist
/// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
/// @param tokenA The contract address of either token0 or token1
/// @param tokenB The contract address of the other token
/// @param tickSpacing The tick spacing of the pool
/// @return pool The pool address
function getPool(
address tokenA,
address tokenB,
int24 tickSpacing
) external view returns (address pool);
/// @notice Return address of pool created by this factory given its `index`
/// @param index Index of the pool
/// @return The pool address in the given index
function allPools(uint256 index) external view returns (address);
/// @notice Returns the number of pools created from this factory
/// @return Number of pools created from this factory
function allPoolsLength() external view returns (uint256);
/// @notice Used in VotingEscrow to determine if a contract is a valid pool of the factory
/// @param pool The address of the pool to check
/// @return Whether the pool is a valid pool of the factory
function isPool(address pool) external view returns (bool);
/// @notice Get swap & flash fee for a given pool. Accounts for default and dynamic fees
/// @dev Swap & flash fee is denominated in pips. i.e. 1e-6
/// @param pool The pool to get the swap & flash fee for
/// @return The swap & flash fee for the given pool
function getSwapFee(address pool) external view returns (uint24);
/// @notice Get unstaked fee for a given pool. Accounts for default and dynamic fees
/// @dev Unstaked fee is denominated in pips. i.e. 1e-6
/// @param pool The pool to get the unstaked fee for
/// @return The unstaked fee for the given pool
function getUnstakedFee(address pool) external view returns (uint24);
/// @notice Creates a pool for the given two tokens and fee
/// @param tokenA One of the two tokens in the desired pool
/// @param tokenB The other of the two tokens in the desired pool
/// @param tickSpacing The desired tick spacing for the pool
/// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
/// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. The call will
/// revert if the pool already exists, the tick spacing is invalid, or the token arguments are invalid
/// @return pool The address of the newly created pool
function createPool(
address tokenA,
address tokenB,
int24 tickSpacing,
uint160 sqrtPriceX96
) external returns (address pool);
/// @notice Updates the owner of the factory
/// @dev Must be called by the current owner
/// @param _owner The new owner of the factory
function setOwner(address _owner) external;
/// @notice Updates the swapFeeManager of the factory
/// @dev Must be called by the current swap fee manager
/// @param _swapFeeManager The new swapFeeManager of the factory
function setSwapFeeManager(address _swapFeeManager) external;
/// @notice Updates the swapFeeModule of the factory
/// @dev Must be called by the current swap fee manager
/// @param _swapFeeModule The new swapFeeModule of the factory
function setSwapFeeModule(address _swapFeeModule) external;
/// @notice Updates the unstakedFeeManager of the factory
/// @dev Must be called by the current unstaked fee manager
/// @param _unstakedFeeManager The new unstakedFeeManager of the factory
function setUnstakedFeeManager(address _unstakedFeeManager) external;
/// @notice Updates the unstakedFeeModule of the factory
/// @dev Must be called by the current unstaked fee manager
/// @param _unstakedFeeModule The new unstakedFeeModule of the factory
function setUnstakedFeeModule(address _unstakedFeeModule) external;
/// @notice Updates the defaultUnstakedFee of the factory
/// @dev Must be called by the current unstaked fee manager
/// @param _defaultUnstakedFee The new defaultUnstakedFee of the factory
function setDefaultUnstakedFee(uint24 _defaultUnstakedFee) external;
/// @notice Enables a certain tickSpacing
/// @dev Tick spacings may never be removed once enabled
/// @param tickSpacing The spacing between ticks to be enforced in the pool
/// @param fee The default fee associated with a given tick spacing
function enableTickSpacing(int24 tickSpacing, uint24 fee) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {INonfungiblePositionManager} from "./INonfungiblePositionManager.sol";
import {IVoter} from "./IVoter.sol";
import {ICLPool} from "./ICLPool.sol";
import {ICLGaugeFactory} from "./ICLGaugeFactory.sol";
interface ICLGauge {
event NotifyReward(address indexed from, uint256 amount);
event Deposit(
address indexed user,
uint256 indexed tokenId,
uint128 indexed liquidityToStake
);
event Withdraw(
address indexed user,
uint256 indexed tokenId,
uint128 indexed liquidityToStake
);
event ClaimFees(address indexed from, uint256 claimed0, uint256 claimed1);
event ClaimRewards(address indexed from, uint256 amount);
/// @notice NonfungiblePositionManager used to create nfts this gauge accepts
function nft() external view returns (INonfungiblePositionManager);
/// @notice Voter contract gauge receives emissions from
function voter() external view returns (IVoter);
/// @notice Address of the CL pool linked to the gauge
function pool() external view returns (ICLPool);
/// @notice Address of the factory that created this gauge
function gaugeFactory() external view returns (ICLGaugeFactory);
/// @notice Address of the FeesVotingReward contract linked to the gauge
function feesVotingReward() external view returns (address);
/// @notice Timestamp end of current rewards period
function periodFinish() external view returns (uint256);
/// @notice Current reward rate of rewardToken to distribute per second
function rewardRate() external view returns (uint256);
/// @notice Claimable rewards by tokenId
function rewards(uint256 tokenId) external view returns (uint256);
/// @notice Most recent timestamp tokenId called updateRewards
function lastUpdateTime(uint256 tokenId) external view returns (uint256);
/// @notice View to see the rewardRate given the timestamp of the start of the epoch
function rewardRateByEpoch(uint256) external view returns (uint256);
/// @notice Cached amount of fees generated from the Pool linked to the Gauge of token0
function fees0() external view returns (uint256);
/// @notice Cached amount of fees generated from the Pool linked to the Gauge of token1
function fees1() external view returns (uint256);
/// @notice Cached address of token0, corresponding to token0 of the pool
function token0() external view returns (address);
/// @notice Cached address of token1, corresponding to token1 of the pool
function token1() external view returns (address);
/// @notice Cached tick spacing of the pool.
function tickSpacing() external view returns (int24);
/// @notice Total amount of rewardToken to distribute for the current rewards period
function left() external view returns (uint256 _left);
/// @notice Address of the emissions token
function rewardToken() external view returns (address);
/// @notice To provide compatibility support with the old voter
function isPool() external view returns (bool);
/// @notice Returns the rewardGrowthInside of the position at the last user action (deposit, withdraw, getReward)
/// @param tokenId The tokenId of the position
/// @return The rewardGrowthInside for the position
function rewardGrowthInside(
uint256 tokenId
) external view returns (uint256);
/// @notice Called on gauge creation by CLGaugeFactory
/// @param _pool The address of the pool
/// @param _feesVotingReward The address of the feesVotingReward contract
/// @param _rewardToken The address of the reward token
/// @param _voter The address of the voter contract
/// @param _nft The address of the nft position manager contract
/// @param _token0 The address of token0 of the pool
/// @param _token1 The address of token1 of the pool
/// @param _tickSpacing The tick spacing of the pool
/// @param _isPool Whether the attached pool is a real pool or not
function initialize(
address _pool,
address _feesVotingReward,
address _rewardToken,
address _voter,
address _nft,
address _token0,
address _token1,
int24 _tickSpacing,
bool _isPool
) external;
/// @notice Returns the claimable rewards for a given account and tokenId
/// @dev Throws if account is not the position owner
/// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards
/// @param account The address of the user
/// @param tokenId The tokenId of the position
/// @return The amount of claimable reward
function earned(
address account,
uint256 tokenId
) external view returns (uint256);
/// @notice Retrieve rewards for all tokens owned by an account
/// @dev Throws if not called by the voter
/// @param account The account of the user
function getReward(address account) external;
/// @notice Retrieve rewards for a tokenId
/// @dev Throws if not called by the position owner
/// @param tokenId The tokenId of the position
function getReward(uint256 tokenId) external;
/// @notice Notifies gauge of gauge rewards.
/// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.
function notifyRewardAmount(uint256 amount) external;
/// @dev Notifies gauge of gauge rewards without distributing its fees.
/// Assumes gauge reward tokens is 18 decimals.
/// If not 18 decimals, rewardRate may have rounding issues.
/// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.
function notifyRewardWithoutClaim(uint256 amount) external;
/// @notice Used to deposit a CL position into the gauge
/// @notice Allows the user to receive emissions instead of fees
/// @param tokenId The tokenId of the position
function deposit(uint256 tokenId) external;
/// @notice Used to withdraw a CL position from the gauge
/// @notice Allows the user to receive fees instead of emissions
/// @notice Outstanding emissions will be collected on withdrawal
/// @param tokenId The tokenId of the position
function withdraw(uint256 tokenId) external;
/// @notice Used to increase liquidity of a staked position
/// @param tokenId The tokenId of the position
/// @param amount0Desired The desired amount of token0 to be staked,
/// @param amount1Desired The desired amount of token1 to be staked,
/// @param amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
/// @param amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
/// @param deadline The time by which the transaction must be included to effect the change
/// @return liquidity The new liquidity amount as a result of the increase
/// @return amount0 The amount of token0 required to obtain new liquidity amount
/// @return amount1 The amount of token1 required to obtain new liquidity amount
function increaseStakedLiquidity(
uint256 tokenId,
uint256 amount0Desired,
uint256 amount1Desired,
uint256 amount0Min,
uint256 amount1Min,
uint256 deadline
) external returns (uint128 liquidity, uint256 amount0, uint256 amount1);
/// @notice Used to decrease liquidity of a staked position
/// @param tokenId The tokenId of the position
/// @param liquidity The amount of liquidity to be unstaked from the gauge
/// @param amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
/// @param amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
/// @param deadline The time by which the transaction must be included to effect the change
/// @return amount0 The amount of token0 decreased from position
/// @return amount1 The amount of token1 decreased from position
function decreaseStakedLiquidity(
uint256 tokenId,
uint128 liquidity,
uint256 amount0Min,
uint256 amount1Min,
uint256 deadline
) external returns (uint256 amount0, uint256 amount1);
/// @notice Fetch all tokenIds staked by a given account
/// @param depositor The address of the user
/// @return The tokenIds of the staked positions
function stakedValues(
address depositor
) external view returns (uint256[] memory);
/// @notice Fetch a staked tokenId by index
/// @param depositor The address of the user
/// @param index The index of the staked tokenId
/// @return The tokenId of the staked position
function stakedByIndex(
address depositor,
uint256 index
) external view returns (uint256);
/// @notice Check whether a position is staked in the gauge by a certain user
/// @param depositor The address of the user
/// @param tokenId The tokenId of the position
/// @return Whether the position is staked in the gauge
function stakedContains(
address depositor,
uint256 tokenId
) external view returns (bool);
/// @notice The amount of positions staked in the gauge by a certain user
/// @param depositor The address of the user
/// @return The amount of positions staked in the gauge
function stakedLength(address depositor) external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ICLGaugeFactory {
event SetNotifyAdmin(address indexed notifyAdmin);
/// @notice Address of the voter contract
function voter() external view returns (address);
/// @notice Address of the gauge implementation contract
function implementation() external view returns (address);
/// @notice Address of the NonfungiblePositionManager used to create nfts that gauges will accept
function nft() external view returns (address);
/// @notice Administrator that can call `notifyRewardWithoutClaim` on gauges
function notifyAdmin() external view returns (address);
/// @notice Set Nonfungible Position Manager
/// @dev Callable once only on initialize
/// @param _nft The nonfungible position manager that will manage positions for this Factory
function setNonfungiblePositionManager(address _nft) external;
/// @notice Set notifyAdmin value on gauge factory
/// @param _admin New administrator that will be able to call `notifyRewardWithoutClaim` on gauges.
function setNotifyAdmin(address _admin) external;
/// @notice Called by the voter contract via factory.createPool
/// @param _forwarder The address of the forwarder contract
/// @param _pool The address of the pool
/// @param _feesVotingReward The address of the feesVotingReward contract
/// @param _rewardToken The address of the reward token
/// @param _isPool Whether the attached pool is a real pool or not
/// @return The address of the created gauge
function createGauge(
address _forwarder,
address _pool,
address _feesVotingReward,
address _rewardToken,
bool _isPool
) external returns (address);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "./pool/ICLPoolConstants.sol";
import "./pool/ICLPoolState.sol";
import "./pool/ICLPoolDerivedState.sol";
import "./pool/ICLPoolActions.sol";
import "./pool/ICLPoolOwnerActions.sol";
import "./pool/ICLPoolEvents.sol";
/// @title The interface for a CL Pool
/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface ICLPool is
ICLPoolConstants,
ICLPoolState,
ICLPoolDerivedState,
ICLPoolActions,
ICLPoolEvents,
ICLPoolOwnerActions
{}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Permissionless pool actions
/// @notice Contains pool methods that can be called by anyone
interface ICLPoolActions {
/// @notice Initialize function used in proxy deployment
/// @dev Can be called once only
/// Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
/// @dev not locked because it initializes unlocked
/// @param _factory The CL factory contract address
/// @param _token0 The first token of the pool by address sort order
/// @param _token1 The second token of the pool by address sort order
/// @param _tickSpacing The pool tick spacing
/// @param _factoryRegistry The address of the factory registry managing the pool factory
/// @param _sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
function initialize(
address _factory,
address _token0,
address _token1,
int24 _tickSpacing,
address _factoryRegistry,
uint160 _sqrtPriceX96
) external;
/// @notice Initialize gauge and nft manager
/// @dev Callable only once, by the gauge factory
/// @param _gauge The gauge corresponding to this pool
/// @param _nft The position manager used for position management
function setGaugeAndPositionManager(address _gauge, address _nft) external;
/// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
/// @dev The caller of this method receives a callback in the form of ICLMintCallback#uniswapV3MintCallback
/// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
/// on tickLower, tickUpper, the amount of liquidity, and the current price.
/// @param recipient The address for which the liquidity will be created
/// @param tickLower The lower tick of the position in which to add liquidity
/// @param tickUpper The upper tick of the position in which to add liquidity
/// @param amount The amount of liquidity to mint
/// @param data Any data that should be passed through to the callback
/// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
/// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
function mint(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount,
bytes calldata data
) external returns (uint256 amount0, uint256 amount1);
/// @notice Collects tokens owed to a position
/// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
/// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
/// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
/// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
/// @param recipient The address which should receive the fees collected
/// @param tickLower The lower tick of the position for which to collect fees
/// @param tickUpper The upper tick of the position for which to collect fees
/// @param amount0Requested How much token0 should be withdrawn from the fees owed
/// @param amount1Requested How much token1 should be withdrawn from the fees owed
/// @return amount0 The amount of fees collected in token0
/// @return amount1 The amount of fees collected in token1
function collect(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
/// @notice Collects tokens owed to a position
/// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
/// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
/// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
/// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
/// @param recipient The address which should receive the fees collected
/// @param tickLower The lower tick of the position for which to collect fees
/// @param tickUpper The upper tick of the position for which to collect fees
/// @param amount0Requested How much token0 should be withdrawn from the fees owed
/// @param amount1Requested How much token1 should be withdrawn from the fees owed
/// @param owner Owner of the position in the pool (nft manager or gauge)
/// @return amount0 The amount of fees collected in token0
/// @return amount1 The amount of fees collected in token1
function collect(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount0Requested,
uint128 amount1Requested,
address owner
) external returns (uint128 amount0, uint128 amount1);
/// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
/// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
/// @dev Fees must be collected separately via a call to #collect
/// @param tickLower The lower tick of the position for which to burn liquidity
/// @param tickUpper The upper tick of the position for which to burn liquidity
/// @param amount How much liquidity to burn
/// @return amount0 The amount of token0 sent to the recipient
/// @return amount1 The amount of token1 sent to the recipient
function burn(
int24 tickLower,
int24 tickUpper,
uint128 amount
) external returns (uint256 amount0, uint256 amount1);
/// @notice Burn liquidity from the supplied owner and account tokens owed for the liquidity to the position
/// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
/// @dev Fees must be collected separately via a call to #collect
/// @param tickLower The lower tick of the position for which to burn liquidity
/// @param tickUpper The upper tick of the position for which to burn liquidity
/// @param amount How much liquidity to burn
/// @param owner Owner of the position in the pool (nft manager or gauge)
/// @return amount0 The amount of token0 sent to the recipient
/// @return amount1 The amount of token1 sent to the recipient
function burn(
int24 tickLower,
int24 tickUpper,
uint128 amount,
address owner
) external returns (uint256 amount0, uint256 amount1);
/// @notice Convert existing liquidity into staked liquidity
/// @notice Only callable by the gauge associated with this pool
/// @param stakedLiquidityDelta The amount by which to increase or decrease the staked liquidity
/// @param tickLower The lower tick of the position for which to stake liquidity
/// @param tickUpper The upper tick of the position for which to stake liquidity
/// @param positionUpdate If the nft and gauge position should be updated
function stake(
int128 stakedLiquidityDelta,
int24 tickLower,
int24 tickUpper,
bool positionUpdate
) external;
/// @notice Swap token0 for token1, or token1 for token0
/// @dev The caller of this method receives a callback in the form of ICLSwapCallback#uniswapV3SwapCallback
/// @param recipient The address to receive the output of the swap
/// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
/// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
/// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
/// value after the swap. If one for zero, the price cannot be greater than this value after the swap
/// @param data Any data to be passed through to the callback
/// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
/// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
/// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
/// @dev The caller of this method receives a callback in the form of ICLFlashCallback#uniswapV3FlashCallback
/// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
/// with 0 amount{0,1} and sending the donation amount(s) from the callback
/// @param recipient The address which will receive the token0 and token1 amounts
/// @param amount0 The amount of token0 to send
/// @param amount1 The amount of token1 to send
/// @param data Any data to be passed through to the callback
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
/// @notice Increase the maximum number of price and liquidity observations that this pool will store
/// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
/// the input observationCardinalityNext.
/// @param observationCardinalityNext The desired minimum number of observations for the pool to store
function increaseObservationCardinalityNext(
uint16 observationCardinalityNext
) external;
/// @notice Updates rewardGrowthGlobalX128 every time when any tick is crossed,
/// or when any position is staked/unstaked from the gauge
function updateRewardsGrowthGlobal() external;
/// @notice Syncs rewards with gauge
/// @param rewardRate the rate rewards being distributed during the epoch
/// @param rewardReserve the available rewards to be distributed during the epoch
/// @param periodFinish the end of the current period of rewards, updated once per epoch
function syncReward(
uint256 rewardRate,
uint256 rewardReserve,
uint256 periodFinish
) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Pool state that never changes
/// @notice These parameters are not defined as immutable (due to proxy pattern) but are effectively immutable.
/// @notice i.e., the methods will always return the same values
interface ICLPoolConstants {
/// @notice The contract that deployed the pool, which must adhere to the ICLFactory interface
/// @return The contract address
function factory() external view returns (address);
/// @notice The first of the two tokens of the pool, sorted by address
/// @return The token contract address
function token0() external view returns (address);
/// @notice The second of the two tokens of the pool, sorted by address
/// @return The token contract address
function token1() external view returns (address);
/// @notice The gauge corresponding to this pool
/// @return The gauge contract address
function gauge() external view returns (address);
/// @notice The nft manager
/// @return The nft manager contract address
function nft() external view returns (address);
/// @notice The factory registry that manages pool <> gauge <> reward factory relationships
/// @return The factory registry contract address
function factoryRegistry() external view returns (address);
/// @notice The pool tick spacing
/// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
/// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
/// This value is an int24 to avoid casting even though it is always positive.
/// @return The tick spacing
function tickSpacing() external view returns (int24);
/// @notice The maximum amount of position liquidity that can use any tick in the range
/// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
/// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
/// @return The max amount of liquidity per tick
function maxLiquidityPerTick() external view returns (uint128);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Pool state that is not stored
/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
/// blockchain. The functions here may have variable gas costs.
interface ICLPoolDerivedState {
/// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
/// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
/// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
/// you must call it with secondsAgos = [3600, 0].
/// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
/// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
/// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
/// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
/// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
/// timestamp
function observe(
uint32[] calldata secondsAgos
)
external
view
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulativeX128s
);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Events emitted by a pool
/// @notice Contains all events emitted by the pool
interface ICLPoolEvents {
/// @notice Emitted exactly once by a pool when #initialize is first called on the pool
/// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
/// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
/// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
event Initialize(uint160 sqrtPriceX96, int24 tick);
/// @notice Emitted when liquidity is minted for a given position
/// @param sender The address that minted the liquidity
/// @param owner The owner of the position and recipient of any minted liquidity
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param amount The amount of liquidity minted to the position range
/// @param amount0 How much token0 was required for the minted liquidity
/// @param amount1 How much token1 was required for the minted liquidity
event Mint(
address sender,
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when fees are collected by the owner of a position
/// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
/// @param owner The owner of the position for which fees are collected
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param amount0 The amount of token0 fees collected
/// @param amount1 The amount of token1 fees collected
event Collect(
address indexed owner,
address recipient,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount0,
uint128 amount1
);
/// @notice Emitted when a position's liquidity is removed
/// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
/// @param owner The owner of the position for which liquidity is removed
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param amount The amount of liquidity to remove
/// @param amount0 The amount of token0 withdrawn
/// @param amount1 The amount of token1 withdrawn
event Burn(
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted by the pool for any swaps between token0 and token1
/// @param sender The address that initiated the swap call, and that received the callback
/// @param recipient The address that received the output of the swap
/// @param amount0 The delta of the token0 balance of the pool
/// @param amount1 The delta of the token1 balance of the pool
/// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
/// @param liquidity The liquidity of the pool after the swap
/// @param tick The log base 1.0001 of price of the pool after the swap
event Swap(
address indexed sender,
address indexed recipient,
int256 amount0,
int256 amount1,
uint160 sqrtPriceX96,
uint128 liquidity,
int24 tick
);
/// @notice Emitted by the pool for any flashes of token0/token1
/// @param sender The address that initiated the swap call, and that received the callback
/// @param recipient The address that received the tokens from flash
/// @param amount0 The amount of token0 that was flashed
/// @param amount1 The amount of token1 that was flashed
/// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
/// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
event Flash(
address indexed sender,
address indexed recipient,
uint256 amount0,
uint256 amount1,
uint256 paid0,
uint256 paid1
);
/// @notice Emitted by the pool for increases to the number of observations that can be stored
/// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index
/// just before a mint/swap/burn.
/// @param observationCardinalityNextOld The previous value of the next observation cardinality
/// @param observationCardinalityNextNew The updated value of the next observation cardinality
event IncreaseObservationCardinalityNext(
uint16 observationCardinalityNextOld,
uint16 observationCardinalityNextNew
);
/// @notice Emitted when the protocol fee is changed by the pool
/// @param feeProtocol0Old The previous value of the token0 protocol fee
/// @param feeProtocol1Old The previous value of the token1 protocol fee
/// @param feeProtocol0New The updated value of the token0 protocol fee
/// @param feeProtocol1New The updated value of the token1 protocol fee
event SetFeeProtocol(
uint8 feeProtocol0Old,
uint8 feeProtocol1Old,
uint8 feeProtocol0New,
uint8 feeProtocol1New
);
/// @notice Emitted when the collected protocol fees are withdrawn by the gauge
/// @param recipient The address that receives the collected protocol fees
/// @param amount0 The amount of token0 protocol fees that is withdrawn
/// @param amount0 The amount of token1 protocol fees that is withdrawn
event CollectFees(
address indexed recipient,
uint128 amount0,
uint128 amount1
);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Permissioned pool actions
/// @notice Contains pool methods that may only be called by the factory owner
interface ICLPoolOwnerActions {
/// @notice Collect the gauge fee accrued to the pool
/// @return amount0 The gauge fee collected in token0
/// @return amount1 The gauge fee collected in token1
function collectFees() external returns (uint128 amount0, uint128 amount1);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
/// per transaction
interface ICLPoolState {
/// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
/// when accessed externally.
/// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
/// tick The current tick of the pool, i.e. according to the last tick transition that was run.
/// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
/// boundary.
/// observationIndex The index of the last oracle observation that was written,
/// observationCardinality The current maximum number of observations stored in the pool,
/// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
/// unlocked Whether the pool is currently locked to reentrancy
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
bool unlocked
);
/// @notice The pool's swap & flash fee in pips, i.e. 1e-6
/// @dev Can be modified in PoolFactory on a pool basis or upgraded to be dynamic.
/// @return The swap & flash fee
function fee() external view returns (uint24);
/// @notice The pool's unstaked fee in pips, i.e. 1e-6
/// @dev Can be modified in PoolFactory on a pool basis or upgraded to be dynamic.
/// @return The unstaked fee
function unstakedFee() external view returns (uint24);
/// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
/// @dev This value can overflow the uint256
function feeGrowthGlobal0X128() external view returns (uint256);
/// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
/// @dev This value can overflow the uint256
function feeGrowthGlobal1X128() external view returns (uint256);
/// @notice The reward growth as a Q128.128 rewards of emission collected per unit of liquidity for the entire life of the pool
/// @dev This value can overflow the uint256
function rewardGrowthGlobalX128() external view returns (uint256);
/// @notice The amounts of token0 and token1 that are owed to the gauge
/// @dev Gauge fees will never exceed uint128 max in either token
function gaugeFees() external view returns (uint128 token0, uint128 token1);
/// @notice the emission rate of time-based farming
function rewardRate() external view returns (uint256);
/// @notice acts as a virtual reserve that holds information on how many rewards are yet to be distributed
function rewardReserve() external view returns (uint256);
/// @notice timestamp of the end of the current epoch's rewards
function periodFinish() external view returns (uint256);
/// @notice last time the rewardReserve and rewardRate were updated
function lastUpdated() external view returns (uint32);
/// @notice tracks total rewards distributed when no staked liquidity in active tick for epoch ending at periodFinish
/// @notice this amount is rolled over on the next call to notifyRewardAmount
/// @dev rollover will always be smaller than the rewards distributed that epoch
function rollover() external view returns (uint256);
/// @notice The currently in range liquidity available to the pool
/// @dev This value has no relationship to the total liquidity across all ticks
/// @dev This value includes staked liquidity
function liquidity() external view returns (uint128);
/// @notice The currently in range staked liquidity available to the pool
/// @dev This value has no relationship to the total staked liquidity across all ticks
function stakedLiquidity() external view returns (uint128);
/// @notice Look up information about a specific tick in the pool
/// @param tick The tick to look up
/// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
/// tick upper,
/// liquidityNet how much liquidity changes when the pool price crosses the tick,
/// stakedLiquidityNet how much staked liquidity changes when the pool price crosses the tick,
/// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
/// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
/// rewardGrowthOutsideX128 the reward growth on the other side of the tick from the current tick in emission token
/// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
/// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
/// secondsOutside the seconds spent on the other side of the tick from the current tick,
/// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
/// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
/// In addition, these values are only relative and must be used only in comparison to previous snapshots for
/// a specific position.
function ticks(
int24 tick
)
external
view
returns (
uint128 liquidityGross,
int128 liquidityNet,
int128 stakedLiquidityNet,
uint256 feeGrowthOutside0X128,
uint256 feeGrowthOutside1X128,
uint256 rewardGrowthOutsideX128,
int56 tickCumulativeOutside,
uint160 secondsPerLiquidityOutsideX128,
uint32 secondsOutside,
bool initialized
);
/// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
function tickBitmap(int16 wordPosition) external view returns (uint256);
/// @notice Returns the information about a position by the position's key
/// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
/// @return _liquidity The amount of liquidity in the position,
/// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
/// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
/// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
/// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
function positions(
bytes32 key
)
external
view
returns (
uint128 _liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
/// @notice Returns data about a specific observation index
/// @param index The element of the observations array to fetch
/// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
/// ago, rather than at a specific index in the array.
/// @return blockTimestamp The timestamp of the observation,
/// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
/// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
/// Returns initialized whether the observation has been initialized and the values are safe to use
function observations(
uint256 index
)
external
view
returns (
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulativeX128,
bool initialized
);
/// @notice Returns data about reward growth within a tick range.
/// RewardGrowthGlobalX128 can be supplied as a parameter for claimable reward calculations.
/// @dev Used in gauge reward/earned calculations
/// @param tickLower The lower tick of the range
/// @param tickUpper The upper tick of the range
/// @param _rewardGrowthGlobalX128 a calculated rewardGrowthGlobalX128 or 0 (in case of 0 it means we use the rewardGrowthGlobalX128 from state)
/// @return rewardGrowthInsideX128 The reward growth in the range
function getRewardGrowthInside(
int24 tickLower,
int24 tickUpper,
uint256 _rewardGrowthGlobalX128
) external view returns (uint256 rewardGrowthInsideX128);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./utils/IRebalanceCallback.sol";
import "./modules/IStrategyModule.sol";
interface ICore is IERC721Receiver {
struct RebalanceEventParams {
address pool;
IAmmModule.AmmPosition ammPositionInfo;
uint160 sqrtPriceX96;
uint256 amount0;
uint256 amount1;
uint256 ammPositionIdBefore;
uint256 ammPositionIdAfter;
}
event Rebalance(RebalanceEventParams rebalanceEventParams);
/**
* @title ManagedPositionInfo Structure
* @dev This structure holds information about a managed position within a liquidity management system.
* It captures various parameters crucial for the operation, management, and strategic decision-making
* for a specific position in Automated Market Makers (AMM) environments.
*/
struct ManagedPositionInfo {
/**
* @notice Determines the portion of the Total Value Locked (TVL) in the ManagedPosition that can be used to pay for rebalancer services.
* @dev Value is multiplied by 1e9. For instance, slippageD9 = 10'000'000 corresponds to 1% of the position.
* This allows for fine-grained control over the economic parameters governing rebalancing actions.
*/
uint32 slippageD9;
/**
* @notice A pool parameter corresponding to the ManagedPosition, usually representing tickSpacing or fee.
* @dev This parameter helps in identifying and utilizing specific characteristics of the pool that are relevant to the management of the position.
*/
uint24 property;
/**
* @notice The owner of the position, capable of performing actions such as withdraw, emptyRebalance, and parameter updates.
* @dev Ensures that only the designated owner can modify or interact with the position, safeguarding against unauthorized access or actions.
*/
address owner;
/**
* @notice The pool corresponding to the ManagedPosition.
* @dev Identifies the specific AMM pool that this position is associated with, facilitating targeted management and operations.
*/
address pool;
/**
* @notice An array of NFTs from the AMM protocol corresponding to the ManagedPosition.
* @dev Allows for the aggregation and management of multiple AMM positions under a single managed position, enhancing the flexibility and capabilities of the system.
*/
uint256[] ammPositionIds;
/**
* @notice A byte array containing custom data for the corresponding AmmModule.
* @dev Stores information necessary for operations like staking, reward collection, etc., enabling customizable and protocol-specific interactions.
*/
bytes callbackParams;
/**
* @notice A byte array containing custom data for the corresponding StrategyModule.
* @dev Holds information about the parameters of the associated strategy, allowing for the implementation and execution of tailored strategic decisions.
*/
bytes strategyParams;
/**
* @notice A byte array containing custom data for the corresponding Oracle.
* @dev Contains parameters for price fetching and protection against MEV (Miner Extractable Value) attacks, enhancing the security and integrity of the position.
*/
bytes securityParams;
}
/**
* @title TargetPositionInfo Structure
* @dev This structure contains data that allows a rebalancer to obtain information about the required final parameters of AMM positions for a specific ManagedPosition.
*/
struct TargetPositionInfo {
/**
* @notice Index of the ManagedPosition.
* @dev Serves as a unique identifier for the ManagedPosition being targeted for rebalancing. This facilitates tracking and management within the broader system.
*/
uint256 id;
/**
* @notice Array of lower ticks corresponding to the expected AMM Positions after rebalancing.
* @dev These ticks define the lower bound of the price ranges for each targeted AMM position. They are integral in determining the optimal positioning and allocation of liquidity within the AMM environment post-rebalance.
*/
int24[] lowerTicks;
/**
* @notice Array of upper ticks corresponding to the expected AMM Positions after rebalancing.
* @dev Similar to `lowerTicks`, these define the upper bound of the price ranges for each targeted AMM position. Together, the lower and upper ticks delineate the price intervals where liquidity will be optimally positioned.
*/
int24[] upperTicks;
/**
* @notice Distribution ratio of liquidity among positions, where the sum in the array equals Q96.
* @dev This array represents the precise distribution of liquidity across the targeted positions, allowing for balanced and strategic allocation post-rebalance. The Q96 notation indicates fixed-point arithmetic for enhanced precision.
*/
uint256[] liquidityRatiosX96;
/**
* @notice Minimum liquidity values for each of the expected AMM Positions after rebalancing.
* @dev Sets the minimum acceptable liquidity for each position, ensuring that rebalancing actions do not result in suboptimal or excessively diluted positions.
*/
uint256[] minLiquidities;
/**
* @notice Information about the original corresponding ManagedPosition.
* @dev Captures the initial state and parameters of the ManagedPosition prior to rebalancing. This includes detailed information necessary for the rebalancer to accurately target the desired end state.
*/
ManagedPositionInfo info;
}
/**
* @title DepositParams Structure
* @dev This structure contains data for depositing AMM Positions and creating corresponding ManagedPositions with specified parameters. It is crucial for initializing and setting up ManagedPositions based on existing AMM positions.
*/
struct DepositParams {
/**
* @notice Defines the portion of the Total Value Locked (TVL) in the ManagedPosition that can be used to pay for rebalancer services.
* @dev The value is multiplied by 1e9, meaning a `slippageD9` value of 10'000'000 corresponds to 1% of the position. This parameter allows for precise economic management of the position with regard to rebalancing costs.
*/
uint32 slippageD9;
/**
* @notice The owner of the position, who is authorized to perform actions such as withdraw, emptyRebalance, and parameter updates.
* @dev Ensures that only the designated owner has the authority to manage and modify the position, safeguarding against unauthorized interventions.
*/
address owner;
/**
* @notice Array of NFTs from the AMM protocol corresponding to the ManagedPosition.
* @dev Enables the aggregation of multiple AMM positions under a single managed position, facilitating collective management and strategic oversight.
*/
uint256[] ammPositionIds;
/**
* @notice A byte array containing custom data for the corresponding AmmModule.
* @dev Stores operational data such as staking details, reward collection mechanisms, etc., providing a flexible interface for AMM-specific functionalities.
*/
bytes callbackParams;
/**
* @notice A byte array containing custom data for the corresponding StrategyModule.
* @dev Encapsulates strategic information, including parameters guiding the management and rebalancing of the position, allowing for tailored strategic execution.
*/
bytes strategyParams;
/**
* @notice A byte array containing custom data for the corresponding Oracle.
* @dev Contains parameters critical for accurate price fetching and MEV (Miner Extractable Value) protection mechanisms, enhancing the position's security and market responsiveness.
*/
bytes securityParams;
}
/**
* @title RebalanceParams Structure
* @dev This structure contains parameters for rebalancing, filled out by the rebalancer. It is crucial for specifying which ManagedPositions are to be rebalanced and detailing how rebalancing actions should be conducted through interactions with a specified callback contract.
*/
struct RebalanceParams {
/**
* @notice Array of IDs for ManagedPositions that the rebalancer intends to rebalance.
* @dev Identifies the specific positions within the liquidity management system that are targeted for rebalancing. This allows for focused and efficient rebalancing actions, addressing the needs of selected positions.
*/
uint256[] ids;
/**
* @notice Address of the contract to which Core.sol will make calls during the rebalancing process to execute all operations with swaps, creation of new positions, etc.
* @dev Specifies the external contract responsible for the operational aspects of the rebalancing process, such as executing swaps and managing position adjustments. This modular approach enables flexible and customizable rebalancing strategies.
*/
address callback;
/**
* @notice Data for the above-mentioned callback contract.
* @dev Contains the necessary information and instructions for the callback contract to execute the rebalancing actions. The format and content of this data are tailored to the specific requirements and functionalities of the callback contract.
*/
bytes data;
}
/**
* @dev Custom error for signaling that a delegate call has failed.
* This error is thrown when a delegate call made by the contract to another contract does not execute successfully.
* Typically, this indicates an issue with the target contract or the data provided for the delegate call.
*/
error DelegateCallFailed();
/**
* @dev Custom error for indicating invalid parameters have been supplied to a function.
* This error is used when the arguments passed to a function do not meet the required criteria,
* such as out-of-range values or parameters that do not adhere to expected formats or constraints.
*/
error InvalidParams();
/**
* @dev Custom error for signaling that an array or similar data structure has an invalid length.
* This error is thrown when the length of an input array or similar data structure does not match
* the expected or required length, potentially leading to incorrect or incomplete processing.
*/
error InvalidLength();
/**
* @dev Custom error for indicating an invalid target has been specified.
* This error is used in contexts where an operation targets a specific entity or address, such as a contract or token,
* and the specified target does not meet the required conditions or is otherwise deemed inappropriate for the operation.
*/
error InvalidTarget();
/**
* @dev Returns the address of the AMM module.
* @return address of the AMM module.
*/
function ammModule() external view returns (IAmmModule);
/**
* @dev Returns the address of the oracle contract.
* @return address of the oracle contract.
*/
function oracle() external view returns (IOracle);
/**
* @dev Returns the strategy module associated with the core contract.
* @return address strategy module contract address.
*/
function strategyModule() external view returns (IStrategyModule);
/**
* @dev Returns the operator flag.
* @return bool value indicating the operator flag.
*/
function operatorFlag() external view returns (bool);
/**
* @dev Retrieves the ManagedPositionInfo struct at the specified index.
* @param id The index of the ManagedPositionInfo struct to retrieve.
* @return ManagedPositionInfo - struct at the specified index.
*/
function managedPositionAt(
uint256 id
) external view returns (ManagedPositionInfo memory);
/**
* @dev Returns the count of managed positions within the contract.
* @return uint256 - total count of managed positions.
*/
function positionCount() external view returns (uint256);
/**
* @dev Retrieves the array of user IDs associated with the given user address.
* @param user The address of the user.
* @return ids array of user IDs.
*/
function getUserIds(
address user
) external view returns (uint256[] memory ids);
/**
* @dev Returns the current protocol parameters.
* This function provides access to protocol-wide settings and parameters
* that govern the behavior and functionalities of the contract. These parameters
* can include configurations related to fees and treasuries.
*
* @return bytes representation of the protocol parameters. The structure and
* interpretation of these parameters depend on the AmmModule implementation and the
* specific protocol logic it adheres to.
*/
function protocolParams() external view returns (bytes memory);
/**
* @dev Sets the operator flag to enable or disable specific operator functionalities,
* such as the requirement for operator privileges to execute rebalances.
* This adjustment allows for the dynamic control over who can initiate and execute rebalance operations,
* a critical aspect of managing the protocol's liquidity and position strategies. By toggling this flag,
* the protocol's administrator can restrict or open rebalancing capabilities in response to operational,
* security, or strategic considerations.
*
* @param operatorFlag_ A boolean value indicating the new state of the operator flag.
* Setting this flag to `true` requires operator privileges for rebalancing actions,
* adding an additional layer of control and security. Conversely, setting it to `false`
* removes the necessity for such privileges, potentially broadening the pool of entities
* that can perform rebalances under certain conditions.
*
* Requirements:
* - Only the admin of the `Core.sol` contract can call this function. This restriction ensures
* that changes to the operator flag, which can significantly impact the protocol's operation and
* security posture, are made solely by the most trusted level of authority within the protocol's governance structure.
*/
function setOperatorFlag(bool operatorFlag_) external;
/**
* @dev Sets the global protocol parameters for the contract.
* This function is intended for administrative use, allowing for the adjustment of
* critical operational parameters that govern the overall behavior of the protocol.
* Changes made through this function can affect rebalancing logic, fee structures,
* security mechanisms, and other foundational aspects of the protocol's operation.
*
* @param params A bytes memory data structure containing the new protocol parameters.
* The structure and content of this data should adhere to the protocol's specification,
* ensuring compatibility and correctness. This could include parameters such as global
* slippage settings, fee rates, security thresholds, or other protocol-wide settings.
*
* Requirements:
* - Only the admin of Core.sol can call this function.
*/
function setProtocolParams(bytes memory params) external;
/**
* @dev Sets the parameters for a specific managed position identified by its ID.
* This function allows updating the position's slippage, callback, strategy, and security parameters,
* enabling dynamic adjustment of the position's operational and strategic settings. It is essential
* for maintaining the relevance and efficiency of the position's strategy and security posture over time.
*
* @param id The unique identifier of the managed position to update.
* @param slippageD9 The maximum allowable proportion of the position's capital that can be allocated
* as compensation to rebalancers for their services. This value is scaled by a factor of 1,000,000,000 (1e9),
* such that a value of 1,000,000,000 represents 100%, allowing for fine-grained control over rebalancing compensation.
* @param callbackParams Custom data for the callback operation, facilitating specific interactions
* and operational adjustments during the rebalancing or other contract-driven processes.
* @param strategyParams Custom data defining the strategic parameters of the position, enabling
* strategic adjustments and alignments with market conditions or portfolio objectives.
* @param securityParams Custom data outlining the security parameters, crucial for adjusting the position's
* security settings and mechanisms in response to evolving market threats or operational requirements.
*
* Requirements:
* - The caller must be the owner of the position, ensuring that only authorized entities can
* make adjustments to the position's parameters.
* - The strategy and security parameters must be valid, adhering to the contract's and underlying protocols'
* requirements and constraints, ensuring the integrity and effectiveness of the position's strategy and security.
*/
function setPositionParams(
uint256 id,
uint32 slippageD9,
bytes memory callbackParams,
bytes memory strategyParams,
bytes memory securityParams
) external;
/**
* @dev Deposits multiple tokens into the contract and creates new ManagedPosition.
* @param params The deposit parameters including strategy parameters, security parameters, slippage, and token IDs.
* @return id The ID of the position for deposited tokens.
*/
function deposit(DepositParams memory params) external returns (uint256 id);
/**
* @dev Withdraws AMM NFTs from the contract and transfers them to the specified address.
* Only the owner of the position can call this function.
* Deletes corresponding ManagedPosition.
*
* @param id The ID of the position with AMM NFTs to withdraw.
* @param to The address to transfer AMM NFTs to.
*/
function withdraw(uint256 id, address to) external;
/**
* @dev Rebalances the portfolio based on the given parameters.
* @param params The parameters for rebalancing.
* - ids: An array of ids of positions to rebalance.
* - callback: The address of the callback contract.
* - data: Additional data to be passed to the callback contract.
*/
function rebalance(RebalanceParams memory params) external;
/**
* @dev This function is used to perform an empty rebalance for a specific position.
* @param id The ID of the position to perform the empty rebalance on.
* @notice This function calls the `beforeRebalance` and `afterRebalance` functions of the `IAmmModule` contract for each tokenId of the position.
* @notice If any of the delegate calls fail, the function will revert.
* @notice This function is used to perform a rebalance without changing the position's liquidity.
* @notice This function is only callable by the owner of the position.
*/
function emptyRebalance(uint256 id) external;
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
interface ICounter {
/**
* @dev Returns the current counter value.
*/
function value() external view returns (uint256);
/**
* @dev Returns the counter's owner address.
*/
function owner() external view returns (address);
/**
* @dev Returns the counter's operator address.
*/
function operator() external view returns (address);
/**
* @dev Transfers ownership of the counter to a new address.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) external;
/**
* @dev Adds a value to the counter. Can only be called by the operator.
*/
function add(
uint256 additionalValue,
address token_,
address farm_
) external;
/**
* @dev Resets the counter to zero. Can only be called by the current owner.
*/
function reset() external;
/**
* @dev Returns the token address.
*/
function token() external view returns (address);
/**
* @dev Returns the farm address.
*/
function farm() external view returns (address);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @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 v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
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 v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @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.
*/
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].
*/
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 v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../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 caller.
*
* 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 v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "../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: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
/// @title ERC721 with permit
/// @notice Extension to ERC721 that includes a permit function for signature based approvals
interface IERC721Permit is IERC721 {
/// @notice The permit typehash used in the permit signature
/// @return The typehash for the permit
function PERMIT_TYPEHASH() external pure returns (bytes32);
/// @notice The domain separator used in the permit signature
/// @return The domain seperator used in encoding of permit signature
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice Approve of a specific token ID for spending by spender via signature
/// @param spender The account that is being approved
/// @param tokenId The ID of the token that is being approved for spending
/// @param deadline The deadline timestamp by which the call must be mined for the approve to work
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function permit(
address spender,
uint256 tokenId,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "../modules/IAmmDepositWithdrawModule.sol";
import "../external/IWETH9.sol";
import "../ICore.sol";
import "../modules/velo/IVeloAmmModule.sol";
import "../modules/strategies/IPulseStrategyModule.sol";
import "../oracles/IVeloOracle.sol";
/**
* @title ILpWrapper Interface
* @dev Interface for a liquidity pool wrapper, facilitating interactions between LP tokens, AMM modules, and core contract functionalities.
*/
interface ILpWrapper {
// Custom errors for handling operation failures
error InsufficientAmounts(); // Thrown when provided amounts are insufficient for operation execution
error InsufficientLpAmount(); // Thrown when the LP amount for withdrawal is insufficient
error AlreadyInitialized(); // Thrown if the wrapper is already initialized
error DepositCallFailed(); // Thrown when a deposit operation fails due to deletage call to the AmmDepositWithdrawModule
error WithdrawCallFailed(); // Thrown when a withdrawal operation fails due to deletage call to the AmmDepositWithdrawModule
error Deadline(); // Thrown when the deadline for a function call has passed
error InvalidPositionsCount(); // Thrown when the number of positions is invalid
event Deposit(
address indexed sender,
address indexed recipient,
address indexed pool,
uint256 lpAmount,
uint256 amount0,
uint256 amount1
);
event Withdraw(
address indexed sender,
address indexed recipient,
address indexed pool,
uint256 lpAmount,
uint256 amount0,
uint256 amount1
);
event PositionParamsSet(
uint56 slippageD9,
IVeloAmmModule.CallbackParams callbackParams,
IPulseStrategyModule.StrategyParams strategyParams,
IVeloOracle.SecurityParams securityParams
);
// Position data structure
struct PositionData {
uint96 nonce;
address operator;
address token0;
address token1;
int24 tickSpacing;
int24 tickLower;
int24 tickUpper;
uint128 liquidity;
uint256 feeGrowthInside0LastX128;
uint256 feeGrowthInside1LastX128;
uint128 tokensOwed0;
uint128 tokensOwed1;
}
/**
* @dev Returns corresponding position info
* @return tokenId - ID of the NFT representing the position
* @return data - PositionData struct containing the position's data
*/
function getInfo()
external
view
returns (uint256 tokenId, PositionData memory data);
/**
* @dev Returns the address of the synthetix farm contract.
* @return Address of the farm contract.
*/
function getFarm() external view returns (address);
/**
* @dev Deposits specified amounts of tokens into corresponding managed position, mints LP tokens and stakes them in the farm on behalf of `to`.
* @param amount0 Amount of token0 to deposit.
* @param amount1 Amount of token1 to deposit.
* @param minLpAmount Minimum amount of LP tokens required to be minted.
* @param to Address to receive the minted LP tokens.
* @param deadline Timestamp by which the deposit operation must be executed.
* @return actualAmount0 Actual amount of token0 deposited.
* @return actualAmount1 Actual amount of token1 deposited.
* @return lpAmount Amount of LP tokens minted.
*/
function depositAndStake(
uint256 amount0,
uint256 amount1,
uint256 minLpAmount,
address to,
uint256 deadline
)
external
returns (
uint256 actualAmount0,
uint256 actualAmount1,
uint256 lpAmount
);
/**
* @dev Withdraws LP tokens from the farm, burns them, and transfers the underlying assets to the specified address.
* @param lpAmount Amount of LP tokens to withdraw.
* @param minAmount0 Minimum amount of asset 0 to receive.
* @param minAmount1 Minimum amount of asset 1 to receive.
* @param to Address to transfer the underlying assets to.
* @param deadline Timestamp by which the withdrawal operation must be executed.
* @return amount0 Actual amount of asset 0 received.
* @return amount1 Actual amount of asset 1 received.
* @return actualLpAmount Actual amount of LP tokens withdrawn.
*/
function unstakeAndWithdraw(
uint256 lpAmount,
uint256 minAmount0,
uint256 minAmount1,
address to,
uint256 deadline
)
external
returns (uint256 amount0, uint256 amount1, uint256 actualLpAmount);
/**
* @dev Harvests the reward tokens from the farm to msg.sender.
*/
function getReward() external;
/**
* @dev Returns the amount of reward tokens earned by the specified user.
*/
function earned(address user) external view returns (uint256 amount);
/**
* @dev Returns protocol params of the corresponding Core.sol
*/
function protocolParams()
external
view
returns (IVeloAmmModule.ProtocolParams memory params, uint256 d9);
/**
* @dev Returns the address of the position manager.
* @return Address of the position manager.
*/
function positionManager() external view returns (address);
/**
* @dev Returns the AMM Deposit Withdraw Module contract address.
* @return Address of the IAmmDepositWithdrawModule contract.
*/
function ammDepositWithdrawModule()
external
view
returns (IAmmDepositWithdrawModule);
/**
* @dev Returns the core contract address.
* @return Address of the core contract.
*/
function core() external view returns (ICore);
/**
* @dev Returns the address of the AMM module associated with this LP wrapper.
* @return Address of the AMM module.
*/
function ammModule() external view returns (IAmmModule);
/**
* @dev Returns the oracle contract address.
* @return Address of the oracle contract.
*/
function oracle() external view returns (IOracle);
/**
* @dev Returns the ID of managed position associated with the LP wrapper contract.
* @return uint256 - id of the managed position.
*/
function positionId() external view returns (uint256);
/**
* @dev Initializes the LP wrapper contract with the specified token ID and initial total supply.
* @param positionId_ Managed position ID to be associated with the LP wrapper contract.
* @param initialTotalSupply Initial total supply of the LP wrapper contract.
*/
function initialize(
uint256 positionId_,
uint256 initialTotalSupply
) external;
/**
* @dev Deposits specified amounts of tokens into corresponding managed position and mints LP tokens to the specified address.
* @param amount0 Amount of token0 to deposit.
* @param amount1 Amount of token1 to deposit.
* @param minLpAmount Minimum amount of LP tokens required to be minted.
* @param to Address to receive the minted LP tokens.
* @param deadline Timestamp by which the deposit operation must be executed.
* @return actualAmount0 Actual amount of token0 deposited.
* @return actualAmount1 Actual amount of token1 deposited.
* @return lpAmount Amount of LP tokens minted.
*/
function deposit(
uint256 amount0,
uint256 amount1,
uint256 minLpAmount,
address to,
uint256 deadline
)
external
returns (
uint256 actualAmount0,
uint256 actualAmount1,
uint256 lpAmount
);
/**
* @dev Burns LP tokens and transfers the underlying assets to the specified address.
* @param lpAmount Amount of LP tokens to withdraw.
* @param minAmount0 Minimum amount of asset 0 to receive.
* @param minAmount1 Minimum amount of asset 1 to receive.
* @param to Address to transfer the underlying assets to.
* @param deadline Timestamp by which the withdrawal operation must be executed.
* @return amount0 Actual amount of asset 0 received.
* @return amount1 Actual amount of asset 1 received.
* @return actualLpAmount Actual amount of LP tokens withdrawn.
*/
function withdraw(
uint256 lpAmount,
uint256 minAmount0,
uint256 minAmount1,
address to,
uint256 deadline
)
external
returns (uint256 amount0, uint256 amount1, uint256 actualLpAmount);
/**
* @dev Sets the managed position parameters for a specified ID, including slippage, strategy, and security parameters.
* @param slippageD9 Maximum permissible proportion of capital allocated to positions for compensating rebalancers, scaled by 1e9.
* @param callbackParams Callback parameters for the position.
* @param strategyParams Strategy parameters for managing the position.
* @param securityParams Security parameters for protecting the position.
* Requirements:
* - Caller must have the ADMIN_ROLE.
*/
function setPositionParams(
uint32 slippageD9,
bytes memory callbackParams,
bytes memory strategyParams,
bytes memory securityParams
) external;
/**
* @dev This function is used to perform an empty rebalance for a specific position.
* @notice This function calls the `beforeRebalance` and `afterRebalance` functions of the `IAmmModule` contract for each tokenId of the position.
* @notice If any of the delegate calls fail, the function will revert.
* Requirements:
* - Caller must have the OPERATOR role.
*/
function emptyRebalance() external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
pragma abicoder v2;
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import "./IERC721Permit.sol";
import "./IPeripheryPayments.sol";
import "./IPeripheryImmutableState.sol";
/// @title Non-fungible token for positions
/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface INonfungiblePositionManager is
IPeripheryPayments,
IPeripheryImmutableState,
IERC721Metadata,
IERC721Enumerable,
IERC721Permit
{
/// @notice Emitted when liquidity is increased for a position NFT
/// @dev Also emitted when a token is minted
/// @param tokenId The ID of the token for which liquidity was increased
/// @param liquidity The amount by which liquidity for the NFT position was increased
/// @param amount0 The amount of token0 that was paid for the increase in liquidity
/// @param amount1 The amount of token1 that was paid for the increase in liquidity
event IncreaseLiquidity(
uint256 indexed tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when liquidity is decreased for a position NFT
/// @param tokenId The ID of the token for which liquidity was decreased
/// @param liquidity The amount by which liquidity for the NFT position was decreased
/// @param amount0 The amount of token0 that was accounted for the decrease in liquidity
/// @param amount1 The amount of token1 that was accounted for the decrease in liquidity
event DecreaseLiquidity(
uint256 indexed tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when tokens are collected for a position NFT
/// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior
/// @param tokenId The ID of the token for which underlying tokens were collected
/// @param recipient The address of the account that received the collected tokens
/// @param amount0 The amount of token0 owed to the position that was collected
/// @param amount1 The amount of token1 owed to the position that was collected
event Collect(
uint256 indexed tokenId,
address recipient,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when a new Token Descriptor is set
/// @param tokenDescriptor Address of the new Token Descriptor
event TokenDescriptorChanged(address indexed tokenDescriptor);
/// @notice Emitted when a new Owner is set
/// @param owner Address of the new Owner
event TransferOwnership(address indexed owner);
/// @notice Returns the position information associated with a given token ID.
/// @dev Throws if the token ID is not valid.
/// @param tokenId The ID of the token that represents the position
/// @return nonce The nonce for permits
/// @return operator The address that is approved for spending
/// @return token0 The address of the token0 for a specific pool
/// @return token1 The address of the token1 for a specific pool
/// @return tickSpacing The tick spacing associated with the pool
/// @return tickLower The lower end of the tick range for the position
/// @return tickUpper The higher end of the tick range for the position
/// @return liquidity The liquidity of the position
/// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
/// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
/// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
/// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
function positions(
uint256 tokenId
)
external
view
returns (
uint96 nonce,
address operator,
address token0,
address token1,
int24 tickSpacing,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
/// @notice Returns the address of the Token Descriptor, that handles generating token URIs for Positions
function tokenDescriptor() external view returns (address);
/// @notice Returns the address of the Owner, that is allowed to set a new TokenDescriptor
function owner() external view returns (address);
struct MintParams {
address token0;
address token1;
int24 tickSpacing;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
uint160 sqrtPriceX96;
}
/// @notice Creates a new position wrapped in a NFT
/// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
/// a method does not exist, i.e. the pool is assumed to be initialized.
/// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
/// @return tokenId The ID of the token that represents the minted position
/// @return liquidity The amount of liquidity for this position
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function mint(
MintParams calldata params
)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct IncreaseLiquidityParams {
uint256 tokenId;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
/// @param params tokenId The ID of the token for which liquidity is being increased,
/// amount0Desired The desired amount of token0 to be spent,
/// amount1Desired The desired amount of token1 to be spent,
/// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
/// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
/// deadline The time by which the transaction must be included to effect the change
/// @return liquidity The new liquidity amount as a result of the increase
/// @return amount0 The amount of token0 to acheive resulting liquidity
/// @return amount1 The amount of token1 to acheive resulting liquidity
function increaseLiquidity(
IncreaseLiquidityParams calldata params
)
external
payable
returns (uint128 liquidity, uint256 amount0, uint256 amount1);
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Decreases the amount of liquidity in a position and accounts it to the position
/// @param params tokenId The ID of the token for which liquidity is being decreased,
/// amount The amount by which liquidity will be decreased,
/// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
/// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
/// deadline The time by which the transaction must be included to effect the change
/// @return amount0 The amount of token0 accounted to the position's tokens owed
/// @return amount1 The amount of token1 accounted to the position's tokens owed
/// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager
/// @dev for tokens that have very high decimals.
/// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.
/// @dev This is equivalent to 1e20 value with 18 decimals.
function decreaseLiquidity(
DecreaseLiquidityParams calldata params
) external payable returns (uint256 amount0, uint256 amount1);
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
/// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
/// @notice Used to update staked positions before deposit and withdraw
/// @param params tokenId The ID of the NFT for which tokens are being collected,
/// recipient The account that should receive the tokens,
/// amount0Max The maximum amount of token0 to collect,
/// amount1Max The maximum amount of token1 to collect
/// @return amount0 The amount of fees collected in token0
/// @return amount1 The amount of fees collected in token1
function collect(
CollectParams calldata params
) external payable returns (uint256 amount0, uint256 amount1);
/// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
/// must be collected first.
/// @param tokenId The ID of the token that is being burned
function burn(uint256 tokenId) external payable;
/// @notice Sets a new Token Descriptor
/// @param _tokenDescriptor Address of the new Token Descriptor to be chosen
function setTokenDescriptor(address _tokenDescriptor) external;
/// @notice Sets a new Owner address
/// @param _owner Address of the new Owner to be chosen
function setOwner(address _owner) external;
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
/**
* @title Oracle Interface
* @dev Interface for interacting with oracles that provide price information for liquidity pools.
* Allows contracts to query such oracles for price information pertinent to specific pools.
*/
interface IOracle {
/**
* @dev Retrieves the price information from an oracle for a given pool.
* This method returns the square root of the price formatted in a fixed-point number with 96 bits of precision,
* along with the tick value associated with the pool's current state. This information is essential
* for contracts that need to perform calculations or make decisions based on the current price dynamics
* of tokens within a liquidity pool.
*
* @param pool The address of the liquidity pool for which price information is requested.
* @return sqrtPriceX96 The square root of the current price in the pool, represented as a 96-bit fixed-point number.
* @return tick The current tick value of the pool, which is an integral value representing the price level.
*/
function getOraclePrice(
address pool
) external view returns (uint160 sqrtPriceX96, int24 tick);
/**
* @dev Ensures that there is no Miner Extractable Value (MEV) opportunity for the specified pool
* based on the current transaction and market conditions. MEV can lead to adverse effects like front-running
* or sandwich attacks, where miners or other participants can exploit users' transactions for profit.
* This method allows contracts to verify the absence of such exploitable conditions before proceeding
* with transactions that might otherwise be vulnerable to MEV.
*
* @param pool The address of the pool for which MEV conditions are being checked.
* @param params Additional parameters that may influence the MEV check, such as transaction details or market conditions.
*/
function ensureNoMEV(address pool, bytes memory params) external view;
/**
* @dev Validates the security parameters provided to the oracle.
* This method allows contracts to ensure that the parameters they intend to use for oracle interactions
* conform to expected formats, ranges, or other criteria established by the oracle for secure operation.
* It's a preemptive measure to catch and correct potential issues in the parameters that could affect
* the reliability or accuracy of the oracle's data.
*
* @param params The security parameters to be validated by the oracle.
*/
function validateSecurityParams(bytes memory params) external view;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Immutable state
/// @notice Functions that return immutable state of the router
interface IPeripheryImmutableState {
/// @return Returns the address of the CL factory
function factory() external view returns (address);
/// @return Returns the address of WETH9
function WETH9() external view returns (address);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Periphery Payments
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPayments {
/// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH.
/// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
/// @param amountMinimum The minimum amount of WETH9 to unwrap
/// @param recipient The address receiving ETH
function unwrapWETH9(
uint256 amountMinimum,
address recipient
) external payable;
/// @notice Refunds any ETH balance held by this contract to the `msg.sender`
/// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps
/// that use ether for the input amount
function refundETH() external payable;
/// @notice Transfers the full amount of a token held by this contract to recipient
/// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
/// @param token The contract address of the token which will be transferred to `recipient`
/// @param amountMinimum The minimum amount of token required for a transfer
/// @param recipient The destination address of the token
function sweepToken(
address token,
uint256 amountMinimum,
address recipient
) external payable;
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "../IStrategyModule.sol";
/**
* @title PulseStrategyModule
* @dev Implements various strategies for Pulse V1, including Original, Lazy Syncing, Lazy Ascending, and Lazy Descending strategies.
*/
interface IPulseStrategyModule is IStrategyModule {
// Custom errors to address operation failures
error InvalidParams(); // Thrown when input parameters are invalid
error InvalidLength(); // Thrown when an array length is incorrect
// Enum representing different types of strategies
enum StrategyType {
Original, // Original Pulse V1 strategy
LazySyncing, // Lazy syncing strategy
LazyAscending, // Lazy ascending strategy
LazyDescending // Lazy descending strategy
}
/**
* @dev Struct for strategy parameters.
* Encapsulates the details required to execute different types of strategies.
*/
struct StrategyParams {
StrategyType strategyType; // Type of strategy
int24 tickNeighborhood; // Neighborhood of ticks to consider for rebalancing
int24 tickSpacing; // tickSpacing of the corresponding amm pool
int24 width; // Width of the interval
}
/**
* @dev Returns the constant value of Q96, representing 2 ** 96.
* Used for fixed-point arithmetic operations.
* @return Q96 The constant value 2 ** 96.
*/
function Q96() external view returns (uint256);
/**
* @dev Calculates the target position after rebalance based on the provided strategy parameters and the current market state.
* This function's behavior varies with the chosen strategy type, adapting to market movements and strategic requirements:
*
* StrategyType.Original (Pulse V1):
* This is the classic strategy where the position is actively managed within an interval [tickLower, tickUpper].
* If the market tick moves outside an interval [tickLower + tickNeighborhood, tickUpper - tickNeighborhood],
* a rebalance is triggered to center the position as closely as possible to the current tick, maintaining the same width.
* This ensures the position remains effectively aligned with the market.
*
* StrategyType.LazySyncing:
* Supports active position management within the [tickLower, tickUpper] interval, with rebalancing actions triggered under two scenarios:
* - If the current tick < tickLower, rebalance to a new position closest to the current tick on the right side, with the same width.
* - If the current tick > tickUpper, rebalance to a new position closest to the current tick on the left side, with the same width.
* This strategy aims to realign the position with the market with minimal adjustments.
*
* StrategyType.LazyAscending:
* Similar to LazySyncing but specifically focuses on ascending market conditions. If the current tick is less than tickLower,
* it does not trigger a rebalance. Rebalancing is considered only when the market moves upwards beyond the tickUpper,
* aiming to catch upward trends without reacting to downward movements.
*
* StrategyType.LazyDescending:
* Opposite to LazyAscending, this strategy caters to descending market conditions. If the current tick is greater than tickUpper,
* it does not prompt a rebalance. The strategy focuses on rebalancing when the market descends below tickLower,
* aiming to manage downward trends without reacting to upward movements.
*
* For each strategy, the function evaluates whether rebalancing is necessary based on the current tick's position relative to the strategy's parameters.
* If rebalancing is required, it calculates the target position details, ensuring strategic alignment with the current market conditions.
*
* @param tick The current tick of the market, indicating the instantaneous price level.
* @param tickLower The lower bound tick of the existing position.
* @param tickUpper The upper bound tick of the existing position.
* @param params The strategy parameters defining the rebalancing logic, including strategy type, tick neighborhood, and desired position width.
* @return isRebalanceRequired A boolean indicating if rebalancing is needed based on the current market condition and strategy parameters.
* @return target Details of the target position if rebalancing is required, including new tick bounds and liquidity distribution.
*/
function calculateTarget(
int24 tick,
int24 tickLower,
int24 tickUpper,
StrategyParams memory params
)
external
pure
returns (
bool isRebalanceRequired,
ICore.TargetPositionInfo memory target
);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "../ICore.sol";
interface IRebalanceCallback {
/**
* @dev Executes a callback function for rebalancing.
* @param data The data to be passed to the callback function.
* @param targets An array of target position information.
* @return newAmmPositionIds An array of new AMM position IDs.
*/
function call(
bytes memory data,
ICore.TargetPositionInfo[] memory targets
) external returns (uint256[][] memory newAmmPositionIds);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "../oracles/IOracle.sol";
import "../ICore.sol";
import "./IAmmModule.sol";
interface IStrategyModule {
/**
* @dev Validates the strategy parameters.
* @param params The encoded strategy parameters.
*/
function validateStrategyParams(bytes memory params) external view;
/**
* @dev Retrieves the target information for rebalancing based on the given parameters.
* @param info position information.
* @param ammModule The AMM module.
* @param oracle The oracle.
* @return isRebalanceRequired A boolean indicating whether rebalancing is required.
* @return target The target position information for rebalancing.
*/
function getTargets(
ICore.ManagedPositionInfo memory info,
IAmmModule ammModule,
IOracle oracle
)
external
view
returns (
bool isRebalanceRequired,
ICore.TargetPositionInfo memory target
);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../IAmmModule.sol";
import "../../external/velo/ICLPool.sol";
import "../../external/velo/ICLGauge.sol";
import "../../external/velo/ICLFactory.sol";
import "../../external/velo/INonfungiblePositionManager.sol";
import "../../utils/ICounter.sol";
/**
* @title IVeloAmmModule Interface
* @dev Extension of the IAmmModule interface for interaction with the Velo protocol,
* including functions for handling callback and protocol parameters, as well as accessing
* Velo-specific contracts and settings.
*/
interface IVeloAmmModule is IAmmModule {
error InvalidFee(); // Thrown when the fee is invalid
error AddressZero(); // Thrown when an address is zero
error InvalidParams(); // Thrown when input parameters are invalid
error InvalidLength(); // Thrown when array lengths are mismatched or invalid
error InvalidGauge(); // Thrown when the gauge is invalid
/**
* @dev Struct representing callback parameters for operations associated with the Velo protocol.
*
* Parameters:
* @param farm Address of the Synthetix farm contract. It acts as a central hub for yield farming activities, interfacing directly
* with users and other contracts to manage and allocate yield farming rewards based on defined criteria.
* @param gauge Address of the Velo gauge contract.
* @param counter Address of a counter contract. This contract is designed for tracking and aggregating
* specific numerical data, such as the total amount of rewards added to the farm. It serves as a
* specialized tool for monitoring and reporting on key metrics that inform decisions and actions within
* the protocol, ensuring transparency and accuracy in reward distribution and other quantifiable activities.
*/
struct CallbackParams {
address farm; // Synthetix farm contract address for yield farming operations
address gauge; // Velo gauge contract address
address counter; // Counter contract address for aggregating and tracking numerical data, such as reward amounts
}
/**
* @dev Struct representing the operational parameters specific to the Velo AMM module.
* These parameters play a crucial role in defining how the module interacts financially
* with the broader ecosystem, including aspects of fee collection and distribution.
* @param treasury The address of the Mellow protocol's treasury. This address is used
* to collect fees generated by the operations within the Velo AMM module.
* @param feeD9 The fee percentage charged by the Velo AMM module.
* This fee is denoted in a fixed-point format with 9 decimal places,
* allowing for precise representation of fee percentages smaller than one percent. For example,
* a `feeD9` value of 10,000,000 represents a fee of 1%, while a value of 1,000,000 represents
* a 0.1% fee.
*/
struct ProtocolParams {
address treasury; // Mellow protocol treasury address for fee collection
uint32 feeD9; // Fee percentage, represented as a fixed-point number with 9 decimal places
}
/**
* @dev Returns 10 ** 9, the base for fixed-point calculations.
* @return uint256 representing 10^9 for fixed-point arithmetic.
*/
function D9() external view returns (uint256);
/**
* @dev Returns the maximum protocol fee allowed within the Velo AMM module.
* @return uint32 maximum protocol fee as a uint32 value.
*/
function MAX_PROTOCOL_FEE() external view returns (uint32);
/**
* @dev Returns the address of the ICLFactory contract used by the Velo protocol.
* @return ICLFactory contract address.
*/
function factory() external view returns (ICLFactory);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "../ICore.sol";
import "../modules/velo/IVeloAmmModule.sol";
import "../modules/velo/IVeloDepositWithdrawModule.sol";
import "../modules/strategies/IPulseStrategyModule.sol";
import "./IVeloDeployFactoryHelper.sol";
/**
* @title IVeloDeployFactory Interface
* @dev Interface for the VeloDeployFactory contract, facilitating the creation of strategies,
* LP wrappers, and managing their configurations for Velo pools.
*/
interface IVeloDeployFactory {
// Custom errors for operation failures
error InvalidParams();
error LpWrapperAlreadyCreated();
struct StrategyCreatedParams {
address pool;
IVeloAmmModule.AmmPosition ammPosition;
IPulseStrategyModule.StrategyParams strategyParams;
address lpWrapper;
address synthetixFarm;
address caller;
}
event StrategyCreated(StrategyCreatedParams params);
/**
* @dev Represents the immutable parameters for the VeloDeployFactory contract.
*/
struct ImmutableParams {
ICore core; // Core contract interface
IPulseStrategyModule strategyModule; // Pulse strategy module contract interface
IVeloAmmModule veloModule; // Velo AMM module contract interface
IVeloDepositWithdrawModule depositWithdrawModule; // Velo deposit/withdraw module contract interface
IVeloDeployFactoryHelper helper; // Helper contract interface for the VeloDeployFactory
}
/**
* @dev Represents the mutable parameters for the VeloDeployFactory contract.
*/
struct MutableParams {
address lpWrapperAdmin; // Admin address for the LP wrapper
address lpWrapperManager; // Manager address for the LP wrapper
address farmOwner; // Owner address for the farm
address farmOperator; // Operator address for the farm (compounder)
uint256 minInitialLiquidity; // Minimum initial liquidity for the LP wrapper
}
/**
* @dev Stores addresses related to a specific pool.
*/
struct PoolAddresses {
address synthetixFarm; // Synthetix farm contract address
address lpWrapper; // LP wrapper contract address
}
/**
* @dev Represents the parameters for configuring a strategy.
*/
struct DeployParams {
int24 tickNeighborhood;
uint32 slippageD9;
uint256 tokenId;
bytes securityParams;
IPulseStrategyModule.StrategyType strategyType;
}
/**
* @dev Updates the mutable parameters of the contract, accessible only to users with the ADMIN_ROLE. This function enables post-deployment
* adjustments to key operational settings, reflecting the evolving nature of protocol management and governance.
*
* @param newMutableParams The new mutable parameters to be applied, including administrative and operational settings crucial for protocol functionality.
* Requirements:
* - Caller must have the ADMIN_ROLE.
*/
function updateMutableParams(
MutableParams memory newMutableParams
) external;
/**
* @dev Creates a strategy for the given deployParams
* @param params DeployParams for the strategy
* @return poolAddresses addresses related to the created pool
*/
function createStrategy(
DeployParams calldata params
) external returns (PoolAddresses memory poolAddresses);
/**
* @dev Maps a pool address to its associated addresses.
* @param pool Pool address
* @return PoolAddresses addresses associated with the pool
*/
function poolToAddresses(
address pool
) external view returns (PoolAddresses memory);
/**
* @dev Removes the addresses associated with a specific pool from the contract's records. This action is irreversible
* and should be performed with caution. Only users with the ADMIN role are authorized to execute this function,
* ensuring that such a critical operation is tightly controlled and aligned with the protocol's governance policies.
*
* Removing a pool's addresses can be necessary for protocol maintenance, updates, or in response to security concerns.
* It effectively unlinks the pool from the factory's management and operational framework, requiring careful consideration
* and alignment with strategic objectives.
*
* @param pool The address of the pool for which associated addresses are to be removed. This could include any contracts
* or entities tied to the pool's operational lifecycle within the Velo ecosystem, such as LP wrappers or strategy modules.
* Requirements:
* - Caller must have the ADMIN role, ensuring that only authorized personnel can alter the protocol's configuration in this manner.
*/
function removeAddressesForPool(address pool) external;
/**
* @dev Retrieves the immutable parameters for the VeloDeployFactory contract.
* @return ImmutableParams Immutable parameters for the VeloDeployFactory
*/
function getImmutableParams()
external
view
returns (ImmutableParams memory);
/**
* @dev Retrieves the mutable parameters for the VeloDeployFactory contract.
* @return MutableParams Mutable parameters for the VeloDeployFactory
*/
function getMutableParams() external view returns (MutableParams memory);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "./ILpWrapper.sol";
interface IVeloDeployFactoryHelper {
/**
* @dev Creates a new LP wrapper contract.
* @param core The address of the core contract.
* @param ammDepositWithdrawModule The address of the AMM deposit/withdraw module contract.
* @param name The name of the LP wrapper contract.
* @param symbol The symbol of the LP wrapper contract.
* @param admin The address of the admin for the LP wrapper contract.
* @param manager The address of the manager contract for auto update of parameters.
* @return ILpWrapper The newly created LP wrapper contract.
*/
function createLpWrapper(
ICore core,
IAmmDepositWithdrawModule ammDepositWithdrawModule,
string memory name,
string memory symbol,
address admin,
address manager,
address pool
) external returns (ILpWrapper);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../IAmmDepositWithdrawModule.sol";
import "../../external/velo/INonfungiblePositionManager.sol";
import "../../external/velo/ICLPool.sol";
/**
* @title IVeloDepositWithdrawModule Interface
* @dev Implements the IAmmDepositWithdrawModule interface specifically for Velo protocol pools,
* facilitating the deposit and withdrawal of liquidity in an efficient and protocol-compliant manner.
*/
interface IVeloDepositWithdrawModule is IAmmDepositWithdrawModule {
/**
* @dev Returns the address of the Velo protocol's non-fungible position manager contract.
* @return INonfungiblePositionManager contract, facilitating interactions
* with Velo protocol's liquidity positions.
*/
function positionManager()
external
view
returns (INonfungiblePositionManager);
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "../external/velo/ICLPool.sol";
import "./IOracle.sol";
/**
* @title VeloOracle
* @dev Implements the IOracle interface specifically for Velo pools, providing price information and MEV protection functionalities.
*/
interface IVeloOracle is IOracle {
// Custom errors to handle various validation and operational failures
error InvalidLength(); // Thrown when input data length is incorrect
error InvalidParams(); // Thrown when security parameters do not meet expected criteria
error PriceManipulationDetected(); // Thrown when potential price manipulation is detected
error NotEnoughObservations(); // Thrown when there are not enough data points for reliable calculation
/**
* @dev Struct to represent security parameters for the Velo Oracle.
* Defines the criteria for detecting Miner Extractable Value (MEV) manipulations based on historical observations.
* These parameters are crucial for safeguarding against price manipulations by evaluating price movements over time.
*
* In the `ensureNoMEV` function, these parameters are utilized as follows:
* - The function examines the last `lookback + 1` observations, which contain cumulative time-weighted ticks.
* - From these observations, it calculates `lookback` average ticks. Considering the current spot tick, the function then computes `lookback`
* deltas between them.
* - If any of these deltas is greater in magnitude than `maxAllowedDelta`, the function reverts with the `PriceManipulationDetected` error,
* indicating a potential MEV manipulation attempt.
* - If there are insufficient observations at any step of the process, the function reverts with the `NotEnoughObservations` error,
* indicating that the available data is not adequate for a reliable MEV check.
*
* Parameters:
* @param lookback The number of historical observations to analyze, not including the most recent observation.
* This parameter determines the depth of the historical data analysis for MEV detection. The oracle function effectively
* examines `lookback + 1` observations to include the current state in the analysis, offering a comprehensive view of market behavior.
* @param maxAllowedDelta The threshold for acceptable deviation between average ticks within the lookback period and the current tick.
* This value defines the boundary for normal versus manipulative market behavior, serving as a critical parameter in identifying
* potential price manipulations.
* @param maxAge The maximum age of observations to consider for analysis. This parameter ensures that the oracle only
* uses recent observations. Older data points are excluded from the analysis to maintain
* the integrity of the MEV detection mechanism.
*/
struct SecurityParams {
uint16 lookback; // Maximum number of historical data points to consider for analysis
uint32 maxAge; // Maximum age of observations to be used in the analysis
int24 maxAllowedDelta; // Maximum allowed change between data points to be considered valid
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v2;
import {IVotingEscrow} from "./IVotingEscrow.sol";
interface IVoter {
function ve() external view returns (IVotingEscrow);
function vote(
uint256 _tokenId,
address[] calldata _poolVote,
uint256[] calldata _weights
) external;
function gauges(address _pool) external view returns (address);
function gaugeToFees(address _gauge) external view returns (address);
function gaugeToBribes(address _gauge) external view returns (address);
function createGauge(
address _poolFactory,
address _pool
) external returns (address);
function distribute(address gauge) external;
/// @dev Utility to distribute to gauges of pools in array.
/// @param _gauges Array of gauges to distribute to.
function distribute(address[] memory _gauges) external;
function isAlive(address _gauge) external view returns (bool);
function killGauge(address _gauge) external;
function emergencyCouncil() external view returns (address);
/// @notice Claim emissions from gauges.
/// @param _gauges Array of gauges to collect emissions from.
function claimRewards(address[] memory _gauges) external;
/// @notice Claim fees for a given NFT.
/// @dev Utility to help batch fee claims.
/// @param _fees Array of FeesVotingReward contracts to collect from.
/// @param _tokens Array of tokens that are used as fees.
/// @param _tokenId Id of veNFT that you wish to claim fees for.
function claimFees(
address[] memory _fees,
address[][] memory _tokens,
uint256 _tokenId
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IVotingEscrow {
function team() external returns (address);
/// @notice Deposit `_value` tokens for `msg.sender` and lock for `_lockDuration`
/// @param _value Amount to deposit
/// @param _lockDuration Number of seconds to lock tokens for (rounded down to nearest week)
/// @return TokenId of created veNFT
function createLock(
uint256 _value,
uint256 _lockDuration
) external returns (uint256);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title Interface for WETH9
interface IWETH9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "../interfaces/external/velo/INonfungiblePositionManager.sol";
import "../interfaces/utils/ILpWrapper.sol";
import "../libraries/external/FullMath.sol";
import "./DefaultAccessControl.sol";
import "./StakingRewards.sol";
import "./VeloDeployFactory.sol";
contract LpWrapper is ILpWrapper, ERC20, DefaultAccessControl {
using SafeERC20 for IERC20;
/// @inheritdoc ILpWrapper
address public immutable positionManager;
/// @inheritdoc ILpWrapper
IAmmDepositWithdrawModule public immutable ammDepositWithdrawModule;
/// @inheritdoc ILpWrapper
ICore public immutable core;
/// @inheritdoc ILpWrapper
IAmmModule public immutable ammModule;
/// @inheritdoc ILpWrapper
IOracle public immutable oracle;
/// @inheritdoc ILpWrapper
uint256 public positionId;
address private immutable _weth;
address private immutable _pool;
VeloDeployFactory private immutable _factory;
uint56 immutable D9 = 10**9;
/**
* @dev Constructor function for the LpWrapper contract.
* @param core_ The address of the ICore contract.
* @param ammDepositWithdrawModule_ The address of the IAmmDepositWithdrawModule contract.
* @param name_ The name of the ERC20 token.
* @param symbol_ The symbol of the ERC20 token.
* @param admin The address of the admin.
* @param weth_ The address of the WETH contract.
* @param factory_ The address of the ALM deploy factory.
* @param pool_ The address of the Pool.
*/
constructor(
ICore core_,
IAmmDepositWithdrawModule ammDepositWithdrawModule_,
string memory name_,
string memory symbol_,
address admin,
address weth_,
address factory_,
address pool_
) ERC20(name_, symbol_) DefaultAccessControl(admin) {
core = core_;
ammModule = core.ammModule();
positionManager = ammModule.positionManager();
oracle = core.oracle();
ammDepositWithdrawModule = ammDepositWithdrawModule_;
_weth = weth_;
_factory = VeloDeployFactory(factory_);
_pool = pool_;
}
/// @inheritdoc ILpWrapper
function initialize(
uint256 positionId_,
uint256 initialTotalSupply
) external {
if (positionId != 0) revert AlreadyInitialized();
if (core.managedPositionAt(positionId_).owner != address(this))
revert Forbidden();
if (initialTotalSupply == 0) revert InsufficientLpAmount();
positionId = positionId_;
_mint(address(this), initialTotalSupply);
}
/// @inheritdoc ILpWrapper
function deposit(
uint256 amount0,
uint256 amount1,
uint256 minLpAmount,
address to,
uint256 deadline
)
external
returns (uint256 actualAmount0, uint256 actualAmount1, uint256 lpAmount)
{
return _deposit(amount0, amount1, minLpAmount, to, to, deadline);
}
/// @inheritdoc ILpWrapper
function getFarm() public view returns (address) {
IVeloDeployFactory.PoolAddresses memory addresses = _factory
.poolToAddresses(_pool);
require(address(addresses.lpWrapper) == address(this));
return addresses.synthetixFarm;
}
/// @inheritdoc ILpWrapper
function depositAndStake(
uint256 amount0,
uint256 amount1,
uint256 minLpAmount,
address to,
uint256 deadline
)
external
returns (uint256 actualAmount0, uint256 actualAmount1, uint256 lpAmount)
{
(actualAmount0, actualAmount1, lpAmount) = _deposit(
amount0,
amount1,
minLpAmount,
address(this),
to,
deadline
);
address farm = getFarm();
_approve(address(this), farm, lpAmount);
StakingRewards(farm).stakeOnBehalf(lpAmount, to);
}
function _deposit(
uint256 amount0,
uint256 amount1,
uint256 minLpAmount,
address lpRecipient,
address to,
uint256 deadline
)
private
returns (uint256 actualAmount0, uint256 actualAmount1, uint256 lpAmount)
{
if (block.timestamp > deadline) revert Deadline();
ICore.ManagedPositionInfo memory info = core.managedPositionAt(
positionId
);
core.withdraw(positionId, address(this));
uint256 n = info.ammPositionIds.length;
IAmmModule.AmmPosition[]
memory positionsBefore = new IAmmModule.AmmPosition[](n);
for (uint256 i = 0; i < n; i++) {
positionsBefore[i] = ammModule.getAmmPosition(
info.ammPositionIds[i]
);
}
uint256[] memory amounts0 = new uint256[](n);
uint256[] memory amounts1 = new uint256[](n);
{
{
(uint160 sqrtPriceX96, ) = oracle.getOraclePrice(info.pool);
for (uint256 i = 0; i < n; i++) {
if (positionsBefore[i].liquidity == 0) continue;
(amounts0[i], amounts1[i]) = ammModule
.getAmountsForLiquidity(
positionsBefore[i].liquidity,
sqrtPriceX96,
positionsBefore[i].tickLower,
positionsBefore[i].tickUpper
);
actualAmount0 += amounts0[i];
actualAmount1 += amounts1[i];
}
}
for (uint256 i = 0; i < n; i++) {
if (actualAmount0 != 0) {
amounts0[i] = FullMath.mulDiv(
amount0,
amounts0[i],
actualAmount0
);
}
if (actualAmount1 != 0) {
amounts1[i] = FullMath.mulDiv(
amount1,
amounts1[i],
actualAmount1
);
}
}
// used to avoid stack too deep error
actualAmount0 = 0;
actualAmount1 = 0;
}
if (amount0 > 0 || amount1 > 0) {
for (uint256 i = 0; i < n; i++) {
if (positionsBefore[i].liquidity == 0) continue;
(bool success, bytes memory response) = address(
ammDepositWithdrawModule
).delegatecall(
abi.encodeWithSelector(
IAmmDepositWithdrawModule.deposit.selector,
info.ammPositionIds[i],
amounts0[i],
amounts1[i],
msg.sender
)
);
if (!success) revert DepositCallFailed();
(uint256 amount0_, uint256 amount1_) = abi.decode(
response,
(uint256, uint256)
);
actualAmount0 += amount0_;
actualAmount1 += amount1_;
}
}
IAmmModule.AmmPosition[]
memory positionsAfter = new IAmmModule.AmmPosition[](n);
for (uint256 i = 0; i < n; i++) {
positionsAfter[i] = ammModule.getAmmPosition(
info.ammPositionIds[i]
);
}
uint256 totalSupply_ = totalSupply();
for (uint256 i = 0; i < n; i++) {
IERC721(positionManager).approve(
address(core),
info.ammPositionIds[i]
);
}
lpAmount = type(uint256).max;
for (uint256 i = 0; i < n; i++) {
if (positionsBefore[i].liquidity == 0) continue;
uint256 currentLpAmount = FullMath.mulDiv(
positionsAfter[i].liquidity - positionsBefore[i].liquidity,
totalSupply_,
positionsBefore[i].liquidity
);
if (lpAmount > currentLpAmount) {
lpAmount = currentLpAmount;
}
}
if (lpAmount < minLpAmount) revert InsufficientLpAmount();
positionId = core.deposit(
ICore.DepositParams({
ammPositionIds: info.ammPositionIds,
owner: info.owner,
slippageD9: info.slippageD9,
callbackParams: info.callbackParams,
strategyParams: info.strategyParams,
securityParams: info.securityParams
})
);
_mint(lpRecipient, lpAmount);
emit Deposit(msg.sender, to, _pool, lpAmount, actualAmount0, actualAmount1);
}
/// @inheritdoc ILpWrapper
function withdraw(
uint256 lpAmount,
uint256 minAmount0,
uint256 minAmount1,
address to,
uint256 deadline
)
external
returns (uint256 amount0, uint256 amount1, uint256 actualLpAmount)
{
return _withdraw(lpAmount, minAmount0, minAmount1, to, deadline);
}
/// @inheritdoc ILpWrapper
function unstakeAndWithdraw(
uint256 lpAmount,
uint256 minAmount0,
uint256 minAmount1,
address to,
uint256 deadline
)
external
returns (uint256 amount0, uint256 amount1, uint256 actualLpAmount)
{
address farm = getFarm();
actualLpAmount = StakingRewards(farm).balanceOf(msg.sender);
if (actualLpAmount > lpAmount) {
actualLpAmount = lpAmount;
}
StakingRewards(farm).withdrawOnBehalf(actualLpAmount, msg.sender);
(amount0, amount1, actualLpAmount) = _withdraw(
actualLpAmount,
minAmount0,
minAmount1,
to,
deadline
);
}
function _withdraw(
uint256 lpAmount,
uint256 minAmount0,
uint256 minAmount1,
address to,
uint256 deadline
)
private
returns (uint256 amount0, uint256 amount1, uint256 actualLpAmount)
{
if (block.timestamp > deadline) revert Deadline();
ICore.ManagedPositionInfo memory info = core.managedPositionAt(
positionId
);
core.withdraw(positionId, address(this));
actualLpAmount = balanceOf(msg.sender);
if (actualLpAmount > lpAmount) {
actualLpAmount = lpAmount;
}
uint256 totalSupply_ = totalSupply();
_burn(msg.sender, actualLpAmount);
{
for (uint256 i = 0; i < info.ammPositionIds.length; i++) {
IERC721(positionManager).approve(
address(core),
info.ammPositionIds[i]
);
IAmmModule.AmmPosition memory position = ammModule
.getAmmPosition(info.ammPositionIds[i]);
uint256 liquidity = FullMath.mulDiv(
position.liquidity,
actualLpAmount,
totalSupply_
);
if (liquidity == 0) continue;
(bool success, bytes memory response) = address(
ammDepositWithdrawModule
).delegatecall(
abi.encodeWithSelector(
IAmmDepositWithdrawModule.withdraw.selector,
info.ammPositionIds[i],
liquidity,
to
)
);
if (!success) revert WithdrawCallFailed();
(uint256 actualAmount0, uint256 actualAmount1) = abi.decode(
response,
(uint256, uint256)
);
amount0 += actualAmount0;
amount1 += actualAmount1;
}
}
if (amount0 < minAmount0 || amount1 < minAmount1) {
revert InsufficientAmounts();
}
positionId = core.deposit(
ICore.DepositParams({
ammPositionIds: info.ammPositionIds,
owner: info.owner,
slippageD9: info.slippageD9,
callbackParams: info.callbackParams,
strategyParams: info.strategyParams,
securityParams: info.securityParams
})
);
_getReward();
emit Withdraw(msg.sender, to, _pool, lpAmount, amount0, amount1);
}
/// @inheritdoc ILpWrapper
function getReward() external {
_getReward();
}
function _getReward() internal {
address farm = getFarm();
StakingRewards(farm).getRewardOnBehalf(msg.sender);
}
/// @inheritdoc ILpWrapper
function earned(address user) external view returns (uint256 amount) {
address farm = getFarm();
return StakingRewards(farm).earned(user);
}
/// @inheritdoc ILpWrapper
function protocolParams()
external
view
returns (IVeloAmmModule.ProtocolParams memory params, uint256 d9)
{
return (
abi.decode(core.protocolParams(), (IVeloAmmModule.ProtocolParams)),
D9
);
}
/// @inheritdoc ILpWrapper
function getInfo()
external
view
returns (uint256 tokenId, PositionData memory data)
{
{
ICore.ManagedPositionInfo memory info = core.managedPositionAt(
positionId
);
if (info.ammPositionIds.length != 1) revert InvalidPositionsCount();
tokenId = info.ammPositionIds[0];
}
(
uint96 nonce,
address operator,
address token0,
address token1,
int24 tickSpacing,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
) = INonfungiblePositionManager(positionManager).positions(tokenId);
data.nonce = nonce;
data.operator = operator;
data.token0 = token0;
data.token1 = token1;
data.tickSpacing = tickSpacing;
data.tickLower = tickLower;
data.tickUpper = tickUpper;
data.liquidity = liquidity;
data.feeGrowthInside0LastX128 = feeGrowthInside0LastX128;
data.feeGrowthInside1LastX128 = feeGrowthInside1LastX128;
data.tokensOwed0 = tokensOwed0;
data.tokensOwed1 = tokensOwed1;
}
/// @inheritdoc ILpWrapper
function setPositionParams(
uint32 slippageD9,
bytes memory callbackParams,
bytes memory strategyParams,
bytes memory securityParams
) external {
_requireAdmin();
core.setPositionParams(
positionId,
slippageD9,
callbackParams,
strategyParams,
securityParams
);
emit PositionParamsSet(
slippageD9,
abi.decode(callbackParams, (IVeloAmmModule.CallbackParams)),
abi.decode(strategyParams, (IPulseStrategyModule.StrategyParams)),
abi.decode(securityParams, (IVeloOracle.SecurityParams))
);
}
/// @inheritdoc ILpWrapper
function emptyRebalance() external {
core.emptyRebalance(positionId);
}
receive() external payable {
uint256 amount = msg.value;
IWETH9(_weth).deposit{value: amount}();
IERC20(_weth).safeTransfer(tx.origin, amount);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @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;
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
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// 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 v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../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 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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @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.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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.isContract(address(token));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
* now has built in overflow checking.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
// Inheritance
contract Owned {
address public owner;
address public nominatedOwner;
constructor(address _owner) {
require(_owner != address(0), "Owner address cannot be 0");
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(
msg.sender == nominatedOwner,
"You must be nominated before you can accept ownership"
);
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
modifier onlyOwner() {
_onlyOwner();
_;
}
function _onlyOwner() private view {
require(
msg.sender == owner,
"Only the contract owner may perform this action"
);
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}
// https://docs.synthetix.io/contracts/source/contracts/pausable
abstract contract Pausable is Owned {
uint public lastPauseTime;
bool public paused;
constructor() {
// This contract is abstract, and thus cannot be instantiated directly
require(owner != address(0), "Owner must be set");
// Paused will be false, and lastPauseTime will be 0 upon initialisation
}
/**
* @notice Change the paused state of the contract
* @dev Only the contract owner may call this.
*/
function setPaused(bool _paused) external onlyOwner {
// Ensure we're actually changing the state before we do anything
if (_paused == paused) {
return;
}
// Set our paused state.
paused = _paused;
// If applicable, set the last pause time.
if (paused) {
lastPauseTime = block.timestamp;
}
// Let everyone know that our pause state has changed.
emit PauseChanged(paused);
}
event PauseChanged(bool isPaused);
modifier notPaused() {
require(
!paused,
"This action cannot be performed while the contract is paused"
);
_;
}
}
// https://docs.synthetix.io/contracts/source/contracts/rewardsdistributionrecipient
abstract contract RewardsDistributionRecipient is Owned {
address public rewardsDistribution;
function notifyRewardAmount(uint256 reward) external virtual;
modifier onlyRewardsDistribution() {
require(
msg.sender == rewardsDistribution,
"Caller is not RewardsDistribution contract"
);
_;
}
function setRewardsDistribution(
address _rewardsDistribution
) external virtual onlyOwner {
rewardsDistribution = _rewardsDistribution;
}
}
// https://docs.synthetix.io/contracts/source/contracts/stakingrewards
contract StakingRewards is
RewardsDistributionRecipient,
ReentrancyGuard,
Pausable
{
using SafeMath for uint256;
using SafeERC20 for IERC20;
/* ========== STATE VARIABLES ========== */
address public rewardsToken;
address public stakingToken;
uint256 public periodFinish = 0;
uint256 public rewardRate = 0;
uint256 public rewardsDuration = 7 days;
uint256 public lastUpdateTime;
uint256 public rewardPerTokenStored;
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public rewards;
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
/* ========== CONSTRUCTOR ========== */
constructor(
address _owner,
address _rewardsDistribution,
address _rewardsToken,
address _stakingToken
) Owned(_owner) {
rewardsToken = _rewardsToken;
stakingToken = _stakingToken;
rewardsDistribution = _rewardsDistribution;
}
/* ========== VIEWS ========== */
function totalSupply() external view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view returns (uint256) {
return _balances[account];
}
function lastTimeRewardApplicable() public view returns (uint256) {
return block.timestamp < periodFinish ? block.timestamp : periodFinish;
}
function rewardPerToken() public view returns (uint256) {
if (_totalSupply == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored.add(
lastTimeRewardApplicable()
.sub(lastUpdateTime)
.mul(rewardRate)
.mul(1e18)
.div(_totalSupply)
);
}
function earned(address account) public view returns (uint256) {
return
_balances[account]
.mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
.div(1e18)
.add(rewards[account]);
}
function getRewardForDuration() external view returns (uint256) {
return rewardRate.mul(rewardsDuration);
}
/* ========== MUTATIVE FUNCTIONS ========== */
function stake(
uint256 amount
) external nonReentrant notPaused updateReward(msg.sender) {
require(amount > 0, "Cannot stake 0");
_totalSupply = _totalSupply.add(amount);
_balances[msg.sender] = _balances[msg.sender].add(amount);
IERC20(stakingToken).safeTransferFrom(
msg.sender,
address(this),
amount
);
emit Staked(msg.sender, amount);
}
function stakeOnBehalf(
uint256 amount,
address to
) external nonReentrant notPaused updateReward(to) {
require(msg.sender == stakingToken, "Forbidden");
require(amount > 0, "Cannot stake 0");
_totalSupply = _totalSupply.add(amount);
_balances[to] = _balances[to].add(amount);
IERC20(stakingToken).safeTransferFrom(
stakingToken,
address(this),
amount
);
emit Staked(to, amount);
}
function withdraw(
uint256 amount
) public nonReentrant updateReward(msg.sender) {
require(amount > 0, "Cannot withdraw 0");
_totalSupply = _totalSupply.sub(amount);
_balances[msg.sender] = _balances[msg.sender].sub(amount);
IERC20(stakingToken).safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
function withdrawOnBehalf(
uint256 amount,
address to
) public nonReentrant updateReward(to) {
require(msg.sender == stakingToken, "Forbidden");
require(amount > 0, "Cannot withdraw 0");
_totalSupply = _totalSupply.sub(amount);
_balances[to] = _balances[to].sub(amount);
IERC20(stakingToken).safeTransfer(to, amount);
emit Withdrawn(to, amount);
}
function getReward() public nonReentrant updateReward(msg.sender) {
uint256 reward = rewards[msg.sender];
if (reward > 0) {
rewards[msg.sender] = 0;
IERC20(rewardsToken).safeTransfer(msg.sender, reward);
emit RewardPaid(msg.sender, reward);
}
}
function getRewardOnBehalf(
address to
) public nonReentrant updateReward(to) {
require(msg.sender == stakingToken, "Forbidden");
uint256 reward = rewards[to];
if (reward > 0) {
rewards[to] = 0;
IERC20(rewardsToken).safeTransfer(to, reward);
emit RewardPaid(to, reward);
}
}
function exit() external {
withdraw(_balances[msg.sender]);
getReward();
}
/* ========== RESTRICTED FUNCTIONS ========== */
function notifyRewardAmount(
uint256 reward
) external override onlyRewardsDistribution updateReward(address(0)) {
if (block.timestamp >= periodFinish) {
rewardRate = reward.div(rewardsDuration);
} else {
uint256 remaining = periodFinish.sub(block.timestamp);
uint256 leftover = remaining.mul(rewardRate);
rewardRate = reward.add(leftover).div(rewardsDuration);
}
// Ensure the provided reward amount is not more than the balance in the contract.
// This keeps the reward rate in the right range, preventing overflows due to
// very high values of rewardRate in the earned and rewardsPerToken functions;
// Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.
uint balance = IERC20(rewardsToken).balanceOf(address(this));
require(
rewardRate <= balance.div(rewardsDuration),
"Provided reward too high"
);
lastUpdateTime = block.timestamp;
periodFinish = block.timestamp.add(rewardsDuration);
emit RewardAdded(reward);
}
// Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
function recoverERC20(
address tokenAddress,
uint256 tokenAmount
) external onlyOwner {
require(
tokenAddress != address(stakingToken),
"Cannot withdraw the staking token"
);
IERC20(tokenAddress).safeTransfer(owner, tokenAmount);
emit Recovered(tokenAddress, tokenAmount);
}
function setRewardsDuration(uint256 _rewardsDuration) external onlyOwner {
require(
block.timestamp > periodFinish,
"Previous rewards period must be complete before changing the duration for the new period"
);
rewardsDuration = _rewardsDuration;
emit RewardsDurationUpdated(rewardsDuration);
}
/* ========== MODIFIERS ========== */
modifier updateReward(address account) {
rewardPerTokenStored = rewardPerToken();
lastUpdateTime = lastTimeRewardApplicable();
if (account != address(0)) {
rewards[account] = earned(account);
userRewardPerTokenPaid[account] = rewardPerTokenStored;
}
_;
}
/* ========== EVENTS ========== */
event RewardAdded(uint256 reward);
event Staked(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
event RewardPaid(address indexed user, uint256 reward);
event RewardsDurationUpdated(uint256 newDuration);
event Recovered(address token, uint256 amount);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.0;
import "../interfaces/utils/IVeloDeployFactory.sol";
import "./Counter.sol";
import "./DefaultAccessControl.sol";
import "./StakingRewards.sol";
contract VeloDeployFactory is
DefaultAccessControl,
IERC721Receiver,
IVeloDeployFactory
{
mapping(address => PoolAddresses) private _poolToAddresses;
ImmutableParams private _immutableParams;
MutableParams private _mutableParams;
constructor(
address admin_,
ICore core_,
IVeloDepositWithdrawModule ammDepositWithdrawModule_,
IVeloDeployFactoryHelper helper_
) DefaultAccessControl(admin_) {
_immutableParams = ImmutableParams({
core: core_,
strategyModule: IPulseStrategyModule(
address(core_.strategyModule())
),
veloModule: IVeloAmmModule(address(core_.ammModule())),
depositWithdrawModule: ammDepositWithdrawModule_,
helper: helper_
});
}
/// @inheritdoc IVeloDeployFactory
function updateMutableParams(
MutableParams memory newMutableParams
) external {
_requireAdmin();
if (
newMutableParams.farmOperator == address(0) ||
newMutableParams.farmOwner == address(0) ||
newMutableParams.lpWrapperAdmin == address(0) ||
newMutableParams.minInitialLiquidity == 0
) revert InvalidParams();
_mutableParams = newMutableParams;
}
/// @inheritdoc IVeloDeployFactory
function createStrategy(
DeployParams calldata params
) external returns (PoolAddresses memory poolAddresses) {
_requireAtLeastOperator();
if (
params.slippageD9 == 0 ||
params.securityParams.length == 0 ||
params.tokenId == 0
) {
revert InvalidParams();
}
ImmutableParams memory immutableParams = _immutableParams;
MutableParams memory mutableParams = _mutableParams;
ICore core = immutableParams.core;
IAmmModule.AmmPosition memory position = immutableParams
.veloModule
.getAmmPosition(params.tokenId);
if (position.liquidity < mutableParams.minInitialLiquidity)
revert InvalidParams();
ICLPool pool = ICLPool(
immutableParams.veloModule.getPool(
position.token0,
position.token1,
position.property
)
);
if (_poolToAddresses[address(pool)].lpWrapper != address(0)) {
revert LpWrapperAlreadyCreated();
}
core.oracle().ensureNoMEV(address(pool), params.securityParams);
IPulseStrategyModule.StrategyParams
memory strategyParams = IPulseStrategyModule.StrategyParams({
tickNeighborhood: params.tickNeighborhood,
tickSpacing: int24(position.property),
strategyType: params.strategyType,
width: position.tickUpper - position.tickLower
});
{
(, int24 tick, , , , ) = pool.slot0();
(
bool isRebalanceRequired,
ICore.TargetPositionInfo memory target
) = immutableParams.strategyModule.calculateTarget(
tick,
0,
0,
strategyParams
);
assert(isRebalanceRequired);
if (
position.tickLower != target.lowerTicks[0] ||
position.tickUpper != target.upperTicks[0]
)
revert(
string(
abi.encodePacked(
"Invalid ticks. expected: {",
Strings.toString(target.lowerTicks[0]),
", ",
Strings.toString(target.upperTicks[0]),
"}, actual: {",
Strings.toString(position.tickLower),
", ",
Strings.toString(position.tickUpper),
"}, spot: ",
Strings.toString(tick)
)
)
);
}
ILpWrapper lpWrapper = immutableParams.helper.createLpWrapper(
core,
immutableParams.depositWithdrawModule,
string(
abi.encodePacked(
"MellowVelodromeStrategy-",
IERC20Metadata(position.token0).symbol(),
"-",
IERC20Metadata(position.token1).symbol(),
"-",
Strings.toString(position.property)
)
),
string(
abi.encodePacked(
"MVS-",
IERC20Metadata(position.token0).symbol(),
"-",
IERC20Metadata(position.token1).symbol(),
"-",
Strings.toString(position.property)
)
),
mutableParams.lpWrapperAdmin,
mutableParams.lpWrapperManager,
address(pool)
);
ICore.DepositParams memory depositParams;
{
depositParams.securityParams = params.securityParams;
depositParams.slippageD9 = params.slippageD9;
depositParams.ammPositionIds = new uint256[](1);
depositParams.ammPositionIds[0] = params.tokenId;
depositParams.owner = address(lpWrapper);
poolAddresses.lpWrapper = address(lpWrapper);
address gauge = pool.gauge();
address rewardToken = ICLGauge(gauge).rewardToken();
poolAddresses.synthetixFarm = address(
new StakingRewards(
mutableParams.farmOwner,
mutableParams.farmOperator,
rewardToken,
address(lpWrapper)
)
);
depositParams.callbackParams = abi.encode(
IVeloAmmModule.CallbackParams({
farm: poolAddresses.synthetixFarm,
gauge: address(gauge),
counter: address(
new Counter(
mutableParams.farmOperator,
address(core),
rewardToken,
poolAddresses.synthetixFarm
)
)
})
);
depositParams.strategyParams = abi.encode(strategyParams);
_poolToAddresses[address(pool)] = poolAddresses;
}
INonfungiblePositionManager positionManager = INonfungiblePositionManager(
immutableParams.veloModule.positionManager()
);
positionManager.transferFrom(msg.sender, address(this), params.tokenId);
positionManager.approve(address(core), params.tokenId);
lpWrapper.initialize(core.deposit(depositParams), position.liquidity);
emit StrategyCreated(
StrategyCreatedParams({
pool: address(pool),
ammPosition: core.ammModule().getAmmPosition(params.tokenId),
strategyParams: strategyParams,
lpWrapper: poolAddresses.lpWrapper,
synthetixFarm: poolAddresses.synthetixFarm,
caller: msg.sender
})
);
}
/// @inheritdoc IERC721Receiver
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external pure returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
/// @inheritdoc IVeloDeployFactory
function poolToAddresses(
address pool
) external view returns (PoolAddresses memory) {
return _poolToAddresses[pool];
}
/// @inheritdoc IVeloDeployFactory
function getImmutableParams()
external
view
returns (ImmutableParams memory)
{
return _immutableParams;
}
/// @inheritdoc IVeloDeployFactory
function getMutableParams() external view returns (MutableParams memory) {
return _mutableParams;
}
/// @inheritdoc IVeloDeployFactory
function removeAddressesForPool(address pool) external {
_requireAdmin();
delete _poolToAddresses[pool];
}
}
{
"compilationTarget": {
"src/utils/LpWrapper.sol": "LpWrapper"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/",
":@gnosis.pm/safe-contracts/=lib/safe-contracts/",
":@mellow-vaults/=lib/mellow-vaults/",
":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@solmate/=lib/solmate/",
":@synthetix/=lib/synthetix/",
":@zodiac/=lib/zodiac/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/",
":safe-contracts/=lib/safe-contracts/",
":synthetix/=lib/synthetix/contracts/",
":zodiac/=lib/zodiac/contracts/"
]
}
[{"inputs":[{"internalType":"contract ICore","name":"core_","type":"address"},{"internalType":"contract IAmmDepositWithdrawModule","name":"ammDepositWithdrawModule_","type":"address"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"weth_","type":"address"},{"internalType":"address","name":"factory_","type":"address"},{"internalType":"address","name":"pool_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"Deadline","type":"error"},{"inputs":[],"name":"DepositCallFailed","type":"error"},{"inputs":[],"name":"Forbidden","type":"error"},{"inputs":[],"name":"InsufficientAmounts","type":"error"},{"inputs":[],"name":"InsufficientLpAmount","type":"error"},{"inputs":[],"name":"InvalidPositionsCount","type":"error"},{"inputs":[],"name":"WithdrawCallFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint56","name":"slippageD9","type":"uint56"},{"components":[{"internalType":"address","name":"farm","type":"address"},{"internalType":"address","name":"gauge","type":"address"},{"internalType":"address","name":"counter","type":"address"}],"indexed":false,"internalType":"struct IVeloAmmModule.CallbackParams","name":"callbackParams","type":"tuple"},{"components":[{"internalType":"enum IPulseStrategyModule.StrategyType","name":"strategyType","type":"uint8"},{"internalType":"int24","name":"tickNeighborhood","type":"int24"},{"internalType":"int24","name":"tickSpacing","type":"int24"},{"internalType":"int24","name":"width","type":"int24"}],"indexed":false,"internalType":"struct IPulseStrategyModule.StrategyParams","name":"strategyParams","type":"tuple"},{"components":[{"internalType":"uint16","name":"lookback","type":"uint16"},{"internalType":"uint32","name":"maxAge","type":"uint32"},{"internalType":"int24","name":"maxAllowedDelta","type":"int24"}],"indexed":false,"internalType":"struct IVeloOracle.SecurityParams","name":"securityParams","type":"tuple"}],"name":"PositionParamsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"ADMIN_DELEGATE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ammDepositWithdrawModule","outputs":[{"internalType":"contract IAmmDepositWithdrawModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ammModule","outputs":[{"internalType":"contract IAmmModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"core","outputs":[{"internalType":"contract ICore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"minLpAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"actualAmount0","type":"uint256"},{"internalType":"uint256","name":"actualAmount1","type":"uint256"},{"internalType":"uint256","name":"lpAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"minLpAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"depositAndStake","outputs":[{"internalType":"uint256","name":"actualAmount0","type":"uint256"},{"internalType":"uint256","name":"actualAmount1","type":"uint256"},{"internalType":"uint256","name":"lpAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emptyRebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getFarm","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInfo","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"int24","name":"tickSpacing","type":"int24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"},{"internalType":"uint128","name":"tokensOwed0","type":"uint128"},{"internalType":"uint128","name":"tokensOwed1","type":"uint128"}],"internalType":"struct ILpWrapper.PositionData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId_","type":"uint256"},{"internalType":"uint256","name":"initialTotalSupply","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolParams","outputs":[{"components":[{"internalType":"address","name":"treasury","type":"address"},{"internalType":"uint32","name":"feeD9","type":"uint32"}],"internalType":"struct IVeloAmmModule.ProtocolParams","name":"params","type":"tuple"},{"internalType":"uint256","name":"d9","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"slippageD9","type":"uint32"},{"internalType":"bytes","name":"callbackParams","type":"bytes"},{"internalType":"bytes","name":"strategyParams","type":"bytes"},{"internalType":"bytes","name":"securityParams","type":"bytes"}],"name":"setPositionParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpAmount","type":"uint256"},{"internalType":"uint256","name":"minAmount0","type":"uint256"},{"internalType":"uint256","name":"minAmount1","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"unstakeAndWithdraw","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"actualLpAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpAmount","type":"uint256"},{"internalType":"uint256","name":"minAmount0","type":"uint256"},{"internalType":"uint256","name":"minAmount1","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"actualLpAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]