Contract Source Code:
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Internal function that returns the initialized version. Returns `_initialized`
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Internal function that returns the initialized version. Returns `_initializing`
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @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 ReentrancyGuardUpgradeable is Initializable {
// 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;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_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 This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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 IERC20PermitUpgradeable {
/**
* @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.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @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 (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../extensions/draft-IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
using AddressUpgradeable for address;
function safeTransfer(
IERC20Upgradeable token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20Upgradeable 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(
IERC20Upgradeable 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));
}
function safeIncreaseAllowance(
IERC20Upgradeable token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20Upgradeable token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20PermitUpgradeable 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(IERC20Upgradeable 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");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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
* ====
*
* [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://diligence.consensys.net/posts/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.5.11/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 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;
import "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/**
* @title The interface for the Algebra Factory
* @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
* https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
*/
interface IAlgebraFactory {
/**
* @notice Emitted when the owner of the factory is changed
* @param newOwner The owner after the owner was changed
*/
event Owner(address indexed newOwner);
/**
* @notice Emitted when the vault address is changed
* @param newVaultAddress The vault address after the address was changed
*/
event VaultAddress(address indexed newVaultAddress);
/**
* @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 pool The address of the created pool
*/
event Pool(address indexed token0, address indexed token1, address pool);
/**
* @notice Emitted when the farming address is changed
* @param newFarmingAddress The farming address after the address was changed
*/
event FarmingAddress(address indexed newFarmingAddress);
event FeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
);
/**
* @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 poolDeployerAddress
* @return The address of the poolDeployer
*/
function poolDeployer() external view returns (address);
/**
* @dev Is retrieved from the pools to restrict calling
* certain functions not by a tokenomics contract
* @return The tokenomics contract address
*/
function farmingAddress() external view returns (address);
function vaultAddress() external view returns (address);
/**
* @notice Returns the pool address for a given pair of tokens and a fee, 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
* @return pool The pool address
*/
function poolByPair(address tokenA, address tokenB) external view returns (address pool);
/**
* @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
* @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
* from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
* are invalid.
* @return pool The address of the newly created pool
*/
function createPool(address tokenA, address tokenB) 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;
/**
* @dev updates tokenomics address on the factory
* @param _farmingAddress The new tokenomics contract address
*/
function setFarmingAddress(address _farmingAddress) external;
/**
* @dev updates vault address on the factory
* @param _vaultAddress The new vault contract address
*/
function setVaultAddress(address _vaultAddress) external;
/**
* @notice Changes initial fee configuration for new pools
* @dev changes coefficients for sigmoids: α / (1 + e^( (β-x) / γ))
* alpha1 + alpha2 + baseFee (max possible fee) must be <= type(uint16).max
* gammas must be > 0
* @param alpha1 max value of the first sigmoid
* @param alpha2 max value of the second sigmoid
* @param beta1 shift along the x-axis for the first sigmoid
* @param beta2 shift along the x-axis for the second sigmoid
* @param gamma1 horizontal stretch factor for the first sigmoid
* @param gamma2 horizontal stretch factor for the second sigmoid
* @param volumeBeta shift along the x-axis for the outer volume-sigmoid
* @param volumeGamma horizontal stretch factor the outer volume-sigmoid
* @param baseFee minimum possible fee
*/
function setBaseFeeConfiguration(
uint16 alpha1,
uint16 alpha2,
uint32 beta1,
uint32 beta2,
uint16 gamma1,
uint16 gamma2,
uint32 volumeBeta,
uint16 volumeGamma,
uint16 baseFee
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IBribe {
function _deposit(uint amount, uint tokenId) external;
function _withdraw(uint amount, uint tokenId) external;
function getRewardForOwner(uint tokenId, address[] memory tokens) external;
function getRewardForAddress(address _owner, address[] memory tokens) external;
function notifyRewardAmount(address token, uint amount) external;
function left(address token) external view returns (uint);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IBribeFactory {
function createInternalBribe(address[] memory) external returns (address);
function createExternalBribe(address[] memory) external returns (address);
function createBribe(address _owner,address _token0,address _token1, string memory _type) external returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IERC20 {
function totalSupply() external view returns (uint256);
function transfer(address recipient, uint amount) external returns (bool);
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
function balanceOf(address) external view returns (uint);
function transferFrom(address sender, address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IGauge {
function notifyRewardAmount(address token, uint amount) external;
function getReward(address account, address[] memory tokens) external;
function getReward(address account) external;
function claimFees() external returns (uint claimed0, uint claimed1);
function left(address token) external view returns (uint);
function rewardRate(address _pair) external view returns (uint);
function balanceOf(address _account) external view returns (uint);
function isForPair() external view returns (bool);
function totalSupply() external view returns (uint);
function earned(address token, address account) external view returns (uint);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IGaugeFactory {
function createGauge(address, address, address, address, bool, address[] memory) external returns (address);
function createGaugeV2(address _rewardToken,address _ve,address _token,address _distribution, address _internal_bribe, address _external_bribe, bool _isPair) external returns (address) ;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IMasterchef {
struct PoolInfo {
uint256 accRewardPerShare;
uint256 accRewardPerShareExtra;
uint256 lastRewardTime;
}
function setDistributionRate(uint256 amount) external;
function setDistributionRateExtra(uint256 amount) external;
function updatePool() external returns (PoolInfo memory pool);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IMinter {
function update_period() external returns (uint);
function check() external view returns(bool);
function period() external view returns(uint);
function active_period() external view returns(uint);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IPairFactory {
function allPairsLength() external view returns (uint);
function isPair(address pair) external view returns (bool);
function allPairs(uint index) external view returns (address);
function stakingFeeHandler() external view returns (address);
function dibs() external view returns (address);
function MAX_TREASURY_FEE() external view returns (uint256);
function stakingNFTFee() external view returns (uint256);
function isPaused() external view returns (bool);
function pairCodeHash() external pure returns (bytes32);
function getPair(address tokenA, address token, bool stable) external view returns (address);
function createPair(address tokenA, address tokenB, bool stable) external returns (address pair);
function getInitializable() external view returns (address, address, bool);
function getFee(bool _stable) external view returns(uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IPairInfo {
function token0() external view returns(address);
function reserve0() external view returns(uint);
function decimals0() external view returns(uint);
function token1() external view returns(address);
function reserve1() external view returns(uint);
function decimals1() external view returns(uint);
function isPair(address _pair) external view returns(bool);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IPermissionsRegistry {
function emergencyCouncil() external view returns(address);
function swpxTeamMultisig() external view returns(address);
function hasRole(bytes memory role, address caller) external view returns(bool);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IVotingEscrow {
struct Point {
int128 bias;
int128 slope; // # -dweight / dt
uint256 ts;
uint256 blk; // block
}
struct LockedBalance {
int128 amount;
uint start;
uint end;
}
function create_lock_for(uint _value, uint _lock_duration, address _to) external returns (uint);
function locked(uint id) external view returns(LockedBalance memory);
function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint);
function token() external view returns (address);
function team() external returns (address);
function epoch() external view returns (uint);
function point_history(uint loc) external view returns (Point memory);
function user_point_history(uint tokenId, uint loc) external view returns (Point memory);
function user_point_epoch(uint tokenId) external view returns (uint);
function ownerOf(uint) external view returns (address);
function isApprovedOrOwner(address, uint) external view returns (bool);
function transferFrom(address, address, uint) external;
function voted(uint) external view returns (bool);
function attachments(uint) external view returns (uint);
function voting(uint tokenId) external;
function abstain(uint tokenId) external;
function attach(uint tokenId) external;
function detach(uint tokenId) external;
function checkpoint() external;
function deposit_for(uint tokenId, uint value) external;
function balanceOfAtNFT(uint _tokenId, uint _block) external view returns (uint);
function balanceOfNFT(uint _id) external view returns (uint);
function balanceOf(address _owner) external view returns (uint);
function totalSupply() external view returns (uint);
function supply() external view returns (uint);
function decimals() external view returns(uint8);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
library Constants {
uint256 internal constant EPOCH_LENGTH = 30 minutes; //7 days;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
library Math {
function max(uint a, uint b) internal pure returns (uint) {
return a >= b ? a : b;
}
function min(uint a, uint b) internal pure returns (uint) {
return a < b ? a : b;
}
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
function cbrt(uint256 n) internal pure returns (uint256) { unchecked {
uint256 x = 0;
for (uint256 y = 1 << 255; y > 0; y >>= 3) {
x <<= 1;
uint256 z = 3 * x * (x + 1) + 1;
if (n / y >= z) {
n -= y * z;
x += 1;
}
}
return x;
}}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "./libraries/Math.sol";
import "./libraries/Constants.sol";
import "./interfaces/IBribe.sol";
import "./interfaces/IBribeFactory.sol";
import "./interfaces/IGauge.sol";
import "./interfaces/IGaugeFactory.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/IMinter.sol";
import "./interfaces/IPairInfo.sol";
import "./interfaces/IPairFactory.sol";
import "./interfaces/IVotingEscrow.sol";
import "./interfaces/IPermissionsRegistry.sol";
import "./interfaces/IAlgebraFactory.sol";
import "./interfaces/IMasterchef.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
interface IHypervisor {
function pool() external view returns (address);
}
contract VoterV3 is OwnableUpgradeable, ReentrancyGuardUpgradeable {
using SafeERC20Upgradeable for IERC20Upgradeable;
uint256 internal firstActiveEpoch;
address public _ve; // the ve token that governs these contracts
address public factory; // classic stable and volatile Pair Factory
address[] internal _factories; // Array with all the pair factories
address internal base; // $SWPx token
address[] internal _gaugeFactories; // array with all the gauge factories
address public bribefactory; // bribe factory (internal and external)
address public minter; // minter mints $SWPx each epoch
address public permissionRegistry; // registry to check accesses
address[] public pools; // all pools viable for incentives
address public masterChef;
uint256 internal index; // gauge index
uint256 internal immutable DURATION = Constants.EPOCH_LENGTH; // rewards are released over 7 days
uint256 public VOTE_DELAY; // delay between votes in seconds
uint256 public immutable MAX_VOTE_DELAY = Constants.EPOCH_LENGTH; // Max vote delay allowed
mapping(uint256 => uint256) internal timestampToIndex;
mapping(address => uint256) internal supplyIndex; // gauge => index
mapping(address => uint256) public claimable; // gauge => claimable $SWPx
mapping(address => address) public gauges; // pool => gauge
mapping(address => uint256) public gaugesDistributionTimestmap; // gauge => last Distribution Time
mapping(address => address) public poolForGauge; // gauge => pool
mapping(address => address) public internal_bribes; // gauge => internal bribe (only fees)
mapping(address => address) public external_bribes; // gauge => external bribe (real bribes)
mapping(uint256 => mapping(address => uint256)) public votes; // nft => pool => votes
mapping(uint256 => address[]) public poolVote; // nft => pools
mapping(uint256 => mapping(address => uint256)) internal weightsPerEpoch; // timestamp => pool => weights
mapping(uint256 => uint256) internal totalWeightsPerEpoch; // timestamp => total weights
mapping(uint256 => uint256) public lastVoted; // nft => timestamp of last vote
mapping(address => bool) public isGauge; // gauge => boolean [is a gauge?]
mapping(address => bool) public isAlive; // gauge => boolean [is the gauge alive?]
mapping(address => bool) public isFactory; // factory => boolean [the pair factory exists?]
mapping(address => bool) public isGaugeFactory; // g.factory=> boolean [the gauge factory exists?]
mapping(address => uint256) public hasGaugeKilled; // gauge => epoch [when gauge killed, if > 0; 0 - gauge is alive]
event GaugeCreated(
address indexed gauge,
address creator,
address internal_bribe,
address indexed external_bribe,
address indexed pool
);
event GaugeKilled(address indexed gauge);
event GaugeRevived(address indexed gauge);
event Voted(
address indexed voter,
uint256 tokenId,
uint256 weight,
address pool
);
event Abstained(uint256 tokenId);
event NotifyReward(
address indexed sender,
address indexed reward,
uint256 amount
);
event DistributeReward(
address indexed sender,
address indexed gauge,
uint256 amount
);
event SetMinter(address indexed old, address indexed latest);
event SetBribeFactory(address indexed old, address indexed latest);
event SetPairFactory(address indexed old, address indexed latest);
event SetPermissionRegistry(address indexed old, address indexed latest);
event SetGaugeFactory(address indexed old, address indexed latest);
event SetBribeFor(
bool isInternal,
address indexed old,
address indexed latest,
address indexed gauge
);
event SetVoteDelay(uint256 old, uint256 latest);
event AddFactories(
address indexed pairfactory,
address indexed gaugefactory
);
constructor() {}
function initialize(
address __ve,
address _pairFactory,
address _gaugeFactory,
address _bribes,
address _masterChef
) public initializer {
__Ownable_init();
__ReentrancyGuard_init();
_ve = __ve;
base = IVotingEscrow(__ve).token();
_factories.push(_pairFactory);
isFactory[_pairFactory] = true;
_gaugeFactories.push(_gaugeFactory);
isGaugeFactory[_gaugeFactory] = true;
bribefactory = _bribes;
minter = msg.sender;
permissionRegistry = msg.sender;
masterChef = _masterChef;
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
MODIFIERS
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
modifier VoterAdmin() {
require(
IPermissionsRegistry(permissionRegistry).hasRole(
"VOTER_ADMIN",
msg.sender
),
"VOTER_ADMIN"
);
_;
}
modifier Governance() {
require(
IPermissionsRegistry(permissionRegistry).hasRole(
"GOVERNANCE",
msg.sender
),
"GOVERNANCE"
);
_;
}
/// @notice initialize the voter contract
// / @param _tokens array of tokens to whitelist
/// @param _minter the minter of $SWPx
function _init(address _permissionsRegistry, address _minter) external {
require(
msg.sender == minter ||
IPermissionsRegistry(permissionRegistry).hasRole(
"VOTER_ADMIN",
msg.sender
)
);
require(firstActiveEpoch == 0);
minter = _minter;
permissionRegistry = _permissionsRegistry;
firstActiveEpoch = _epochTimestamp();
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
VoterAdmin
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice set vote delay in seconds
function setVoteDelay(uint256 _delay) external VoterAdmin {
require(_delay != VOTE_DELAY, "already set");
require(_delay <= MAX_VOTE_DELAY, "max delay");
emit SetVoteDelay(VOTE_DELAY, _delay);
VOTE_DELAY = _delay;
}
/// @notice Set a new Minter
function setMinter(address _minter) external VoterAdmin {
require(_minter != address(0), "addr0");
require(_minter.code.length > 0, "!contract");
emit SetMinter(minter, _minter);
minter = _minter;
}
/// @notice Set a new Bribe Factory
function setBribeFactory(address _bribeFactory) external VoterAdmin {
require(_bribeFactory.code.length > 0, "!contract");
require(_bribeFactory != address(0), "addr0");
emit SetBribeFactory(bribefactory, _bribeFactory);
bribefactory = _bribeFactory;
}
/// @notice Set a new Pair Factory
function setPairFactory(address _factory) external VoterAdmin {
factory = _factory;
}
/// @notice Set a new PermissionRegistry
function setPermissionsRegistry(
address _permissionRegistry
) external VoterAdmin {
require(_permissionRegistry.code.length > 0, "!contract");
require(_permissionRegistry != address(0), "addr0");
emit SetPermissionRegistry(permissionRegistry, _permissionRegistry);
permissionRegistry = _permissionRegistry;
}
/// @notice Set a new bribes for a given gauge
function setNewBribes(
address _gauge,
address _internal,
address _external
) external VoterAdmin {
require(isGauge[_gauge], "!gauge");
require(_gauge.code.length > 0, "!contract");
_setInternalBribe(_gauge, _internal);
_setExternalBribe(_gauge, _external);
}
/// @notice Set a new internal bribe for a given gauge
function setInternalBribeFor(
address _gauge,
address _internal
) external VoterAdmin {
require(isGauge[_gauge], "!gauge");
_setInternalBribe(_gauge, _internal);
}
/// @notice Set a new External bribe for a given gauge
function setExternalBribeFor(
address _gauge,
address _external
) external VoterAdmin {
require(isGauge[_gauge], "!gauge");
_setExternalBribe(_gauge, _external);
}
function _setInternalBribe(address _gauge, address _internal) private {
require(_internal.code.length > 0, "!contract");
emit SetBribeFor(true, internal_bribes[_gauge], _internal, _gauge);
internal_bribes[_gauge] = _internal;
}
function _setExternalBribe(address _gauge, address _external) private {
require(_external.code.length > 0, "!contract");
emit SetBribeFor(false, internal_bribes[_gauge], _external, _gauge);
external_bribes[_gauge] = _external;
}
function addFactory(
address _pairFactory,
address _gaugeFactory
) external VoterAdmin {
require(_pairFactory != address(0), "addr0");
require(_gaugeFactory != address(0), "addr0");
require(!isFactory[_pairFactory], "fact");
require(!isGaugeFactory[_gaugeFactory], "gFact");
//require(_pairFactory.code.length > 0, "!contract");
require(_gaugeFactory.code.length > 0, "!contract");
_factories.push(_pairFactory);
_gaugeFactories.push(_gaugeFactory);
isFactory[_pairFactory] = true;
isGaugeFactory[_gaugeFactory] = true;
emit AddFactories(_pairFactory, _gaugeFactory);
}
function replaceFactory(
address _pairFactory,
address _gaugeFactory,
uint256 _pos
) external VoterAdmin {
require(_pairFactory != address(0), "addr0");
require(_gaugeFactory != address(0), "addr0");
require(!isFactory[_pairFactory], "fact");
require(!isGaugeFactory[_gaugeFactory], "gFact");
address oldPF = _factories[_pos];
address oldGF = _gaugeFactories[_pos];
isFactory[oldPF] = false;
isGaugeFactory[oldGF] = false;
_factories[_pos] = (_pairFactory);
_gaugeFactories[_pos] = (_gaugeFactory);
isFactory[_pairFactory] = true;
isGaugeFactory[_gaugeFactory] = true;
emit SetGaugeFactory(oldGF, _gaugeFactory);
emit SetPairFactory(oldPF, _pairFactory);
}
function removeFactory(uint256 _pos) external VoterAdmin {
address oldPF = _factories[_pos];
address oldGF = _gaugeFactories[_pos];
require(isFactory[oldPF], "!fact");
require(isGaugeFactory[oldGF], "!gFact");
_factories[_pos] = address(0);
_gaugeFactories[_pos] = address(0);
isFactory[oldPF] = false;
isGaugeFactory[oldGF] = false;
emit SetGaugeFactory(oldGF, address(0));
emit SetPairFactory(oldPF, address(0));
}
function recoverERC20(address token, uint256 amount) external VoterAdmin {
IERC20Upgradeable(token).safeTransfer(msg.sender, amount);
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
GOVERNANCE
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice Kill a malicious gauge
/// @param _gauge gauge to kill
function killGauge(address _gauge) external Governance {
require(isAlive[_gauge], "killed");
isAlive[_gauge] = false;
claimable[_gauge] = 0;
uint _time = _epochTimestamp();
totalWeightsPerEpoch[_time] -= weightsPerEpoch[_time][poolForGauge[_gauge]];
hasGaugeKilled[_gauge] = _time;
emit GaugeKilled(_gauge);
}
/// @notice Revive a malicious gauge
/// @param _gauge gauge to revive
function reviveGauge(address _gauge) external Governance {
require(!isAlive[_gauge], "alive");
require(isGauge[_gauge], 'killed');
require(_epochTimestamp() > hasGaugeKilled[_gauge], "early");
isAlive[_gauge] = true;
delete hasGaugeKilled[_gauge];
emit GaugeRevived(_gauge);
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
USER INTERACTION
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice Reset the votes of a given TokenID
function reset(uint256 _tokenId) external nonReentrant {
_voteDelay(_tokenId);
require(
IVotingEscrow(_ve).isApprovedOrOwner(msg.sender, _tokenId),
"!approved/Owner"
);
_reset(_tokenId);
IVotingEscrow(_ve).abstain(_tokenId);
lastVoted[_tokenId] = _epochTimestamp() + 1;
}
function _reset(uint256 _tokenId) internal {
address[] storage _poolVote = poolVote[_tokenId];
uint256 _poolVoteCnt = _poolVote.length;
uint256 _totalWeight;
uint256 _time = _epochTimestamp();
uint256[] memory _votes = new uint256[](_poolVoteCnt);
address _pool;
unchecked {
for (uint256 i; i < _poolVoteCnt; i++) {
_pool = _poolVote[i];
_votes[i] = votes[_tokenId][_pool];
if (_votes[i] != 0) {
// if user last vote is < than epochTimestamp then votes are 0! IF not underflow occur
if (lastVoted[_tokenId] > _time)
weightsPerEpoch[_time][_pool] -= _votes[i];
votes[_tokenId][_pool] -= _votes[i];
if (lastVoted[_tokenId] > _time) {
IBribe(internal_bribes[gauges[_pool]])._withdraw(
uint256(_votes[i]),
_tokenId
);
IBribe(external_bribes[gauges[_pool]])._withdraw(
uint256(_votes[i]),
_tokenId
);
}
// if is alive remove _votes, else don't because we already done it in killGauge()
if (isAlive[gauges[_pool]]) _totalWeight += _votes[i];
}
}
}
// if user last vote is < than epochTimestamp then _totalWeight is 0! IF not underflow occur
if (lastVoted[_tokenId] < _time) _totalWeight = 0;
totalWeightsPerEpoch[_time] -= _totalWeight;
delete poolVote[_tokenId];
emit Abstained(_tokenId);
}
/// @notice Recast the saved votes of a given TokenID
function poke(uint256 _tokenId) external nonReentrant {
_voteDelay(_tokenId);
require(
IVotingEscrow(_ve).isApprovedOrOwner(msg.sender, _tokenId),
"!approved/Owner"
);
address[] memory _poolVote = poolVote[_tokenId];
uint256 _poolCnt = _poolVote.length;
uint256[] memory _weights = new uint256[](_poolCnt);
for (uint256 i = 0; i < _poolCnt; i++) {
_weights[i] = votes[_tokenId][_poolVote[i]];
}
_vote(_tokenId, _poolVote, _weights);
lastVoted[_tokenId] = _epochTimestamp() + 1;
}
/// @notice Vote for pools
/// @param _tokenId veNFT tokenID used to vote
/// @param _poolVote array of LPs addresses to vote (eg.: [sAMM usdc-usdt , sAMM dai-usdt, vAMM wsonic-SWPx ,...])
/// @param _weights array of weights for each LPs (eg.: [10 , 90 , 45 ,...])
function vote(
uint256 _tokenId,
address[] calldata _poolVote,
uint256[] calldata _weights
) external nonReentrant {
_voteDelay(_tokenId);
require(
IVotingEscrow(_ve).isApprovedOrOwner(msg.sender, _tokenId),
"!approved/Owner"
);
require(_poolVote.length == _weights.length, "Pool/Weights length !=");
_vote(_tokenId, _poolVote, _weights);
lastVoted[_tokenId] = _epochTimestamp() + 1;
}
function _vote(
uint256 _tokenId,
address[] memory _poolVote,
uint256[] memory _weights
) internal {
_reset(_tokenId);
uint256 _poolCnt = _poolVote.length;
uint256 _weight = IVotingEscrow(_ve).balanceOfNFT(_tokenId);
uint256 _totalVoteWeight = 0;
uint256 _totalWeight = 0;
uint256 _usedWeight = 0;
uint256 _time = _epochTimestamp();
for (uint i = 0; i < _poolCnt; i++) {
if (isAlive[gauges[_poolVote[i]]])
_totalVoteWeight += _weights[i];
}
for (uint256 i = 0; i < _poolCnt; i++) {
address _pool = _poolVote[i];
address _gauge = gauges[_pool];
if (isGauge[_gauge] && isAlive[_gauge]) {
uint256 _poolWeight = (_weights[i] * _weight) /
_totalVoteWeight;
require(votes[_tokenId][_pool] == 0);
require(_poolWeight != 0);
poolVote[_tokenId].push(_pool);
weightsPerEpoch[_time][_pool] += _poolWeight;
votes[_tokenId][_pool] += _poolWeight;
IBribe(internal_bribes[_gauge])._deposit(
uint256(_poolWeight),
_tokenId
);
IBribe(external_bribes[_gauge])._deposit(
uint256(_poolWeight),
_tokenId
);
_usedWeight += _poolWeight;
_totalWeight += _poolWeight;
emit Voted(msg.sender, _tokenId, _poolWeight, _pool);
}
}
if (_usedWeight > 0) IVotingEscrow(_ve).voting(_tokenId);
totalWeightsPerEpoch[_time] += _totalWeight;
}
/// @notice claim LP gauge rewards
function claimRewards(address[] memory _gauges) external {
for (uint256 i = 0; i < _gauges.length; i++) {
IGauge(_gauges[i]).getReward(msg.sender);
}
}
/// @notice claim bribes rewards given a TokenID
function claimBribes(
address[] memory _bribes,
address[][] memory _tokens,
uint256 _tokenId
) external {
require(
IVotingEscrow(_ve).isApprovedOrOwner(msg.sender, _tokenId),
"!approved/Owner"
);
for (uint256 i = 0; i < _bribes.length; i++) {
IBribe(_bribes[i]).getRewardForOwner(_tokenId, _tokens[i]);
}
}
/// @notice claim fees rewards given a TokenID
function claimFees(
address[] memory _fees,
address[][] memory _tokens,
uint256 _tokenId
) external {
require(
IVotingEscrow(_ve).isApprovedOrOwner(msg.sender, _tokenId),
"!approved/Owner"
);
for (uint256 i = 0; i < _fees.length; i++) {
IBribe(_fees[i]).getRewardForOwner(_tokenId, _tokens[i]);
}
}
/// @notice claim bribes rewards given an address
function claimBribes(
address[] memory _bribes,
address[][] memory _tokens
) external {
for (uint256 i = 0; i < _bribes.length; i++) {
IBribe(_bribes[i]).getRewardForAddress(msg.sender, _tokens[i]);
}
}
/// @notice claim fees rewards given an address
function claimFees(
address[] memory _bribes,
address[][] memory _tokens
) external {
for (uint256 i = 0; i < _bribes.length; i++) {
IBribe(_bribes[i]).getRewardForAddress(msg.sender, _tokens[i]);
}
}
/// @notice check if user can vote
function _voteDelay(uint256 _tokenId) internal view {
require(
block.timestamp > lastVoted[_tokenId] + VOTE_DELAY,
"ERR: VOTE_DELAY"
);
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
GAUGE CREATION
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice create multiple gauges
function createGauges(
address[] memory _pool,
uint256[] memory _gaugeTypes
)
external
nonReentrant
returns (address[] memory, address[] memory, address[] memory)
{
require(_pool.length == _gaugeTypes.length, "len mismatch");
require(_pool.length <= 10, "max 10");
address[] memory _gauge = new address[](_pool.length);
address[] memory _int = new address[](_pool.length);
address[] memory _ext = new address[](_pool.length);
uint256 i = 0;
for (i; i < _pool.length; i++) {
(_gauge[i], _int[i], _ext[i]) = _createGauge(
_pool[i],
_gaugeTypes[i]
);
}
return (_gauge, _int, _ext);
}
/// @notice create a gauge
function createGauge(
address _pool,
uint256 _gaugeType
)
external
nonReentrant
returns (
address _gauge,
address _internal_bribe,
address _external_bribe
)
{
(_gauge, _internal_bribe, _external_bribe) = _createGauge(
_pool,
_gaugeType
);
}
/// @notice create a gauge
/// @param _pool LP address
/// @param _gaugeType the type of the gauge you want to create
/// @dev To create stable/Volatile pair gaugeType = 0, Concentrated liqudity = 1, ...
/// Make sure to use the corrcet gaugeType or it will fail
function _createGauge(
address _pool,
uint256 _gaugeType
)
internal
returns (
address _gauge,
address _internal_bribe,
address _external_bribe
)
{
require(
IPermissionsRegistry(permissionRegistry).hasRole(
"GOVERNANCE",
msg.sender
)
);
require(_gaugeType < _factories.length, "gaugetype");
require(gauges[_pool] == address(0x0), "!exists");
require(_pool.code.length > 0, "!contract");
bool isPair;
address _factory = _factories[_gaugeType];
address _gaugeFactory = _gaugeFactories[_gaugeType];
require(_factory != address(0), "addr0");
require(_gaugeFactory != address(0), "addr0");
address tokenA = address(0);
address tokenB = address(0);
(tokenA) = IPairInfo(_pool).token0();
(tokenB) = IPairInfo(_pool).token1();
// for future implementation add isPair() in factory
if (_gaugeType == 0) {
isPair = IPairFactory(_factory).isPair(_pool);
}
if (_gaugeType == 1) {
address _pool_factory = IAlgebraFactory(_factory).poolByPair(
tokenA,
tokenB
);
address _pool_hyper = IHypervisor(_pool).pool();
require(_pool_hyper == _pool_factory, "wrong tokens");
isPair = true;
} else {
//update
//isPair = false;
}
// create internal and external bribe
address _owner = IPermissionsRegistry(permissionRegistry)
.swpxTeamMultisig();
string memory _type = string.concat(
"SwapX LP Fees: ",
IERC20(_pool).symbol()
);
_internal_bribe = IBribeFactory(bribefactory).createBribe(
_owner,
tokenA,
tokenB,
_type
);
_type = string.concat("SwapX Incentives: ", IERC20(_pool).symbol());
_external_bribe = IBribeFactory(bribefactory).createBribe(
_owner,
tokenA,
tokenB,
_type
);
// create gauge
_gauge = IGaugeFactory(_gaugeFactory).createGaugeV2(
base,
_ve,
_pool,
address(this),
_internal_bribe,
_external_bribe,
isPair
);
// approve spending for $SWPx
IERC20(base).approve(_gauge, type(uint256).max);
// save data
internal_bribes[_gauge] = _internal_bribe;
external_bribes[_gauge] = _external_bribe;
gauges[_pool] = _gauge;
poolForGauge[_gauge] = _pool;
isGauge[_gauge] = true;
isAlive[_gauge] = true;
pools.push(_pool);
// update index
supplyIndex[_gauge] = index; // new gauges are set to the default global state
emit GaugeCreated(
_gauge,
msg.sender,
_internal_bribe,
_external_bribe,
_pool
);
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
VIEW FUNCTIONS
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice view the total length of the pools
function length() external view returns (uint256) {
return pools.length;
}
/// @notice view the total length of the voted pools given a tokenId
function poolVoteLength(uint256 tokenId) external view returns (uint256) {
return poolVote[tokenId].length;
}
function factories() external view returns (address[] memory) {
return _factories;
}
function factoryLength() external view returns (uint256) {
return _factories.length;
}
function gaugeFactories() external view returns (address[] memory) {
return _gaugeFactories;
}
function gaugeFactoriesLength() external view returns (uint256) {
return _gaugeFactories.length;
}
function weights(address _pool) public view returns (uint256) {
uint256 _time = _epochTimestamp();
return weightsPerEpoch[_time][_pool];
}
function weightsAt(
address _pool,
uint256 _time
) public view returns (uint256) {
return weightsPerEpoch[_time][_pool];
}
function totalWeight() public view returns (uint256) {
uint256 _time = _epochTimestamp();
return totalWeightsPerEpoch[_time];
}
function totalWeightAt(uint256 _time) public view returns (uint256) {
return totalWeightsPerEpoch[_time];
}
function _epochTimestamp() public view returns (uint256) {
uint256 active_period = IMinter(minter).active_period();
require(active_period != 0, "Not started epochs");
return active_period;
}
function indexAt(uint256 _time) public view returns (uint256) {
return timestampToIndex[_time];
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
DISTRIBUTION
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice notify reward amount for gauge
/// @dev the function is called by the minter each epoch. Anyway anyone can top up some extra rewards.
/// @param amount amount to distribute
function notifyRewardAmount(uint256 amount) external {
require(msg.sender == minter, "!minter");
uint256 _time = _epochTimestamp() - Constants.EPOCH_LENGTH;
uint256 _totalWeight = totalWeightAt(_time); // minter call notify after updates active_period, loads votes - 1 week
uint256 _ratio = 0;
if (_totalWeight > 0) {
IERC20Upgradeable(base).safeTransferFrom(
msg.sender,
address(this),
amount
);
_ratio = (amount * 1e18) / _totalWeight; // 1e18 adjustment is removed during claim
}
if (_ratio > 0) {
index += _ratio;
timestampToIndex[_time] = index;
}
emit NotifyReward(msg.sender, base, amount);
}
/// @notice distribute the LP Fees to the internal bribes
/// @param _gauges gauge address where to claim the fees
/// @dev the gauge is the owner of the LPs so it has to claim
function distributeFees(address[] memory _gauges) external {
for (uint256 i = 0; i < _gauges.length; i++) {
if (isGauge[_gauges[i]] && isAlive[_gauges[i]]) {
IGauge(_gauges[i]).claimFees();
}
}
}
/// @notice Distribute the emission for ALL gauges
function distributeAll() external nonReentrant {
IMinter(minter).update_period();
IMasterchef(masterChef).updatePool();
uint256 x = 0;
uint256 stop = pools.length;
for (x; x < stop; x++) {
_distribute(gauges[pools[x]]);
}
}
/// @notice distribute the emission for N gauges
/// @param start start index point of the pools array
/// @param finish finish index point of the pools array
/// @dev this function is used in case we have too many pools and gasLimit is reached
function distribute(uint256 start, uint256 finish) public nonReentrant {
IMinter(minter).update_period();
IMasterchef(masterChef).updatePool();
for (uint256 x = start; x < finish; x++) {
_distribute(gauges[pools[x]]);
}
}
/// @notice distribute reward onyl for given gauges
/// @dev this function is used in case some distribution fails
function distribute(address[] memory _gauges) external nonReentrant {
IMinter(minter).update_period();
IMasterchef(masterChef).updatePool();
for (uint256 x = 0; x < _gauges.length; x++) {
_distribute(_gauges[x]);
}
}
/// @notice distribute the emission
function _distribute(address _gauge) internal {
uint256 lastTimestamp = gaugesDistributionTimestmap[_gauge];
uint256 currentTimestamp = _epochTimestamp();
if(lastTimestamp < currentTimestamp){
_updateForAfterDistribution(_gauge); // should set claimable to 0 if killed
uint256 _claimable = claimable[_gauge];
// distribute only if claimable is > 0, currentEpoch != lastepoch and gauge is alive
if (_claimable > 0 && isAlive[_gauge]) {
claimable[_gauge] = 0;
gaugesDistributionTimestmap[_gauge] = currentTimestamp;
IGauge(_gauge).notifyRewardAmount(base, _claimable);
emit DistributeReward(msg.sender, _gauge, _claimable);
}
}
}
/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
HELPERS
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------------------------------------- */
/// @notice update info for gauges
/// @dev this function track the gauge index to emit the correct $SWPx amount after the distribution
function _updateForAfterDistribution(address _gauge) private {
address _pool = poolForGauge[_gauge];
uint256 _time = _epochTimestamp() - Constants.EPOCH_LENGTH;
uint256 _supplied = weightsPerEpoch[_time][_pool];
if (_supplied > 0) {
uint256 _supplyIndex = supplyIndex[_gauge];
uint256 _index = index; // get global index0 for accumulated distro
supplyIndex[_gauge] = _index; // update _gauge current position to global position
uint256 _delta = _index - _supplyIndex; // see if there is any difference that need to be accrued
if (_delta > 0) {
uint256 _share = _supplied * _delta / 1e18; // add accrued difference for each supplied token
if (isAlive[_gauge]) {
claimable[_gauge] += _share;
}
}
} else {
supplyIndex[_gauge] = index; // new users are set to the default global state
}
}
}