Source Code
Overview
HYPE Balance
HYPE Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CompoundEmissionExtensionUpgradeable
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 2000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.19;
import "./interfaces/ICompoundEmissionExtension.sol";
import "./interfaces/IVoter.sol";
import "./interfaces/IVotingEscrow.sol";
import "../bribes/interfaces/IBribe.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {SafeERC20Upgradeable, IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
/**
* @title CompoundEmissionExtensionUpgradeable
* @notice
* This contract serves as an extension on top of a Voter contract to automatically
* compound user emissions into veNFT locks and/or Bribe pools. Users may configure
* how their claimed emissions are allocated among:
* 1) Multiple veNFT locks (via TargetLock[]).
* 2) Multiple bribe pools (via TargetPool[]).
*
* The user can define what fraction (percentage) of their emissions goes to locks
* and what fraction goes to bribe pools. Each fraction’s distribution can further be
* split across multiple targets (locks and/or bribe pools).
*
* The contract also supports creating or depositing into veNFT locks with configurable
* lock parameters, either via a user-specific config or a global default config.
*
* @dev
* - Inherits from {ReentrancyGuardUpgradeable} to protect state-mutating functions
* from reentrancy attacks.
* - Relies on the Voter’s roles to manage who can call certain functions.
* Specifically, only addresses with the COMPOUND_KEEPER_ROLE can perform batch
* compounding on behalf of users.
*/
contract CompoundEmissionExtensionUpgradeable is ICompoundEmissionExtension, ReentrancyGuardUpgradeable {
using SafeERC20Upgradeable for IERC20Upgradeable;
/**
* @notice Precision factor for percentage calculations (1e18 = 100%).
*/
uint256 internal constant _PRECISION = 1e18;
/**
* @notice Role for the keeper responsible for triggering emission compounding
* (e.g., on a regular schedule).
*/
bytes32 public constant COMPOUND_KEEPER_ROLE = keccak256("COMPOUND_KEEPER_ROLE");
/**
* @notice Role for the administrator with permissions to set default create-lock configurations.
*/
bytes32 public constant COMPOUND_EMISSION_EXTENSION_ADMINISTRATOR_ROLE = keccak256("COMPOUND_EMISSION_EXTENSION_ADMINISTRATOR_ROLE");
/**
* @notice The address of the Voter contract from which emissions will be claimed.
*/
address public voter;
/**
* @notice The token being locked (and compounded) in the VotingEscrow contract.
*/
address public token;
/**
* @notice The VotingEscrow contract address where emissions are locked.
*/
address public votingEscrow;
/**
* @notice The default configuration for creating new locks if a user has not set a custom config.
*
* @dev
* - `shouldBoosted` Whether to treat the lock as boosted.
* - `withPermanentLock` Whether this lock is permanently locked.
* - `lockDuration` The duration (in seconds) for the lock (ignored if `withPermanentLock = true`).
* - `managedTokenIdForAttach` An optional existing managed veNFT ID to which this deposit is attached.
*/
CreateLockConfig public defaultCreateLockConfig;
/**
* @notice For each user, the fraction of emissions that should be deposited into veNFT locks (in 1e18 = 100%).
*/
mapping(address => uint256) public getToLocksPercentage;
/**
* @notice For each user, the fraction of emissions that should be deposited into bribe pools (in 1e18 = 100%).
*/
mapping(address => uint256) public getToBribePoolsPercentage;
/**
* @notice Indicates whether a user has a custom `CreateLockConfig` set.
*/
mapping(address => bool) internal _usersCreateLockConfigIsEnable;
/**
* @notice The user’s custom `CreateLockConfig`, if `_usersCreateLockConfigIsEnable[user]` is true.
*/
mapping(address => CreateLockConfig) internal _usersCreateLockConfigs;
/**
* @notice Defines how a user’s allocated portion for veNFT locks is further split among multiple locks.
*
* @dev Each entry includes a `tokenId` of an existing veNFT lock and a `percentage` (1e18 = 100%)
* indicating how that portion is distributed. All `TargetLock[]` for a user must sum to 1e18
* if the user has a nonzero `getToLocksPercentage[user]`.
*/
mapping(address => TargetLock[]) internal _usersCompoundEmissionTargetLocks;
/**
* @notice Defines how a user’s allocated portion for bribe pools is further split among multiple pools.
*
* @dev Each entry includes an address of the pool, and a `percentage` (1e18 = 100%)
* indicating how that portion is distributed. All `TargetPool[]` for a user must sum to 1e18
* if the user has a nonzero `getToBribePoolsPercentage[user]`.
*/
mapping(address => TargetPool[]) internal _usersCompoundEmissionTargetBribesPools;
/**
* @notice Thrown when an invalid lock configuration is provided.
*/
error InvalidCreateLockConfig();
/**
* @notice Thrown when the user sets an invalid emission compounding parameter combination.
*/
error InvalidCompoundEmissionParams();
/**
* @notice Thrown when attempting to set token locks for a veNFT that does not belong to the user.
*/
error AnotherUserTargetLocks();
/**
* @notice Thrown when access is denied for the operation.
*/
error AccessDenied();
/**
* @notice Thrown when attempting to set a target bribe pool that is associated with a killed gauge.
*/
error TargetPoolGaugeIsKilled();
/**
* @dev Restricts execution to addresses holding the specified role in the Voter contract.
* @param role_ The role required for the function call.
*/
modifier onlyRole(bytes32 role_) {
if (!IVoter(voter).hasRole(role_, msg.sender)) {
revert AccessDenied();
}
_;
}
constructor() {
_disableInitializers();
}
/**
* @notice Initializes the compound emission extension (called once).
* @dev
* - Sets default lock duration to approximately 6 months (15724800 seconds).
* - Should be invoked right after deployment.
*
* @param voter_ The address of the Voter contract.
* @param token_ The address of the token being locked in VotingEscrow.
* @param votingEscrow_ The address of the VotingEscrow contract.
*/
function initialize(address voter_, address token_, address votingEscrow_) external initializer {
__ReentrancyGuard_init();
voter = voter_;
token = token_;
votingEscrow = votingEscrow_;
defaultCreateLockConfig = CreateLockConfig(false, false, 15724800, 0);
}
/**
* @notice Sets the global default create-lock configuration. This applies to any user that does not have a custom config.
* @dev Only callable by addresses with the COMPOUND_EMISSION_EXTENSION_ADMINISTRATOR_ROLE role.
* @param config_ The new default `CreateLockConfig`.
*
* Requirements:
* - `config_.lockDuration` must be nonzero if `withPermanentLock` is false.
* - At least one of `withPermanentLock`, `lockDuration`, or `managedTokenIdForAttach` must be set if `shouldBoosted` is false.
*
* Emits a {SetDefaultCreateLockConfig} event.
*/
function setDefaultCreateLockConfig(
CreateLockConfig calldata config_
) external onlyRole(COMPOUND_EMISSION_EXTENSION_ADMINISTRATOR_ROLE) {
if (!config_.withPermanentLock && config_.lockDuration == 0 && config_.managedTokenIdForAttach == 0 && !config_.shouldBoosted) {
revert InvalidCreateLockConfig();
}
if (config_.lockDuration == 0 && !config_.withPermanentLock) {
revert InvalidCreateLockConfig();
}
defaultCreateLockConfig = config_;
emit SetDefaultCreateLockConfig(config_);
}
/**
* @notice Sets or removes a user-specific `CreateLockConfig`.
* @dev
* - If all parameters in `config_` are zero/false, the user's config is removed,
* reverting them to using the default config.
* - Otherwise, the config must be valid (nonzero lock duration if `withPermanentLock` = false).
*
* @param config_ The `CreateLockConfig` for the caller (`msg.sender`).
*
* Emits a {SetCreateLockConfig} event.
*/
function setCreateLockConfig(CreateLockConfig calldata config_) external {
if (!config_.withPermanentLock && config_.lockDuration == 0 && config_.managedTokenIdForAttach == 0 && !config_.shouldBoosted) {
delete _usersCreateLockConfigIsEnable[msg.sender];
delete _usersCreateLockConfigs[msg.sender];
} else {
if (config_.lockDuration == 0 && !config_.withPermanentLock) {
revert InvalidCreateLockConfig();
}
_usersCreateLockConfigIsEnable[msg.sender] = true;
_usersCreateLockConfigs[msg.sender] = config_;
}
emit SetCreateLockConfig(msg.sender, config_);
}
/**
* @notice Updates the user’s emission compounding configuration, including:
* - Percentages allocated to locks vs. bribe pools.
* - The specific lock targets (`TargetLock[]`).
* - The specific bribe pool targets (`TargetPool[]`).
*
* @dev
* - The total of `toLocksPercentage + toBribePoolsPercentage` cannot exceed 1e18 (100%).
* - If `toLocksPercentage > 0`, then there must be a nonempty `targetLocks` array (and vice versa).
* - If `toBribePoolsPercentage > 0`, then there must be a nonempty `targetsBribePools` array (and vice versa).
* - The sum of all `percentage` fields in `targetLocks` must be exactly 1e18 if updating them.
* - The sum of all `percentage` fields in `targetsBribePools` must be exactly 1e18 if updating them.
* - Each `targetLocks[i].tokenId` must belong to the caller if nonzero.
* - Each `targetsBribePools[i].pool` must correspond to a gauge that is alive.
*
* @param p_ A struct with the following fields:
* - `shouldUpdateGeneralPercentages` Whether to update the overall splits to locks/bribe pools.
* - `shouldUpdateTargetLocks` Whether to replace the entire array of user’s `TargetLock[]`.
* - `shouldUpdateTargetBribePools` Whether to replace the entire array of user’s `TargetPool[]`.
* - `toLocksPercentage` The fraction of user’s emissions allocated to locks (1e18 = 100%).
* - `toBribePoolsPercentage` The fraction of user’s emissions allocated to bribe pools (1e18 = 100%).
* - `targetLocks` The new `TargetLock[]`, each with a `tokenId` and `percentage`.
* - `targetsBribePools` The new `TargetPool[]`, each with a `pool` and `percentage`.
*
* Emits {SetCompoundEmissionGeneralPercentages} if `shouldUpdateGeneralPercentages` is true.
* Emits {SetCompoundEmissionTargetLocks} if `shouldUpdateTargetLocks` is true.
* Emits {SetCompoundEmissionTargetBribePools} if `shouldUpdateTargetBribePools` is true.
*
* Reverts with {InvalidCompoundEmissionParams} if the inputs fail the above constraints.
*/
function setCompoundEmissionConfig(UpdateCompoundEmissionConfigParams calldata p_) external {
uint256 newTargetLocksLength = p_.shouldUpdateTargetLocks
? p_.targetLocks.length
: _usersCompoundEmissionTargetLocks[msg.sender].length;
uint256 newTargetBribePoolsLength = p_.shouldUpdateTargetBribePools
? p_.targetsBribePools.length
: _usersCompoundEmissionTargetBribesPools[msg.sender].length;
uint256 newToTargetLocksPercentage = p_.shouldUpdateGeneralPercentages ? p_.toLocksPercentage : getToLocksPercentage[msg.sender];
uint256 newToTargetBribePoolsPercentage = p_.shouldUpdateGeneralPercentages
? p_.toBribePoolsPercentage
: getToBribePoolsPercentage[msg.sender];
if (newToTargetLocksPercentage + newToTargetBribePoolsPercentage > _PRECISION) {
revert InvalidCompoundEmissionParams();
}
if (
(newToTargetLocksPercentage > 0 && newTargetLocksLength == 0) || (newToTargetLocksPercentage == 0 && newTargetLocksLength > 0)
) {
revert InvalidCompoundEmissionParams();
}
if (
(newToTargetBribePoolsPercentage > 0 && newTargetBribePoolsLength == 0) ||
(newToTargetBribePoolsPercentage == 0 && newTargetBribePoolsLength > 0)
) {
revert InvalidCompoundEmissionParams();
}
if (p_.shouldUpdateTargetLocks && newToTargetLocksPercentage > 0) {
IVotingEscrow votingEscrowCache = IVotingEscrow(votingEscrow);
uint256 targetLocksSumPercentage;
for (uint256 i; i < p_.targetLocks.length; ) {
uint256 percentage = p_.targetLocks[i].percentage;
if (p_.targetLocks[i].tokenId != 0) {
if (votingEscrowCache.ownerOf(p_.targetLocks[i].tokenId) != msg.sender) {
revert AnotherUserTargetLocks();
}
}
if (percentage == 0) {
revert InvalidCompoundEmissionParams();
}
targetLocksSumPercentage += p_.targetLocks[i].percentage;
unchecked {
i++;
}
}
if (targetLocksSumPercentage != _PRECISION) {
revert InvalidCompoundEmissionParams();
}
_usersCompoundEmissionTargetLocks[msg.sender] = p_.targetLocks;
emit SetCompoundEmissionTargetLocks(msg.sender, p_.targetLocks);
}
if (p_.shouldUpdateTargetBribePools && newToTargetBribePoolsPercentage > 0) {
IVoter voterCache = IVoter(voter);
uint256 targetBribePoolsSumPercentage;
for (uint256 i; i < p_.targetsBribePools.length; ) {
address targetPool = p_.targetsBribePools[i].pool;
uint256 percentage = p_.targetsBribePools[i].percentage;
if (targetPool == address(0) || percentage == 0) {
revert InvalidCompoundEmissionParams();
}
address gauge = voterCache.poolToGauge(targetPool);
if (!voterCache.isAlive(gauge)) {
revert TargetPoolGaugeIsKilled();
}
targetBribePoolsSumPercentage += percentage;
unchecked {
i++;
}
}
if (targetBribePoolsSumPercentage != _PRECISION) {
revert InvalidCompoundEmissionParams();
}
_usersCompoundEmissionTargetBribesPools[msg.sender] = p_.targetsBribePools;
emit SetCompoundEmissionTargetBribePools(msg.sender, p_.targetsBribePools);
}
if (p_.shouldUpdateGeneralPercentages) {
if (newToTargetLocksPercentage == 0) {
delete _usersCompoundEmissionTargetLocks[msg.sender];
emit SetCompoundEmissionTargetLocks(msg.sender, p_.targetLocks);
}
if (newToTargetBribePoolsPercentage == 0) {
delete _usersCompoundEmissionTargetBribesPools[msg.sender];
emit SetCompoundEmissionTargetBribePools(msg.sender, p_.targetsBribePools);
}
getToLocksPercentage[msg.sender] = newToTargetLocksPercentage;
getToBribePoolsPercentage[msg.sender] = newToTargetBribePoolsPercentage;
emit SetCompoundEmissionGeneralPercentages(msg.sender, p_.toLocksPercentage, p_.toBribePoolsPercentage);
}
}
/**
* @notice Retrieves the effective `CreateLockConfig` for a user, falling back to `defaultCreateLockConfig` if none is set.
* @param target_ The address of the user.
* @return createLockConfig The effective config for the user (custom if set, otherwise default).
*/
function getUserCreateLockConfig(address target_) public view returns (CreateLockConfig memory createLockConfig) {
return _usersCreateLockConfigIsEnable[target_] ? _usersCreateLockConfigs[target_] : defaultCreateLockConfig;
}
/**
* @notice Retrieves a user’s overall emission-compounding configuration.
* @param target_ The address of the user.
* @return toLocksPercentage Fraction allocated to veNFT locks (1e18=100%).
* @return toBribePoolsPercentage Fraction allocated to bribe pools (1e18=100%).
* @return isCreateLockCustomConfig Whether the user has a custom `CreateLockConfig`.
* @return createLockConfig The effective `CreateLockConfig` (custom or default).
* @return targetLocks The user’s `TargetLock[]` array.
* @return targetBribePools The user’s `TargetPool[]` array.
*/
function getUserInfo(
address target_
)
external
view
returns (
uint256 toLocksPercentage,
uint256 toBribePoolsPercentage,
bool isCreateLockCustomConfig,
CreateLockConfig memory createLockConfig,
TargetLock[] memory targetLocks,
TargetPool[] memory targetBribePools
)
{
toLocksPercentage = getToLocksPercentage[target_];
toBribePoolsPercentage = getToBribePoolsPercentage[target_];
createLockConfig = getUserCreateLockConfig(target_);
targetLocks = _usersCompoundEmissionTargetLocks[target_];
isCreateLockCustomConfig = _usersCreateLockConfigIsEnable[target_];
targetBribePools = _usersCompoundEmissionTargetBribesPools[target_];
}
/**
* @notice Batch operation for compounding emissions for multiple users.
* @dev
* - Only callable by addresses with the COMPOUND_KEEPER_ROLE.
* - Processes each user’s claim in a single transaction.
*
* @param claimsParams_ An array of `ClaimParams` describing each user's claim details:
* - `target`: The user whose emissions are being claimed.
* - `gauges`: The array of gauge addresses to claim from.
* - `blaze`: Optional data for Blaze-based claims (if applicable).
*/
function compoundEmissionClaimBatch(ClaimParams[] calldata claimsParams_) external onlyRole(COMPOUND_KEEPER_ROLE) nonReentrant {
for (uint256 i; i < claimsParams_.length; ) {
_compoundEmissionClaim(claimsParams_[i]);
unchecked {
i++;
}
}
}
/**
* @notice Allows a user to directly compound their emissions for the specified gauges.
* @dev
* - Only callable by the user themselves (`claimParams_.target`).
*
* @param claimParams_ The `ClaimParams` struct:
* - `target`: The user who is claiming.
* - `gauges`: The array of gauge addresses to claim from.
* - `blaze`: Optional data for blaze-based claims.
*/
function compoundEmisisonClaim(ClaimParams calldata claimParams_) external nonReentrant {
_checkSender(claimParams_.target);
_compoundEmissionClaim(claimParams_);
}
/**
* @notice Disambiguates changes to a user’s `TargetLock[]` token IDs in case of merges or transfers.
* @dev
* - If multiple entries reference `targetTokenId_`, all will be updated to `newTokenId_`.
* - If `newTokenId_ = 0`, these entries are cleared, meaning a new veNFT can be created
* next time if that portion is used for compounding.
* - Typically called by the Voter after a veNFT merge or transfer event.
*
* @param target_ The user whose `TargetLock[]` to update.
* @param targetTokenId_ The old token ID in the user’s array.
* @param newTokenId_ The new token ID to replace the old one (0 if removing).
*
* Emits a {ChangeEmissionTargetLock} event whenever a replacement occurs.
*/
function changeEmissionTargetLockId(address target_, uint256 targetTokenId_, uint256 newTokenId_) external nonReentrant {
_checkSender(voter);
if (getToLocksPercentage[target_] > 0) {
TargetLock[] memory targetLocks = _usersCompoundEmissionTargetLocks[target_];
for (uint256 i; i < targetLocks.length; ) {
if (targetLocks[i].tokenId == targetTokenId_) {
_usersCompoundEmissionTargetLocks[target_][i].tokenId = newTokenId_;
emit ChangeEmissionTargetLock(target_, targetTokenId_, newTokenId_);
}
unchecked {
i++;
}
}
}
}
/**
* @notice Calculates how much of `amountIn_` would be allocated to locks vs. bribe pools for a given user.
* @dev Does not factor in how that portion is further split among multiple `TargetLock[]` or `TargetPool[]`.
* @param target_ The user in question.
* @param amountIn_ The total amount of tokens to be distributed for the user.
* @return toTargetLocks The portion allocated to veNFT locks.
* @return toTargetBribePools The portion allocated to bribe pools.
*/
function getAmountOutToCompound(
address target_,
uint256 amountIn_
) external view returns (uint256 toTargetLocks, uint256 toTargetBribePools) {
toTargetLocks = (getToLocksPercentage[target_] * amountIn_) / _PRECISION;
toTargetBribePools = (getToBribePoolsPercentage[target_] * amountIn_) / _PRECISION;
}
/**
* @dev Core logic to compound the user’s claimed emissions into veNFT locks
* and/or bribe pools, as dictated by their configuration.
*
* Steps:
* 1) Call Voter to claim the user’s emissions from specified gauges.
* 2) Transfer those claimed tokens from Voter to this contract.
* 3) Based on user’s config, deposit the appropriate amounts into:
* (a) veNFT locks (creating new or depositing into existing).
* (b) Bribe pools.
*
* @param claimParams_ The user’s claim info from {ClaimParams}.
*/
function _compoundEmissionClaim(ClaimParams calldata claimParams_) internal {
IVoter voterCache = IVoter(voter);
(uint256 toTargetLocks, uint256 toTargetBribePools) = voterCache.onCompoundEmissionClaim(
claimParams_.target,
claimParams_.gauges,
claimParams_.blaze
);
if (toTargetLocks + toTargetBribePools == 0) {
return;
}
IERC20Upgradeable tokenCache = IERC20Upgradeable(token);
IVotingEscrow votingEscrowCache = IVotingEscrow(votingEscrow);
CreateLockConfig memory userCreateLockConfig = getUserCreateLockConfig(claimParams_.target);
tokenCache.safeTransferFrom(address(voterCache), address(this), toTargetLocks + toTargetBribePools);
if (toTargetLocks > 0) {
tokenCache.safeApprove(address(votingEscrowCache), toTargetLocks);
TargetLock[] memory targetLocks = _usersCompoundEmissionTargetLocks[claimParams_.target];
uint256 length = targetLocks.length;
for (uint256 i; i < length; ) {
TargetLock memory targetLock = targetLocks[i];
uint256 amount = (targetLock.percentage * toTargetLocks) / _PRECISION;
if (targetLock.tokenId == 0) {
targetLock.tokenId = votingEscrowCache.createLockFor(
amount,
userCreateLockConfig.lockDuration,
claimParams_.target,
userCreateLockConfig.shouldBoosted,
userCreateLockConfig.withPermanentLock,
userCreateLockConfig.managedTokenIdForAttach
);
_usersCompoundEmissionTargetLocks[claimParams_.target][i].tokenId = targetLock.tokenId;
emit CreateLockFromCompoundEmission(claimParams_.target, targetLock.tokenId, amount);
} else {
votingEscrowCache.depositFor(targetLock.tokenId, amount, false, false);
emit CompoundEmissionToTargetLock(claimParams_.target, targetLock.tokenId, amount);
}
unchecked {
i++;
}
}
}
if (toTargetBribePools > 0) {
TargetPool[] memory targetBribePools = _usersCompoundEmissionTargetBribesPools[claimParams_.target];
uint256 length = targetBribePools.length;
for (uint256 i; i < length; ) {
TargetPool memory targetPool = targetBribePools[i];
address gauge = voterCache.poolToGauge(targetPool.pool);
uint256 amount = (targetPool.percentage * toTargetBribePools) / _PRECISION;
if (voterCache.isAlive(gauge)) {
address externalBribe = voterCache.getGaugeState(gauge).externalBribe;
tokenCache.safeApprove(externalBribe, amount);
IBribe(externalBribe).notifyRewardAmount(address(tokenCache), amount);
emit CompoundEmissionToBribePool(claimParams_.target, targetPool.pool, amount);
} else {
tokenCache.safeApprove(address(votingEscrowCache), amount);
uint256 tokenId = votingEscrowCache.createLockFor(
amount,
userCreateLockConfig.lockDuration,
claimParams_.target,
userCreateLockConfig.shouldBoosted,
userCreateLockConfig.withPermanentLock,
userCreateLockConfig.managedTokenIdForAttach
);
emit CreateLockFromCompoundEmissionForBribePools(claimParams_.target, targetPool.pool, tokenId, amount);
}
unchecked {
i++;
}
}
}
}
/**
* @dev Checks that `msg.sender` matches `expected_`; otherwise reverts with {AccessDenied}.
* @param expected_ The address required for the operation.
*/
function _checkSender(address expected_) internal view {
if (msg.sender != expected_) {
revert AccessDenied();
}
}
}// 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 IAccessControlUpgradeable {
/**
* @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 (last updated v4.9.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]
* ```solidity
* 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 Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../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 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;
}
/**
* @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.9.4) (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.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface 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].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../extensions/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;
/**
* @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(IERC20Upgradeable 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(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));
}
/**
* @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(IERC20Upgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(
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");
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(IERC20Upgradeable 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))) && AddressUpgradeable.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721Upgradeable is IERC165Upgradeable {
/**
* @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.9.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
*
* 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/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 IERC165Upgradeable {
/**
* @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
pragma solidity >=0.8.0;
interface IBribe {
struct Reward {
uint256 periodFinish;
uint256 rewardsPerEpoch;
uint256 lastUpdateTime;
}
/* ========== EVENTS ========== */
event RewardAdded(address indexed rewardToken, uint256 reward, uint256 startTimestamp);
event Staked(uint256 indexed tokenId, uint256 amount);
event Withdrawn(uint256 indexed tokenId, uint256 amount);
event RewardPaid(address indexed user, address indexed rewardsToken, uint256 reward);
event Recovered(address indexed token, uint256 amount);
event AddRewardToken(address indexed token);
function deposit(uint amount, uint tokenId) external;
function withdraw(uint amount, uint tokenId) external;
function getRewardTokens() external view returns (address[] memory);
function getSpecificRewardTokens() external view returns (address[] memory);
function getRewardForOwner(uint tokenId, address[] memory tokens) external;
function getRewardForAddress(address _owner, address[] memory tokens) external;
function notifyRewardAmount(address token, uint amount) external;
function addRewardToken(address) external;
function addRewardTokens(address[] memory) external;
function initialize(address, address, string memory) external;
function firstBribeTimestamp() external view returns (uint256);
function totalSupplyAt(uint256 timestamp) external view returns (uint256);
function rewardData(address, uint256) external view returns (uint256 periodFinish, uint256 rewardsPerEpoch, uint256 lastUpdateTime);
function rewardsListLength() external view returns (uint256);
function getEpochStart() external view returns (uint256);
function earned(uint256 tokenId, address _rewardToken) external view returns (uint256);
function earned(address _owner, address _rewardToken) external view returns (uint256);
function balanceOfAt(uint256 tokenId, uint256 _timestamp) external view returns (uint256);
function balanceOf(uint256 tokenId) external view returns (uint256);
function getNextEpochStart() external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import "./IVoter.sol";
/**
* @title ICompoundEmissionExtension
* @notice
* This interface defines the external functions and data structures for an extension
* that automatically compounds user emissions into veNFT locks and/or bribe pools.
* Users can configure how their claimed emissions are split across multiple veNFTs
* (`TargetLock[]`) and multiple bribe pools (`TargetPool[]`).
*/
interface ICompoundEmissionExtension {
/**
* @notice Describes a specific veNFT token lock target and the fraction of emissions to deposit there.
* @dev
* - `percentage` is in 1e18 format (1e18 = 100%).
* - If `tokenId` is zero, a new veNFT lock may be created in the compounding process.
*/
struct TargetLock {
/**
* @notice The identifier of an existing veNFT. If zero, a new veNFT may be created.
*/
uint256 tokenId;
/**
* @notice Fraction of allocated emissions for this target (1e18 = 100%).
*/
uint256 percentage;
}
/**
* @notice Defines the fraction of emissions to be sent to a particular pool's bribe contract.
* @dev
* - `percentage` is in 1e18 format (1e18 = 100%).
* - If the associated gauge is dead/killed, fallback logic may apply (e.g., lock creation).
*/
struct TargetPool {
/**
* @notice The address of the pool whose external bribe contract will receive emissions.
*/
address pool;
/**
* @notice Fraction of allocated emissions for this pool (1e18 = 100%).
*/
uint256 percentage;
}
/**
* @notice Encapsulates parameters for updating a user's compound-emission configuration in a single call.
* @dev
* - `toLocksPercentage + toBribePoolsPercentage` must not exceed 1e18.
* - Sum of percentages in `targetLocks` must be 1e18 if `toLocksPercentage > 0`.
* - Sum of percentages in `targetsBribePools` must be 1e18 if `toBribePoolsPercentage > 0`.
* - If updating `TargetLock[]` or `TargetPool[]`, the array must match the respective percentage being >0.
*/
struct UpdateCompoundEmissionConfigParams {
/**
* @notice Whether to update the overall fraction sent to locks vs. bribe pools.
*/
bool shouldUpdateGeneralPercentages;
/**
* @notice Whether to replace the user's entire array of `TargetLock[]`.
*/
bool shouldUpdateTargetLocks;
/**
* @notice Whether to replace the user's entire array of `TargetPool[]`.
*/
bool shouldUpdateTargetBribePools;
/**
* @notice Fraction of the user's total emissions allocated to veNFT locks (1e18 = 100%).
*/
uint256 toLocksPercentage;
/**
* @notice Fraction of the user's total emissions allocated to bribe pools (1e18 = 100%).
*/
uint256 toBribePoolsPercentage;
/**
* @notice The new set of veNFT lock targets (replaces the old array if updated).
*/
TargetLock[] targetLocks;
/**
* @notice The new set of bribe pool targets (replaces the old array if updated).
*/
TargetPool[] targetsBribePools;
}
/**
* @notice Configuration options for creating or depositing into veNFTs during compounding.
* @dev
* - If `withPermanentLock` is `true`, `lockDuration` is ignored (the lock is permanent).
* - If `managedTokenIdForAttach` is nonzero, the deposit may be attached to an existing managed veNFT.
*/
struct CreateLockConfig {
/**
* @notice Whether the created lock should be considered "boosted" (if the underlying system supports boosted logic).
*/
bool shouldBoosted;
/**
* @notice Whether the lock is permanent (no withdrawal).
*/
bool withPermanentLock;
/**
* @notice Duration in seconds for the lock if it is not permanent.
*/
uint256 lockDuration;
/**
* @notice An optional managed veNFT ID for attaching a new deposit.
*/
uint256 managedTokenIdForAttach;
}
/**
* @notice Parameters to claim emissions from the Voter, which are then compounded into locks and/or bribe pools.
* @dev
* - `target` is the user whose emissions are being claimed.
* - `gauges` is a list of gauge addresses for which to claim the user’s emissions.
* - `blaze` is optional blaze-based claim data (if the Voter supports blaze signature).
*/
struct ClaimParams {
/**
* @notice The user whose emissions will be claimed and compounded.
*/
address target;
/**
* @notice The gauge addresses to claim emissions from.
*/
address[] gauges;
/**
* @notice Optional data for blaze-based claims, if applicable in the Voter implementation.
*/
IVoter.AggregateClaimBlazeDataParams blaze;
}
// --------------------- Events ---------------------
/**
* @notice Emitted when the default lock configuration is updated.
* @param config The new default configuration for creating veNFT locks.
*/
event SetDefaultCreateLockConfig(CreateLockConfig config);
/**
* @notice Emitted when a user sets or removes their custom configuration for creating veNFT locks.
* @param user The user whose configuration changed.
* @param config The new config, or default values if removed.
*/
event SetCreateLockConfig(address indexed user, CreateLockConfig config);
/**
* @notice Emitted when a user updates their overall percentages of emissions allocated to locks vs. bribe pools.
* @param user The user whose allocation changed.
* @param toLocksPercentage Fraction of emissions allocated to locks (1e18 = 100%).
* @param toBribePoolsPercentage Fraction of emissions allocated to bribe pools (1e18 = 100%).
*/
event SetCompoundEmissionGeneralPercentages(address indexed user, uint256 toLocksPercentage, uint256 toBribePoolsPercentage);
/**
* @notice Emitted when a user replaces or updates their entire array of lock targets.
* @param user The user whose targets changed.
* @param targetLocks The new array of `TargetLock` structs.
*/
event SetCompoundEmissionTargetLocks(address indexed user, TargetLock[] targetLocks);
/**
* @notice Emitted when a user replaces or updates their entire array of bribe pool targets.
* @param user The user whose targets changed.
* @param targetBribePools The new array of `TargetPool` structs.
*/
event SetCompoundEmissionTargetBribePools(address indexed user, TargetPool[] targetBribePools);
/**
* @notice Emitted when a user changes the veNFT token ID in an existing emission distribution target lock.
* @param user The user making the change.
* @param targetLockFromId The old token ID to be replaced.
* @param targetTokenToId The new token ID (0 if effectively removing the old lock reference).
*/
event ChangeEmissionTargetLock(address indexed user, uint256 targetLockFromId, uint256 targetTokenToId);
/**
* @notice Emitted when a new veNFT lock is created due to emission compounding.
* @param user The user for whom the lock was created.
* @param tokenId The newly created veNFT token ID.
* @param amount The amount of tokens locked.
*/
event CreateLockFromCompoundEmission(address indexed user, uint256 indexed tokenId, uint256 amount);
/**
* @notice Emitted when a new veNFT lock is created during fallback logic for bribe pools (e.g., if the gauge is killed).
* @param user The user for whom the fallback lock was created.
* @param pool The bribe pool that was originally intended to receive tokens.
* @param tokenId The newly created veNFT token ID.
* @param amount The amount of tokens locked instead of bribe distribution.
*/
event CreateLockFromCompoundEmissionForBribePools(address indexed user, address pool, uint256 indexed tokenId, uint256 amount);
/**
* @notice Emitted when a user compounds emissions into a specific bribe pool.
* @param user The user compounding emissions.
* @param pool The bribe pool receiving the tokens.
* @param amount The amount of tokens deposited.
*/
event CompoundEmissionToBribePool(address indexed user, address pool, uint256 amount);
/**
* @notice Emitted when a user compounds emissions into an existing veNFT lock.
* @param user The address of the user compounding emissions.
* @param tokenId The identifier of the veNFT receiving the deposit.
* @param amount The amount of tokens deposited into the lock.
*/
event CompoundEmissionToTargetLock(address indexed user, uint256 indexed tokenId, uint256 amount);
// --------------------- External Functions ---------------------
/**
* @notice Batch operation to claim and compound emissions for multiple users simultaneously.
* @dev
* - Only callable by addresses with the COMPOUND_KEEPER_ROLE.
* - Iterates over each user's claim, collecting and distributing emissions according to user configs.
*
* @param claimsParams_ An array of `ClaimParams`, one for each user to process.
*/
function compoundEmissionClaimBatch(ClaimParams[] calldata claimsParams_) external;
/**
* @notice Allows an individual user to claim and compound emissions for specified gauges.
* @dev
* - Only callable by the user matching `claimParams_.target`.
* - Collects emissions from the specified gauges, then distributes them to veNFT locks and bribe pools.
*
* @param claimParams_ The struct containing:
* - `target`: user whose emissions are being claimed.
* - `gauges`: gauges to claim from.
* - `merkl`: optional merkle claim data.
*/
function compoundEmisisonClaim(ClaimParams calldata claimParams_) external;
/**
* @notice Updates occurrences of `targetTokenId_` in a user's `TargetLock[]` to `newTokenId_`.
* @dev
* - If multiple entries reference `targetTokenId_`, all will be replaced.
* - If `newTokenId_ = 0`, references to `targetTokenId_` are cleared,
* effectively enabling a new veNFT to be created in future compounding for that portion.
* - Typically called by the Voter after a veNFT transfer or merge.
*
* @param target_ The user whose `TargetLock[]` will be updated.
* @param targetTokenId_ The old token ID to search for in that user's target array.
* @param newTokenId_ The new token ID to replace the old one. Zero if removing.
*/
function changeEmissionTargetLockId(address target_, uint256 targetTokenId_, uint256 newTokenId_) external;
/**
* @notice Retrieves the fraction (in 1e18 scale) of a user's emissions allocated to veNFT locks.
* @dev A value of 1e18 indicates 100%. If this returns 5e17, that means 50% is allocated.
* @param target_ The user address to query.
* @return The fraction of emissions (1e18 = 100%) allocated to locks.
*/
function getToLocksPercentage(address target_) external view returns (uint256);
/**
* @notice Returns how much of a given `amountIn_` would be allocated to locks vs. bribe pools for a user.
* @dev Does not break down per veNFT or per pool, only the high-level split.
* @param target_ The user whose configuration to apply.
* @param amountIn_ The total emission amount to be distributed.
* @return toTargetLocks The portion allocated to veNFT locks.
* @return toTargetBribePools The portion allocated to bribe pools.
*/
function getAmountOutToCompound(
address target_,
uint256 amountIn_
) external view returns (uint256 toTargetLocks, uint256 toTargetBribePools);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {IAccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol";
interface IVoter is IAccessControlUpgradeable {
/**
* @notice Represents the state of a gauge.
* @param isGauge Indicates if the address is a gauge.
* @param isAlive Indicates if the gauge is active.
* @param internalBribe The address of the internal bribe contract.
* @param externalBribe The address of the external bribe contract.
* @param pool The address of the associated pool.
* @param claimable The amount of rewards claimable by the gauge.
* @param index The current index used for reward distribution calculations.
* @param lastDistributionTimestamp The last time rewards were distributed.
*/
struct GaugeState {
bool isGauge;
bool isAlive;
address internalBribe;
address externalBribe;
address pool;
uint256 claimable;
uint256 index;
uint256 lastDistributionTimestamp;
}
/**
* @notice Parameters for creating a veNFT through VotingEscrow.
* @param percentageToLock The percentage (in 18 decimals) of the claimed reward tokens to be locked.
* @param lockDuration The duration (in seconds) for which the tokens will be locked.
* @param to The address that will receive the veNFT.
* @param shouldBoosted Indicates whether the veNFT should have boosted properties.
* @param withPermanentLock Indicates if the lock should be permanent.
* @param managedTokenIdForAttach The ID of the managed veNFT token to which this lock will be attached.
*/
struct AggregateCreateLockParams {
uint256 percentageToLock;
uint256 lockDuration;
address to;
bool shouldBoosted;
bool withPermanentLock;
uint256 managedTokenIdForAttach;
}
/**
* @notice Parameters for claiming bribes using a specific tokenId.
* @param tokenId The token ID to claim bribes for.
* @param bribes The array of bribe contract addresses.
* @param tokens The array of arrays containing token addresses for each bribe.
*/
struct AggregateClaimBribesByTokenIdParams {
uint256 tokenId;
address[] bribes;
address[][] tokens;
}
/**
* @notice Parameters for claiming bribes.
* @param bribes The array of bribe contract addresses.
* @param tokens The array of arrays containing token addresses for each bribe.
*/
struct AggregateClaimBribesParams {
address[] bribes;
address[][] tokens;
}
/**
* @notice Parameters for claiming Blaze data.
* @param totalAmount The total amount of reward being claimed.
* @param deadline The expiration time of the claim.
* @param signature The signature authorizing the claim.
*/
struct AggregateClaimBlazeDataParams {
uint256 totalAmount;
uint256 deadline;
bytes signature;
}
/**
* @notice Parameters for claiming VeNest Merkl airdrop data.
* @param inPureTokens_ Boolean indicating if the claim is in pure tokens.
* @param amount The amount to claim.
* @param withPermanentLock_ Whether the lock should be permanent.
* @param managedTokenIdForAttach_ The ID of the managed NFT to attach, if any. 0 for ignore
* @param proofs The array of Merkle proofs.
*/
struct AggregateClaimVeNestMerklAirdrop {
bool inPureTokens;
uint256 amount;
bool withPermanentLock;
uint256 managedTokenIdForAttach;
bytes32[] proofs;
}
/**
* @notice Emitted when a gauge is created.
* @param gauge The address of the created gauge.
* @param creator The address of the creator.
* @param internalBribe The address of the created internal bribe.
* @param externalBribe The address of the created external bribe.
* @param pool The address of the associated pool.
*/
event GaugeCreated(address indexed gauge, address creator, address internalBribe, address indexed externalBribe, address indexed pool);
/**
* @notice Emitted when a gauge is created.
* @param gauge The address of the created gauge.
* @param gaugeType Type identifier of the created gauge.
*/
event GaugeCreatedType(address indexed gauge, uint256 indexed gaugeType);
/**
* @notice Emitted when a gauge is killed.
* @param gauge The address of the killed gauge.
*/
event GaugeKilled(address indexed gauge);
/**
* @notice Emitted when a gauge is revived.
* @param gauge The address of the revived gauge.
*/
event GaugeRevived(address indexed gauge);
/**
* @dev Emitted when a user casts votes for multiple pools using a specific token.
*
* @param voter The address of the user who cast the votes.
* @param tokenId The ID of the token used for voting.
* @param epoch The epoch during which the votes were cast.
* @param pools An array of pool addresses that received votes.
* @param voteWeights An array representing the weight of votes allocated to each pool.
*
* Requirements:
* - `pools` and `voteWeights` arrays must have the same length.
*
* Note: The voting power represented in `voteWeights` is allocated across the specified `votedPools` for the given `epoch`.
* The `totalVotingPower` represents the cumulative voting power used in this vote.
*/
event VoteCast(
address indexed voter,
uint256 indexed tokenId,
uint256 indexed epoch,
address[] pools,
uint256[] voteWeights,
uint256 totalVotingPower
);
/**
* @dev Emitted when a user resets all votes for the current epoch.
*
* @param voter The address of the user who resets their votes.
* @param tokenId The ID of the token used for voting that is being reset.
* @param epoch The epoch during which the votes were reset.
* @param totalResetVotingPower The total voting power that was reset.
*
* Note: This event indicates that all previously cast votes for the specified `epoch` have been reset for the given `votingTokenId`.
* The `totalResetVotingPower` represents the cumulative voting power that was removed during the reset.
*/
event VoteReset(address indexed voter, uint256 indexed tokenId, uint256 indexed epoch, uint256 totalResetVotingPower);
/**
* @notice Emitted when rewards are notified for distribution.
* @param sender The address of the sender.
* @param reward The address of the reward token.
* @param amount The amount of rewards to distribute.
*/
event NotifyReward(address indexed sender, address indexed reward, uint256 amount);
/**
* @notice Emitted when rewards are distributed to a gauge.
* @param sender The address of the sender.
* @param gauge The address of the gauge receiving the rewards.
* @param amount The amount of rewards distributed.
*/
event DistributeReward(address indexed sender, address indexed gauge, uint256 amount);
/**
* @notice Emitted when the vote delay is updated.
* @param old The previous vote delay.
* @param latest The new vote delay.
*/
event SetVoteDelay(uint256 old, uint256 latest);
/**
* @notice Emitted when a contract address is updated.
* @param key The key representing the contract.
* @param value The new address of the contract.
*/
event UpdateAddress(string key, address indexed value);
/// @notice Event emitted when voting is paused or unpaused.
/// @dev Emits the current paused state of voting.
/// @param paused Indicates whether voting is paused (true) or unpaused (false).
event VotingPaused(bool indexed paused);
/**
* @notice Emitted when the distribution window duration is set or updated.
* @param duration New duration of the distribution window in seconds.
*/
event SetDistributionWindowDuration(uint256 indexed duration);
/**
* @notice Emitted when a token is attached to a managed NFT.
* @param tokenId ID of the user's token that is being attached.
* @param managedTokenId ID of the managed token to which the user's token is attached.
*/
event AttachToManagedNFT(uint256 indexed tokenId, uint256 indexed managedTokenId);
/**
* @notice Emitted when a token is detached from a managed NFT.
* @param tokenId ID of the user's token that is being detached.
*/
event DettachFromManagedNFT(uint256 indexed tokenId);
/**
* @notice Updates the address of a specified contract.
* @param key_ The key representing the contract.
* @param value_ The new address of the contract.
*/
function updateAddress(string memory key_, address value_) external;
/**
* @notice Sets the duration of the distribution window for voting.
* @param distributionWindowDuration_ The duration in seconds.
*/
function setDistributionWindowDuration(uint256 distributionWindowDuration_) external;
/**
* @notice Disables a gauge, preventing further rewards distribution.
* @param gauge_ The address of the gauge to be disabled.
*/
function killGauge(address gauge_) external;
/**
* @notice Revives a previously disabled gauge, allowing it to distribute rewards again.
* @param gauge_ The address of the gauge to be revived.
*/
function reviveGauge(address gauge_) external;
/**
* @notice Creates a new V2 gauge for a specified pool.
* @param pool_ The address of the pool for which to create a gauge.
* @return gauge The address of the created gauge.
* @return internalBribe The address of the created internal bribe.
* @return externalBribe The address of the created external bribe.
*/
function createV2Gauge(address pool_) external returns (address gauge, address internalBribe, address externalBribe);
/**
* @notice Creates a new V3 gauge for a specified pool.
* @param pool_ The address of the pool for which to create a gauge.
* @return gauge The address of the created gauge.
* @return internalBribe The address of the created internal bribe.
* @return externalBribe The address of the created external bribe.
*/
function createV3Gauge(address pool_) external returns (address gauge, address internalBribe, address externalBribe);
/**
* @notice Creates a custom gauge with specified parameters.
* @param gauge_ The address of the custom gauge.
* @param pool_ The address of the pool for which to create a gauge.
* @param tokenA_ The address of token A in the pool.
* @param tokenB_ The address of token B in the pool.
* @param externalBribesName_ The name of the external bribe.
* @param internalBribesName_ The name of the internal bribe.
* @return gauge The address of the created gauge.
* @return internalBribe The address of the created internal bribe.
* @return externalBribe The address of the created external bribe.
*/
function createCustomGauge(
address gauge_,
address pool_,
address tokenA_,
address tokenB_,
string memory externalBribesName_,
string memory internalBribesName_
) external returns (address gauge, address internalBribe, address externalBribe);
/**
* @notice Notifies the contract of a reward amount to be distributed.
* @param amount_ The amount of rewards to distribute.
*/
function notifyRewardAmount(uint256 amount_) external;
/**
* @notice Distributes fees to a list of gauges.
* @param gauges_ An array of gauge addresses to distribute fees to.
*/
function distributeFees(address[] calldata gauges_) external;
/**
* @notice Distributes rewards to all pools managed by the contract.
*/
function distributeAll() external;
/**
* @notice Distributes rewards to a specified range of pools.
* @param start_ The starting index of the pool array.
* @param finish_ The ending index of the pool array.
*/
function distribute(uint256 start_, uint256 finish_) external;
/**
* @notice Distributes rewards to a specified list of gauges.
* @param gauges_ An array of gauge addresses to distribute rewards to.
*/
function distribute(address[] calldata gauges_) external;
/**
* @notice Resets the votes for a given NFT token ID.
* @param tokenId_ The token ID for which to reset votes.
*/
function reset(uint256 tokenId_) external;
/**
* @notice Updates the voting preferences for a given token ID.
* @param tokenId_ The token ID for which to update voting preferences.
*/
function poke(uint256 tokenId_) external;
/**
* @notice Casts votes for a given NFT token ID.
* @param tokenId_ The token ID for which to cast votes.
* @param poolsVotes_ An array of pool addresses to vote for.
* @param weights_ An array of weights corresponding to the pools.
*/
function vote(uint256 tokenId_, address[] calldata poolsVotes_, uint256[] calldata weights_) external;
/**
* @notice Claims rewards from multiple gauges.
* @param _gauges An array of gauge addresses to claim rewards from.
*/
function claimRewards(address[] memory _gauges) external;
/**
* @notice Claims bribes for a given NFT token ID from multiple bribe contracts.
* @param _bribes An array of bribe contract addresses to claim bribes from.
* @param _tokens An array of token arrays, specifying the tokens to claim.
* @param tokenId_ The token ID for which to claim bribes.
*/
function claimBribes(address[] memory _bribes, address[][] memory _tokens, uint256 tokenId_) external;
/**
* @notice Claims bribes from multiple bribe contracts.
* @param _bribes An array of bribe contract addresses to claim bribes from.
* @param _tokens An array of token arrays, specifying the tokens to claim.
*/
function claimBribes(address[] memory _bribes, address[][] memory _tokens) external;
/**
* @notice Handles the deposit of voting power to a managed NFT.
* @dev This function is called after tokens are deposited into the Voting Escrow contract for a managed NFT.
* Only callable by the Voting Escrow contract.
* @param tokenId_ The ID of the token that has received the deposit.
* @param managedTokenId_ The ID of the managed token receiving the voting power.
* @custom:error AccessDenied Thrown if the caller is not the Voting Escrow contract.
*/
function onDepositToManagedNFT(uint256 tokenId_, uint256 managedTokenId_) external;
/**
* @notice Attaches a tokenId to a managed tokenId.
* @param tokenId_ The user's tokenId to be attached.
* @param managedTokenId_ The managed tokenId to attach to.
*/
function attachToManagedNFT(uint256 tokenId_, uint256 managedTokenId_) external;
/**
* @notice Detaches a tokenId from its managed tokenId.
* @param tokenId_ The user's tokenId to be detached.
*/
function dettachFromManagedNFT(uint256 tokenId_) external;
/**
* @notice Checks if the provided address is a registered gauge.
* @param gauge_ The address of the gauge to check.
* @return True if the address is a registered gauge, false otherwise.
*/
function isGauge(address gauge_) external view returns (bool);
/**
* @notice Returns the state of a specific gauge.
* @param gauge_ The address of the gauge.
* @return GaugeState The current state of the specified gauge.
*/
function getGaugeState(address gauge_) external view returns (GaugeState memory);
/**
* @notice Checks if the specified gauge is alive (i.e., enabled for reward distribution).
* @param gauge_ The address of the gauge to check.
* @return True if the gauge is alive, false otherwise.
*/
function isAlive(address gauge_) external view returns (bool);
/**
* @notice Returns the pool address associated with a specified gauge.
* @param gauge_ The address of the gauge to query.
* @return The address of the pool associated with the specified gauge.
*/
function poolForGauge(address gauge_) external view returns (address);
/**
* @notice Returns the gauge address associated with a specified pool.
* @param pool_ The address of the pool to query.
* @return The address of the gauge associated with the specified pool.
*/
function poolToGauge(address pool_) external view returns (address);
/**
* @notice Returns the address of the Voting Escrow contract.
* @return The address of the Voting Escrow contract.
*/
function votingEscrow() external view returns (address);
/**
* @notice Returns the address of the Minter contract.
* @return The address of the Minter contract.
*/
function minter() external view returns (address);
/**
* @notice Returns the address of the V2 Pool Factory contract.
* @return The address of the V2 Pool Factory contract.
*/
function v2PoolFactory() external view returns (address);
/**
* @notice Returns the address of the V3 Pool Factory contract.
* @return The address of the V3 Pool Factory contract.
*/
function v3PoolFactory() external view returns (address);
/**
* @notice Returns the V2 pool address at a specific index.
* @param index The index of the V2 pool.
* @return The address of the V2 pool at the specified index.
*/
function v2Pools(uint256 index) external view returns (address);
/**
* @notice Returns the V3 pool address at a specific index.
* @param index The index of the V3 pool.
* @return The address of the V3 pool at the specified index.
*/
function v3Pools(uint256 index) external view returns (address);
/**
* @notice Returns the total number of pools, V2 pools, and V3 pools managed by the contract.
* @return totalCount The total number of pools.
* @return v2PoolsCount The total number of V2 pools.
* @return v3PoolsCount The total number of V3 pools.
*/
function poolsCounts() external view returns (uint256 totalCount, uint256 v2PoolsCount, uint256 v3PoolsCount);
/**
* @notice Returns the current epoch timestamp used for reward calculations.
* @return The current epoch timestamp.
*/
function epochTimestamp() external view returns (uint256);
/**
* @notice Returns the weight for a specific pool in a given epoch.
* @param timestamp The timestamp of the epoch.
* @param pool The address of the pool.
* @return The weight of the pool in the specified epoch.
*/
function weightsPerEpoch(uint256 timestamp, address pool) external view returns (uint256);
/**
* @notice Returns the vote weight of a specific NFT token ID for a given pool.
* @param tokenId The ID of the NFT.
* @param pool The address of the pool.
* @return The vote weight of the token for the specified pool.
*/
function votes(uint256 tokenId, address pool) external view returns (uint256);
/**
* @notice Returns the number of pools that an NFT token ID has voted for.
* @param tokenId The ID of the NFT.
* @return The number of pools the token has voted for.
*/
function poolVoteLength(uint256 tokenId) external view returns (uint256);
/**
* @notice Returns the pool address at a specific index for which the NFT token ID has voted.
* @param tokenId The ID of the NFT.
* @param index The index of the pool.
* @return The address of the pool at the specified index.
*/
function poolVote(uint256 tokenId, uint256 index) external view returns (address);
/**
* @notice Returns the last timestamp when an NFT token ID voted.
* @param tokenId The ID of the NFT.
* @return The timestamp of the last vote.
*/
function lastVotedTimestamps(uint256 tokenId) external view returns (uint256);
/**
* @notice Called after a token transfer to update external logic or linkage.
* @dev Typically invoked by the VotingEscrow contract whenever a veNFT changes ownership.
* Implementations can handle scenario-specific logic such as emission extension or target lock updates.
* @param from_ The address from which the token is transferred.
* @param to_ The address to which the token is transferred.
* @param tokenId_ The ID of the token being transferred.
*/
function onAfterTokenTransfer(address from_, address to_, uint256 tokenId_) external;
/**
* @notice Called after two veNFT tokens are merged into one.
* @dev Typically invoked by the VotingEscrow contract during the merge operation.
* Implementations can adjust bookkeeping, reward balances, or other logic related to the merged tokens.
* @param fromTokenId_ The ID of the token that is merged (source).
* @param toTokenId_ The ID of the token that remains (destination).
*/
function onAfterTokenMerge(uint256 fromTokenId_, uint256 toTokenId_) external;
/**
* @notice This function is called by the CompoundEmissionExtension to process a user’s reward claims
* and determine how much of the claimed tokens will be routed into veNFT locks and/or bribe pools.
*
* @param target_ The address of the user for whom the emission claim is being processed.
* @param gauges_ The array of gauge addresses from which to claim rewards on behalf of `target_`.
* @param blaze_ Optional Blaze-based claim data (if the Voter supports Blaze claims).
*
* @return toTargetLocks The portion of claimed tokens that should go into veNFT locks.
* @return toTargetBribePools The portion of claimed tokens that should go into bribe pools.
*/
function onCompoundEmissionClaim(
address target_,
address[] calldata gauges_,
AggregateClaimBlazeDataParams calldata blaze_
) external returns (uint256 toTargetLocks, uint256 toTargetBribePools);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
/**
* @title IVotingEscrow
* @notice Interface for Voting Escrow, allowing users to lock tokens in exchange for veNFTs that are used in governance and other systems.
*/
interface IVotingEscrow is IERC721Upgradeable {
/**
* @notice Enum representing the types of deposits that can be made.
* @dev Defines the context in which a deposit is made:
* - `DEPOSIT_FOR_TYPE`: Regular deposit for an existing lock.
* - `CREATE_LOCK_TYPE`: Creating a new lock.
* - `INCREASE_UNLOCK_TIME`: Increasing the unlock time for an existing lock.
* - `MERGE_TYPE`: Merging two locks together.
*/
enum DepositType {
DEPOSIT_FOR_TYPE,
CREATE_LOCK_TYPE,
INCREASE_UNLOCK_TIME,
MERGE_TYPE
}
/**
* @notice Structure representing the state of a token.
* @dev This includes information about the lock, voting status, attachment status,
* the block of the last transfer, and the index (epoch) of its latest checkpoint.
* @param locked The locked balance (amount + end timestamp + permanent status) of the token.
* @param isVoted Whether the token has been used to vote in the current epoch.
* @param isAttached Whether the token is attached to a managed NFT.
* @param lastTranferBlock The block number of the last transfer.
* @param pointEpoch The epoch (checkpoint index) for the token’s most recent voting power change.
*/
struct TokenState {
LockedBalance locked;
bool isVoted;
bool isAttached;
uint256 lastTranferBlock;
uint256 pointEpoch;
}
/**
* @notice Structure representing a locked balance.
* @dev Contains the amount locked, the end timestamp of the lock, and whether the lock is permanent.
* @param amount The amount of tokens locked (signed integer for slope calculations).
* @param end The timestamp when the lock ends (0 if permanently locked).
* @param isPermanentLocked Whether the lock is permanent (no unlock time).
*/
struct LockedBalance {
int128 amount;
uint256 end;
bool isPermanentLocked;
}
/**
* @notice Structure representing a point in time for calculating voting power.
* @dev Used for slope/bias math across epochs.
* @param bias The bias of the lock, representing the remaining voting power.
* @param slope The rate at which voting power (bias) decays over time.
* @param ts The timestamp of the checkpoint.
* @param blk The block number of the checkpoint.
* @param permanent The permanently locked amount at this checkpoint.
*/
struct Point {
int128 bias;
int128 slope; // -dweight / dt
uint256 ts;
uint256 blk; // block
int128 permanent;
}
/**
* @notice Emitted when a boost is applied to a token's lock.
* @param tokenId The ID of the token that received the boost.
* @param value The amount of tokens used as a boost.
*/
event Boost(uint256 indexed tokenId, uint256 value);
/**
* @notice Emitted when a deposit is made into a lock.
* @param provider The address of the entity making the deposit.
* @param tokenId The ID of the token associated with the deposit.
* @param value The amount of tokens deposited.
* @param locktime The time (timestamp) until which the lock is extended.
* @param deposit_type The type of deposit (see {DepositType}).
* @param ts The timestamp when the deposit was made.
*/
event Deposit(address indexed provider, uint256 tokenId, uint256 value, uint256 indexed locktime, DepositType deposit_type, uint256 ts);
/**
* @notice Emitted when tokens are deposited to an attached NFT.
* @param provider The address of the user making the deposit.
* @param tokenId The ID of the NFT receiving the deposit.
* @param managedTokenId The ID of the managed token receiving the voting power.
* @param value The amount of tokens deposited.
*/
event DepositToAttachedNFT(address indexed provider, uint256 tokenId, uint256 managedTokenId, uint256 value);
/**
* @notice Emitted when a withdrawal is made from a lock.
* @param provider The address of the entity making the withdrawal.
* @param tokenId The ID of the token associated with the withdrawal.
* @param value The amount of tokens withdrawn.
* @param ts The timestamp when the withdrawal occurred.
*/
event Withdraw(address indexed provider, uint256 tokenId, uint256 value, uint256 ts);
/**
* @notice Emitted when the merging process of two veNFT locks is initiated.
* @param tokenFromId The ID of the token being merged from.
* @param tokenToId The ID of the token being merged into.
*/
event MergeInit(uint256 tokenFromId, uint256 tokenToId);
/**
* @notice Emitted when two veNFT locks are successfully merged.
* @param provider The address of the entity initiating the merge.
* @param tokenIdFrom The ID of the token being merged from.
* @param tokenIdTo The ID of the token being merged into.
*/
event Merge(address indexed provider, uint256 tokenIdFrom, uint256 tokenIdTo);
/**
* @notice Emitted when the total supply of voting power changes.
* @param prevSupply The previous total supply of voting power.
* @param supply The new total supply of voting power.
*/
event Supply(uint256 prevSupply, uint256 supply);
/**
* @notice Emitted when an address associated with the contract is updated.
* @param key The key representing the contract being updated.
* @param value The new address of the contract.
*/
event UpdateAddress(string key, address indexed value);
/**
* @notice Emitted when a token is permanently locked by a user.
* @param sender The address of the user who initiated the lock.
* @param tokenId The ID of the token that has been permanently locked.
*/
event LockPermanent(address indexed sender, uint256 indexed tokenId);
/**
* @notice Emitted when a token is unlocked from a permanent lock state by a user.
* @param sender The address of the user who initiated the unlock.
* @param tokenId The ID of the token that has been unlocked from its permanent state.
*/
event UnlockPermanent(address indexed sender, uint256 indexed tokenId);
/**
* @notice Emitted when a veNEST NFT lock is burned and the underlying NEST is released for use in bribes.
* @param sender The address which initiated the burn-to-bribes operation.
* @param tokenId The identifier of the veNEST NFT that was burned.
* @param value The amount of NEST tokens released from the burned lock.
*/
event BurnToBribes(address indexed sender, uint256 indexed tokenId, uint256 value);
/**
* @notice Returns the address of the token used in voting escrow.
* @return The address of the token contract.
*/
function token() external view returns (address);
/**
* @notice Returns the address of the voter contract.
* @return The address of the voter.
*/
function voter() external view returns (address);
/**
* @notice Checks if the specified address is approved or the owner of the given token.
* @param sender The address to check.
* @param tokenId The ID of the token to check.
* @return True if `sender` is approved or the owner of `tokenId`, otherwise false.
*/
function isApprovedOrOwner(address sender, uint256 tokenId) external view returns (bool);
/**
* @notice Checks if a specific NFT token is transferable.
* @dev In the current implementation, this function always returns `true`,
* meaning the contract does not enforce non-transferability at code level.
* @param tokenId_ The ID of the NFT to check.
* @return bool Always returns true in the current version.
*/
function isTransferable(uint256 tokenId_) external view returns (bool);
/**
* @notice Retrieves the state of a specific NFT.
* @param tokenId_ The ID of the NFT to query.
* @return The current {TokenState} of the specified NFT.
*/
function getNftState(uint256 tokenId_) external view returns (TokenState memory);
/**
* @notice Returns the total supply of voting power at the current block timestamp.
* @return The total supply of voting power.
*/
function votingPowerTotalSupply() external view returns (uint256);
/**
* @notice Returns the balance of a veNFT at the current block timestamp.
* @dev Balance is determined by the lock’s slope and bias at this moment.
* @param tokenId_ The ID of the veNFT to query.
* @return The current voting power (balance) of the veNFT.
*/
function balanceOfNFT(uint256 tokenId_) external view returns (uint256);
/**
* @notice Returns the balance of a veNFT at the current block timestamp, ignoring ownership changes.
* @dev This function is similar to {balanceOfNFT} but does not zero out the balance
* if the token was transferred in the same block.
* @param tokenId_ The ID of the veNFT to query.
* @return The current voting power (balance) of the veNFT.
*/
function balanceOfNftIgnoreOwnershipChange(uint256 tokenId_) external view returns (uint256);
/**
* @notice Updates the address of a specified contract.
* @param key_ The key representing the contract.
* @param value_ The new address of the contract.
* @dev Reverts with `InvalidAddressKey` if the key does not match any known setting.
* Emits an {UpdateAddress} event on success.
*/
function updateAddress(string memory key_, address value_) external;
/**
* @notice Hooks the voting state for a specified NFT.
* @dev Only callable by the voter contract. Used to mark a veNFT as having voted or not.
* @param tokenId_ The ID of the NFT.
* @param state_ True if the NFT is now considered “voted,” false otherwise.
* @custom:error AccessDenied If called by any address other than the voter.
*/
function votingHook(uint256 tokenId_, bool state_) external;
/**
* @notice Creates a new lock for a specified recipient.
* @param amount_ The amount of tokens to lock.
* @param lockDuration_ The duration in seconds for which the tokens will be locked.
* @param to_ The address of the recipient who will receive the new veNFT.
* @param shouldBoosted_ Whether the deposit should attempt to get a veBoost.
* @param withPermanentLock_ Whether the lock should be created as a permanent lock.
* @param managedTokenIdForAttach_ (Optional) The ID of the managed NFT to attach. Pass 0 to ignore.
* @return The ID of the newly created veNFT.
* @dev Reverts with `InvalidLockDuration` if lockDuration_ is 0 or too large.
* Reverts with `ValueZero` if amount_ is 0.
* Emits a {Deposit} event on success.
*/
function createLockFor(
uint256 amount_,
uint256 lockDuration_,
address to_,
bool shouldBoosted_,
bool withPermanentLock_,
uint256 managedTokenIdForAttach_
) external returns (uint256);
/**
* @notice Deposits tokens for a specific NFT, increasing its locked balance.
* @param tokenId_ The ID of the veNFT to top up.
* @param amount_ The amount of tokens to deposit.
* @param shouldBoosted_ Whether this deposit should attempt to get a veBoost.
* @param withPermanentLock_ Whether to apply a permanent lock alongside the deposit.
* @dev Reverts with `ValueZero` if amount_ is 0.
* Emits a {Deposit} event upon success.
*/
function depositFor(uint256 tokenId_, uint256 amount_, bool shouldBoosted_, bool withPermanentLock_) external;
/**
* @notice Increases the unlock time for an existing lock.
* @param tokenId_ The ID of the veNFT to extend.
* @param lockDuration_ The additional duration in seconds to add to the current unlock time.
* @dev Reverts with `InvalidLockDuration` if the new unlock time is invalid.
* Reverts with `AccessDenied` if the caller is not the owner or approved.
* Emits a {Deposit} event with the deposit type set to {INCREASE_UNLOCK_TIME}.
*/
function increase_unlock_time(uint256 tokenId_, uint256 lockDuration_) external;
/**
* @notice Deposits tokens and extends the lock duration for a veNFT in one call.
* @dev This may trigger veBoost if conditions are met.
* @param tokenId_ The ID of the veNFT.
* @param amount_ The amount of tokens to deposit.
* @param lockDuration_ The duration in seconds to add to the current unlock time.
* Emits one {Deposit} event for the deposit itself
* and another {Deposit} event for the unlock time increase.
*/
function depositWithIncreaseUnlockTime(uint256 tokenId_, uint256 amount_, uint256 lockDuration_) external;
/**
* @notice Deposits tokens directly into a veNFT that is attached to a managed NFT.
* @dev This updates the locked balance on the managed NFT, adjusts total supply,
* and emits {DepositToAttachedNFT} and {Supply} events.
* @param tokenId_ The ID of the attached veNFT.
* @param amount_ The amount of tokens to deposit.
* @custom:error NotManagedNft if the managed token ID is invalid or not recognized.
*/
function depositToAttachedNFT(uint256 tokenId_, uint256 amount_) external;
/**
* @notice Withdraws tokens from an expired lock (non-permanent).
* @param tokenId_ The ID of the veNFT to withdraw from.
* @dev Reverts with `AccessDenied` if caller is not owner or approved.
* Reverts with `TokenNoExpired` if the lock is not yet expired.
* Reverts with `PermanentLocked` if the lock is permanent.
* Emits a {Withdraw} event and a {Supply} event.
*/
function withdraw(uint256 tokenId_) external;
/**
* @notice Merges one veNFT (tokenFromId_) into another (tokenToId_).
* @param tokenFromId_ The ID of the source veNFT being merged.
* @param tokenToId_ The ID of the target veNFT receiving the locked tokens.
* @dev Reverts with `MergeTokenIdsTheSame` if both IDs are the same.
* Reverts with `AccessDenied` if the caller isn't owner or approved for both IDs.
* Emits a {MergeInit} event at the start, and a {Merge} event upon completion.
* Also emits a {Deposit} event reflecting the updated lock in the target token.
*/
function merge(uint256 tokenFromId_, uint256 tokenToId_) external;
/**
* @notice Permanently locks a veNFT.
* @param tokenId_ The ID of the veNFT to be permanently locked.
* @dev Reverts with `AccessDenied` if caller isn't owner or approved.
* Reverts with `TokenAttached` if the token is attached to a managed NFT.
* Reverts with `PermanentLocked` if the token already permanent lcoked
* Emits {LockPermanent} on success.
*/
function lockPermanent(uint256 tokenId_) external;
/**
* @notice Unlocks a permanently locked veNFT, reverting it to a temporary lock.
* @param tokenId_ The ID of the veNFT to unlock.
* @dev Reverts with `AccessDenied` if caller isn't owner or approved.
* Reverts with `TokenAttached` if the token is attached.
* Reverts with `NotPermanentLocked` if the lock isn't actually permanent.
* Emits {UnlockPermanent} on success.
*/
function unlockPermanent(uint256 tokenId_) external;
/**
* @notice Creates a new managed NFT for a given recipient.
* @param recipient_ The address that will receive the newly created managed NFT.
* @return The ID of the newly created managed NFT.
* @dev Reverts with `AccessDenied` if caller is not the managed NFT manager.
*/
function createManagedNFT(address recipient_) external returns (uint256);
/**
* @notice Attaches a veNFT (user’s token) to a managed NFT, combining their locked balances.
* @param tokenId_ The ID of the user’s veNFT being attached.
* @param managedTokenId_ The ID of the managed NFT.
* @return The amount of tokens locked during the attachment.
* @dev Reverts with `AccessDenied` if caller is not the managed NFT manager.
* Reverts with `ZeroVotingPower` if the user’s token has zero voting power.
* Reverts with `NotManagedNft` if the target is not recognized as a managed NFT.
*/
function onAttachToManagedNFT(uint256 tokenId_, uint256 managedTokenId_) external returns (uint256);
/**
* @notice Detaches a veNFT from a managed NFT.
* @param tokenId_ The ID of the user’s veNFT being detached.
* @param managedTokenId_ The ID of the managed NFT from which it’s being detached.
* @param newBalance_ The new locked balance the veNFT will hold after detachment.
* @dev Reverts with `AccessDenied` if caller is not the managed NFT manager.
* Reverts with `NotManagedNft` if the target is not recognized as a managed NFT.
*/
function onDettachFromManagedNFT(uint256 tokenId_, uint256 managedTokenId_, uint256 newBalance_) external;
/**
* @notice Burns a veNEST NFT to reclaim the underlying NEST tokens for use in bribes.
* @dev Must be called by `customBribeRewardRouter`.
* The token must not be permanently locked or attached.
* Also resets any votes before burning.
* Emits a {BurnToBribes} event on successful burn.
* @param tokenId_ The ID of the veNEST NFT to burn.
*/
function burnToBribes(uint256 tokenId_) external;
}{
"evmVersion": "paris",
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 2000
},
"metadata": {
"bytecodeHash": "none"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[],"name":"AnotherUserTargetLocks","type":"error"},{"inputs":[],"name":"InvalidCompoundEmissionParams","type":"error"},{"inputs":[],"name":"InvalidCreateLockConfig","type":"error"},{"inputs":[],"name":"TargetPoolGaugeIsKilled","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"targetLockFromId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"targetTokenToId","type":"uint256"}],"name":"ChangeEmissionTargetLock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CompoundEmissionToBribePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CompoundEmissionToTargetLock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreateLockFromCompoundEmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreateLockFromCompoundEmissionForBribePools","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"toLocksPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toBribePoolsPercentage","type":"uint256"}],"name":"SetCompoundEmissionGeneralPercentages","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"percentage","type":"uint256"}],"indexed":false,"internalType":"struct ICompoundEmissionExtension.TargetPool[]","name":"targetBribePools","type":"tuple[]"}],"name":"SetCompoundEmissionTargetBribePools","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"percentage","type":"uint256"}],"indexed":false,"internalType":"struct ICompoundEmissionExtension.TargetLock[]","name":"targetLocks","type":"tuple[]"}],"name":"SetCompoundEmissionTargetLocks","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"bool","name":"shouldBoosted","type":"bool"},{"internalType":"bool","name":"withPermanentLock","type":"bool"},{"internalType":"uint256","name":"lockDuration","type":"uint256"},{"internalType":"uint256","name":"managedTokenIdForAttach","type":"uint256"}],"indexed":false,"internalType":"struct ICompoundEmissionExtension.CreateLockConfig","name":"config","type":"tuple"}],"name":"SetCreateLockConfig","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bool","name":"shouldBoosted","type":"bool"},{"internalType":"bool","name":"withPermanentLock","type":"bool"},{"internalType":"uint256","name":"lockDuration","type":"uint256"},{"internalType":"uint256","name":"managedTokenIdForAttach","type":"uint256"}],"indexed":false,"internalType":"struct ICompoundEmissionExtension.CreateLockConfig","name":"config","type":"tuple"}],"name":"SetDefaultCreateLockConfig","type":"event"},{"inputs":[],"name":"COMPOUND_EMISSION_EXTENSION_ADMINISTRATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COMPOUND_KEEPER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target_","type":"address"},{"internalType":"uint256","name":"targetTokenId_","type":"uint256"},{"internalType":"uint256","name":"newTokenId_","type":"uint256"}],"name":"changeEmissionTargetLockId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address[]","name":"gauges","type":"address[]"},{"components":[{"internalType":"uint256","name":"totalAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IVoter.AggregateClaimBlazeDataParams","name":"blaze","type":"tuple"}],"internalType":"struct ICompoundEmissionExtension.ClaimParams","name":"claimParams_","type":"tuple"}],"name":"compoundEmisisonClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address[]","name":"gauges","type":"address[]"},{"components":[{"internalType":"uint256","name":"totalAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IVoter.AggregateClaimBlazeDataParams","name":"blaze","type":"tuple"}],"internalType":"struct ICompoundEmissionExtension.ClaimParams[]","name":"claimsParams_","type":"tuple[]"}],"name":"compoundEmissionClaimBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultCreateLockConfig","outputs":[{"internalType":"bool","name":"shouldBoosted","type":"bool"},{"internalType":"bool","name":"withPermanentLock","type":"bool"},{"internalType":"uint256","name":"lockDuration","type":"uint256"},{"internalType":"uint256","name":"managedTokenIdForAttach","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target_","type":"address"},{"internalType":"uint256","name":"amountIn_","type":"uint256"}],"name":"getAmountOutToCompound","outputs":[{"internalType":"uint256","name":"toTargetLocks","type":"uint256"},{"internalType":"uint256","name":"toTargetBribePools","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getToBribePoolsPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getToLocksPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target_","type":"address"}],"name":"getUserCreateLockConfig","outputs":[{"components":[{"internalType":"bool","name":"shouldBoosted","type":"bool"},{"internalType":"bool","name":"withPermanentLock","type":"bool"},{"internalType":"uint256","name":"lockDuration","type":"uint256"},{"internalType":"uint256","name":"managedTokenIdForAttach","type":"uint256"}],"internalType":"struct ICompoundEmissionExtension.CreateLockConfig","name":"createLockConfig","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target_","type":"address"}],"name":"getUserInfo","outputs":[{"internalType":"uint256","name":"toLocksPercentage","type":"uint256"},{"internalType":"uint256","name":"toBribePoolsPercentage","type":"uint256"},{"internalType":"bool","name":"isCreateLockCustomConfig","type":"bool"},{"components":[{"internalType":"bool","name":"shouldBoosted","type":"bool"},{"internalType":"bool","name":"withPermanentLock","type":"bool"},{"internalType":"uint256","name":"lockDuration","type":"uint256"},{"internalType":"uint256","name":"managedTokenIdForAttach","type":"uint256"}],"internalType":"struct ICompoundEmissionExtension.CreateLockConfig","name":"createLockConfig","type":"tuple"},{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"percentage","type":"uint256"}],"internalType":"struct ICompoundEmissionExtension.TargetLock[]","name":"targetLocks","type":"tuple[]"},{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"percentage","type":"uint256"}],"internalType":"struct ICompoundEmissionExtension.TargetPool[]","name":"targetBribePools","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"voter_","type":"address"},{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"votingEscrow_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"shouldUpdateGeneralPercentages","type":"bool"},{"internalType":"bool","name":"shouldUpdateTargetLocks","type":"bool"},{"internalType":"bool","name":"shouldUpdateTargetBribePools","type":"bool"},{"internalType":"uint256","name":"toLocksPercentage","type":"uint256"},{"internalType":"uint256","name":"toBribePoolsPercentage","type":"uint256"},{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"percentage","type":"uint256"}],"internalType":"struct ICompoundEmissionExtension.TargetLock[]","name":"targetLocks","type":"tuple[]"},{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"percentage","type":"uint256"}],"internalType":"struct ICompoundEmissionExtension.TargetPool[]","name":"targetsBribePools","type":"tuple[]"}],"internalType":"struct ICompoundEmissionExtension.UpdateCompoundEmissionConfigParams","name":"p_","type":"tuple"}],"name":"setCompoundEmissionConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"shouldBoosted","type":"bool"},{"internalType":"bool","name":"withPermanentLock","type":"bool"},{"internalType":"uint256","name":"lockDuration","type":"uint256"},{"internalType":"uint256","name":"managedTokenIdForAttach","type":"uint256"}],"internalType":"struct ICompoundEmissionExtension.CreateLockConfig","name":"config_","type":"tuple"}],"name":"setCreateLockConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"shouldBoosted","type":"bool"},{"internalType":"bool","name":"withPermanentLock","type":"bool"},{"internalType":"uint256","name":"lockDuration","type":"uint256"},{"internalType":"uint256","name":"managedTokenIdForAttach","type":"uint256"}],"internalType":"struct ICompoundEmissionExtension.CreateLockConfig","name":"config_","type":"tuple"}],"name":"setDefaultCreateLockConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votingEscrow","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
608080604052346100c1576000549060ff8260081c1661006f575060ff80821603610034575b60405161294b90816100c78239f35b60ff90811916176000557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160ff8152a138610025565b62461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b6064820152608490fd5b600080fdfe6080604052600436101561001257600080fd5b60003560e01c8063173f9ea31461148d57806318518521146114355780632618d360146112a557806346c96aac1461127e5780634f2bfe5b14611257578063537afe4d1461121d5780635650ba77146110b55780636386c1c714610e99578063902f039d14610623578063992cb9c3146104c2578063a4015b4c1461046e578063bd6464ce14610433578063bf4cd7ba146103f8578063c0c53b8b146101f8578063c8856fd7146101b7578063fc0c546a14610190578063fe3a04c4146101215763ff88710c146100e257600080fd5b3461011c57602060031936011261011c576001600160a01b036101036115d9565b16600052603a6020526020604060002054604051908152f35b600080fd5b3461011c57604060031936011261011c57604061013c6115d9565b6001600160a01b03602435911690816000526039602052610183670de0b6b3a7640000918261016f8287600020546117fd565b0493600052603a60205284600020546117fd565b0482519182526020820152f35b3461011c57600060031936011261011c5760206001600160a01b0360345416604051908152f35b3461011c57600060031936011261011c5760806036546037546038549060ff604051938181161515855260081c161515602084015260408301526060820152f35b3461011c57606060031936011261011c576102116115d9565b6024356001600160a01b039182821680920361011c576044359083821680920361011c576000549260ff8460081c1615938480956103eb575b80156103d4575b1561036a5784600160ff19831617600055610358575b506000549461028460ff8760081c1661027f81611688565b611688565b600180557fffffffffffffffffffffffff0000000000000000000000000000000000000000921682603354161760335581603454161760345560355416176035556040516102d181611635565b60008152600060208201526000606062eff10092836040820152015261ffff1960365416603655603755600060385561030657005b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166000557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160018152a1005b61ffff19166101011760005585610267565b608460405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b50303b1580156102515750600160ff821614610251565b50600160ff82161061024a565b3461011c57600060031936011261011c5760206040517f4b3b47150674b0de9e350179e12934407a716a86ccb0467b0468ca9fe61053148152f35b3461011c57600060031936011261011c5760206040517f689a3b4b571d498bd544dad28376fcbb1860a3763c569bee21a1bdf2da9580d28152f35b3461011c57602060031936011261011c57608061049161048c6115d9565b6119ec565b6104c0604051809260608091805115158452602081015115156020850152604081015160408501520151910152565bf35b3461011c57608060031936011261011c576104db611706565b1580610619575b8061060f575b80610600575b156105575733600052603b602052604060002060ff198154169055603c602052600060026040822082815582600182015501555b6040517f725f64dac887dde9519cdb1a010e7bb9fe6b8304f8ac9557798b56e9199e366e33918061055281611731565b0390a2005b6044358015806105f1575b6105c75733600052603b6020526040600020600160ff19825416179055603c602052604060002090610592611715565b1515825461ffff1960ff61ff006105a7611706565b151560081b16931691161717825560018201556002606435910155610522565b60046040517f5a4ea98b000000000000000000000000000000000000000000000000000000008152fd5b506105fa611706565b15610562565b50610609611715565b156104ee565b50606435156104e8565b50604435156104e2565b3461011c5760031960208136011261011c5767ffffffffffffffff6004351161011c5760e0906004353603011261011c5760246004350161066381611724565b15610e855761067c60a46004350160043560040161176a565b90505b61068d604460043501611724565b15610e71576106a660c46004350160043560040161176a565b90505b6106b7600435600401611724565b15610e5c5760646004350135915b6106d3600435600401611724565b15610e475760846004350135935b670de0b6b3a76400006106f486866117a0565b11610a8a5783151580928193610e3e575b8115610e23575b50610a8a5784151580938194610e1a575b8115610dff575b50610a8a5761073290611724565b9081610df7575b50610bb2575b61074d604460043501611724565b9081610baa575b506108a2575b610768600435600401611724565b61076e57005b801561083c575b81156107d6575b336000526039602052604060002055603a6020526040600020556040516064600435013581526084600435013560208201527fb585f7689006e053982f09109072da2f28d50b6786f32e09a1b2407ff002ff2660403392a2005b33600052603e6020526107ec6040600020611810565b6108347f928637e2e499fbf8cf92386e95156659014d26214df9547c8b0c7522aead1af261082460c46004350160043560040161176a565b604051339490928392908361196a565b0390a261077c565b33600052603d6020526108526040600020611810565b61089a7f87bbf83c4d9d2750a11d63e9a012db6d64650f43fe4dfbc51f2660e7164fcadb61088a60a46004350160043560040161176a565b6040513394909283929083611914565b0390a2610775565b6033546001600160a01b031691600090815b6108c860c46004350160043560040161176a565b9050831015610abc576108f76108f2846108ec60c46004350160043560040161176a565b906117c3565b611956565b906020610912856108ec60c46004350160043560040161176a565b0135916001600160a01b038116158015610ab4575b610a8a576001600160a01b03604051917faa3f22b80000000000000000000000000000000000000000000000000000000083521660048201526020816024818a5afa908115610a4457600091610a50575b506001600160a01b03604051917f1703e5f90000000000000000000000000000000000000000000000000000000083521660048201526020816024818a5afa908115610a4457600091610a0a575b50156109e0576001916109d8916117a0565b9201916108b4565b60046040517fd9f5b97a000000000000000000000000000000000000000000000000000000008152fd5b90506020813d602011610a3c575b81610a2560209383611665565b8101031261011c57610a36906116f9565b876109c6565b3d9150610a18565b6040513d6000823e3d90fd5b90506020813d602011610a82575b81610a6b60209383611665565b8101031261011c57610a7c906117e9565b87610978565b3d9150610a5e565b60046040517f6ab443c9000000000000000000000000000000000000000000000000000000008152fd5b508215610927565b909350670de0b6b3a7640000915003610a8a57610ae360c46004350160043560040161176a565b9033600052603e602052604060002090610afd838361188e565b9060005260206000206000915b838310610b555750505050610b4d7f928637e2e499fbf8cf92386e95156659014d26214df9547c8b0c7522aead1af261082460c46004350160043560040161176a565b0390a261075a565b60026040826001600160a01b03610b6d600195611956565b167fffffffffffffffffffffffff000000000000000000000000000000000000000086541617855560208101358486015501920192019190610b0a565b905083610754565b90916001600160a01b0360355416906000936000945b610bdc60a46004350160043560040161176a565b9050861015610d25576020610bff876108ec60a46004350160043560040161176a565b0135610c19876108ec60a46004350160043560040161176a565b35610c54575b15610a8a57610c4c6001916020610c44896108ec60a46004350160043560040161176a565b0135906117a0565b950194610bc8565b610c6c876108ec60a46004350160043560040161176a565b35604051907f6352211e0000000000000000000000000000000000000000000000000000000082526004820152602081602481895afa908115610a4457600091610ceb575b506001600160a01b03163314610c1f5760046040517f0b9378da000000000000000000000000000000000000000000000000000000008152fd5b90506020813d602011610d1d575b81610d0660209383611665565b8101031261011c57610d17906117e9565b88610cb1565b3d9150610cf9565b9194509291507ffffffffffffffffffffffffffffffffffffffffffffffffff21f494c589c000001610a8a57610d6560a46004350160043560040161176a565b9033600052603d602052604060002090610d7f838361188e565b9060005260206000206000915b838310610dd75750505050610dcf7f87bbf83c4d9d2750a11d63e9a012db6d64650f43fe4dfbc51f2660e7164fcadb61088a60a46004350160043560040161176a565b0390a261073f565b600260408260019335855560208101358486015501920192019190610d8c565b905084610739565b8615915081610e10575b5086610724565b9050151586610e09565b8015915061071d565b8515915081610e34575b508661070c565b9050151586610e2d565b80159150610705565b33600052603a602052604060002054936106e1565b336000526039602052604060002054916106c5565b33600052603e6020526040600020546106a9565b33600052603d60205260406000205461067f565b3461011c5760208060031936011261011c57610eb36115d9565b90610ebc6119c7565b506001600160a01b0391828116918260005260398152604060002054603a8252610eeb604060002054936119ec565b84600052603d8352604060002091825495610f0587611a62565b96610f136040519889611665565b8088528588018095600052866000206000915b8383106110835750505050600052603b845260ff6040600020541695603e8552604060002095865493610f5885611a62565b94610f666040519687611665565b8086528786018099600052886000206000915b8d84841061104e575050505050610fcf906040519961012093848c01968c52898c0152151560408b015260608a019060608091805115158452602081015115156020850152604081015160408501520151910152565b60e0880152518091526101408601929060005b81811061102f5750505084820361010086015251808252908201929160005b82811061100e5785850386f35b83518051881686528201518583015260409094019392810192600101611001565b8251805186528601518587015260409094019391850191600101610fe2565b8b60019260029260409e9d9e519061106582611603565b86541681528486015483820152815201920192019190999899610f79565b60028960019260409b9a9b5161109881611603565b855481528486015483820152815201920192019190979697610f26565b3461011c57602060031936011261011c5760043567ffffffffffffffff80821161011c573660238301121561011c57816004013590811161011c5760249160058184013684831b840186011161011c57604460206001600160a01b0360335416604051928380927f91d148540000000000000000000000000000000000000000000000000000000082527f4b3b47150674b0de9e350179e12934407a716a86ccb0467b0468ca9fe61053146004830152338b8301525afa908115610a44576000916111e4575b50156111ba57611189611aad565b60005b8481106111995760018055005b806111b46111af88600194871b88010185611a7a565b611b32565b0161118c565b60046040517f4ca88867000000000000000000000000000000000000000000000000000000008152fd5b90506020813d8211611215575b816111fe60209383611665565b8101031261011c5761120f906116f9565b8661117b565b3d91506111f1565b3461011c57602060031936011261011c576001600160a01b0361123e6115d9565b1660005260396020526020604060002054604051908152f35b3461011c57600060031936011261011c5760206001600160a01b0360355416604051908152f35b3461011c57600060031936011261011c5760206001600160a01b0360335416604051908152f35b3461011c57608060031936011261011c57604460206001600160a01b0360335416604051928380927f91d148540000000000000000000000000000000000000000000000000000000082527f689a3b4b571d498bd544dad28376fcbb1860a3763c569bee21a1bdf2da9580d260048301523360248301525afa908115610a44576000916113fc575b50156111ba5761133b611706565b15806113f2575b806113e8575b806113d9575b6105c7576044358015806113ca575b6105c757611369611715565b151560365461ffff1960ff61ff0061137f611706565b151560081b169316911617176036556037556064356038557f2dc661669d339e3e160448267a8c01c178b6193f5cb3b206c57f3732a6da0594604051806113c581611731565b0390a1005b506113d3611706565b1561135d565b506113e2611715565b1561134e565b5060643515611348565b5060443515611342565b90506020813d821161142d575b8161141660209383611665565b8101031261011c57611427906116f9565b8161132d565b3d9150611409565b3461011c5760031960208136011261011c576004359067ffffffffffffffff821161011c5760609082600401923603011261011c5761148790611476611aad565b6111af61148282611956565b61292d565b60018055005b3461011c57606060031936011261011c576114a66115d9565b60243590604435906114b6611aad565b6001600160a01b03906114cc826033541661292d565b16806000526020603981526040600020546114e75760018055005b603d80825260406000209485546114fd81611a62565b9661150b6040519889611665565b818852600090815284812085808a015b8484106115ab57505050505060005b865181101561159b5780826115416001938a611b02565b51511461154f575b0161152a565b8560005283855286611565826040600020611b16565b5055857f184c00bc284cb3ff998f3eed0fd31c5fdbeedca85afe6ef16d2db14715ee3faf604080518681528a89820152a2611549565b5050505050505080808080611487565b6001916002916040516115bd81611603565b855481528486015483820152815201920192019190869061151b565b600435906001600160a01b038216820361011c57565b35906001600160a01b038216820361011c57565b6040810190811067ffffffffffffffff82111761161f57604052565b634e487b7160e01b600052604160045260246000fd5b6080810190811067ffffffffffffffff82111761161f57604052565b67ffffffffffffffff811161161f57604052565b90601f601f19910116810190811067ffffffffffffffff82111761161f57604052565b1561168f57565b608460405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152fd5b5190811515820361011c57565b602435801515810361011c5790565b600435801515810361011c5790565b35801515810361011c5790565b90608082019160043580151580910361011c57815260243580151580910361011c57602082015260443560408201526060606435910152565b903590601e198136030182121561011c570180359067ffffffffffffffff821161011c57602001918160061b3603831361011c57565b919082018092116117ad57565b634e487b7160e01b600052601160045260246000fd5b91908110156117d35760061b0190565b634e487b7160e01b600052603260045260246000fd5b51906001600160a01b038216820361011c57565b818102929181159184041417156117ad57565b8054906000908181558261182357505050565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316830361187a5781526020812091600190811b8301925b8381106118695750505050565b80836002925583838201550161185c565b602482634e487b7160e01b81526011600452fd5b68010000000000000000821161161f5780548282558083106118af57505050565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80821682036117ad57831683036117ad57600091825260208220600191821b810193821b015b8381106119035750505050565b8083600292558383820155016118f6565b602080825280820184905260409182019390929160005b82811061193a57505050505090565b833586528484013586860152948101949281019260010161192b565b356001600160a01b038116810361011c5790565b602080825280820184905260409182019390929160005b82811061199057505050505090565b90919293959482806001926001600160a01b036119ac896115ef565b168152878a01358a8201529798970196950193929101611981565b604051906119d482611635565b60006060838281528260208201528260408201520152565b6001600160a01b03906119fd6119c7565b5016600052603b60205260ff60406000205416600014611a5b57603c60205260406000205b600260405191611a3183611635565b60ff81548181161515855260081c1615156020840152600181015460408401520154606082015290565b6036611a22565b67ffffffffffffffff811161161f5760051b60200190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18136030182121561011c570190565b600260015414611abe576002600155565b606460405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b80518210156117d35760209160051b010190565b80548210156117d35760005260206000209060011b0190600090565b6001600160a01b0360335416611b4782611956565b906020830135601e1990818536030181121561011c57840180359067ffffffffffffffff821161011c57602001918160051b3603831361011c57611b8e6040870187611a7a565b9180604051967f1e95542c0000000000000000000000000000000000000000000000000000000088526001600160a01b036064890191166004890152606060248901525260848601939060005b8181106125785750505060031985840301604486015281358352602082013560208401526040820135908236030181121561011c57019067ffffffffffffffff82351161011c57602082019180353603831361011c578482601f19601f608094604097606089879901528135606086015281359087860137600086823586010152350116010301816000855af1918215610a4457600090819361253e575b50611c8483826117a0565b15612538576001600160a01b0360345416906001600160a01b036035541692611caf61048c87611956565b91611cba86826117a0565b604051907f23b872dd0000000000000000000000000000000000000000000000000000000060208301528360248301523060448301526064820152606481528060a081011067ffffffffffffffff60a08301111761161f578060a0611d23920160405285612706565b80612277575b5084611d38575b505050505050565b6001600160a01b03611d4987611956565b16600052603e6020526040600020948554611d6381611a62565b96611d716040519889611665565b818852602088019060005260206000206000915b838310612241575050505085519560005b878110611da557505050611d30565b611daf8183611b02565b51906001600160a01b03825116604051907faa3f22b80000000000000000000000000000000000000000000000000000000082526004820152602081602481895afa908115610a4457600091612207575b50670de0b6b3a7640000611e188660208601516117fd565b04906040517f1703e5f90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526020816024818b5afa8015610a445789916000916121cb575b501561209c57506001600160a01b03604051917fe839663b000000000000000000000000000000000000000000000000000000008352166004820152610100816024818a5afa908115610a4457600091611fb2575b50606001516001600160a01b0316611ed682828b6125a0565b803b1561011c576040517fb66503cf0000000000000000000000000000000000000000000000000000000081526001600160a01b038a16600482015260248101839052906000908290604490829084905af1908115610a4457600194611f996001600160a01b038f611f719082957f89623c97a68fe5cdd5d4d093255dd12eb6f1a4c01666e3a15d062ad042216b9397611fa3575b50611956565b92511694604051938493169583602090939291936001600160a01b0360408201951681520152565b0390a25b01611d96565b611fac90611651565b38611f6b565b610100813d61010011612094575b81611fce6101009383611665565b8101031261209057604051918261010081011067ffffffffffffffff6101008501111761207c5750816060916101006001600160a01b039401604052612013816116f9565b8252612021602082016116f9565b6020830152612032604082016117e9565b60408301526120428382016117e9565b83830152612052608082016117e9565b608083015260a081015160a083015260c0808201519083015260e080910151908201529150611ebd565b80634e487b7160e01b602492526041600452fd5b5080fd5b3d9150611fc0565b939190506120ab818b8b6125a0565b60208c8b6120bd604088015192611956565b8751848901516060909901516040517f42c15c870000000000000000000000000000000000000000000000000000000081526004810188905260248101959095526001600160a01b0392909216604485015215156064840152961515608483015260a482019690965294859060c49082906000905af1918215610a44578c94600093612186575b506001600160a01b037fb3efb2d293d7d90e619d71b4c0cdc02c87368c44a86a4512fc4141924ade02dd9161217e82611f71600199611956565b0390a3611f9d565b91939450916020823d6020116121c3575b816121a460209383611665565b810103126121c05750518b939290916001600160a01b03612144565b80fd5b3d9150612197565b91506020823d6020116121ff575b816121e660209383611665565b810103126121c057506121f989916116f9565b38611e68565b3d91506121d9565b906020823d602011612239575b8161222160209383611665565b810103126121c05750612233906117e9565b38611e00565b3d9150612214565b6002602060019260405161225481611603565b6001600160a01b0386541681528486015483820152815201920192019190611d85565b6122828186866125a0565b6001600160a01b0361229388611956565b16600052603d60205260406000209081546122ad81611a62565b926122bb6040519485611665565b818452602084019060005260206000206000915b83831061250b575050505081519160005b8381106122ef57505050611d29565b6122f98183611b02565b51670de0b6b3a76400006123118560208401516117fd565b048151801560001461246057508760208d8c612331604085015192611956565b8451848601516060909601516040517f42c15c870000000000000000000000000000000000000000000000000000000081526004810189905260248101959095526001600160a01b0392909216604485015215156064840152931515608483015260a482019390935291829060c49082906000905af18015610a44578d9160009161242a575b5060206001600160a01b03612417869594847f32affd6196df3c9436a016339e74fc3bc660962e9f62816e1d5bee46377a6e149560019a9952836123fa83611956565b16600052603d8552612410896040600020611b16565b5055611956565b9451946040519485521692a35b016122e0565b91506020823d602011612458575b8161244560209383611665565b810103126121c05750518c9060206123b7565b3d9150612438565b9291908a3b1561011c5760008b6084829660405197889384927fbdb78cf600000000000000000000000000000000000000000000000000000000845260048401528660248401528160448401528160648401525af1938415610a445760206001600160a01b038f6124fa907fd4612a0ed11d22ae81a5e7a3372dec379d6bbc85813fa8cb55ec7064201351cf94600199611fa35750611956565b9451946040519485521692a3612424565b6002602060019260405161251e81611603565b8554815284860154838201528152019201920191906122cf565b50505050565b92506040833d604011612570575b8161255960409383611665565b810103126121c05750602082519201519138611c79565b3d915061254c565b9091946020806001926001600160a01b036125928a6115ef565b168152019601929101611bdb565b91909181158015612678575b1561260e576040517f095ea7b30000000000000000000000000000000000000000000000000000000060208201526001600160a01b0393909316602484015260448084019290925290825261260c9190612607606483611665565b612706565b565b608460405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152fd5b506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526020816044816001600160a01b03808916602483015286165afa908115610a44576000916126d5575b50156125ac565b906020823d82116126fe575b816126ee60209383611665565b810103126121c0575051386126ce565b3d91506126e1565b6001600160a01b03169060405161271c81611603565b6020928382527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564848301526000808486829651910182855af13d15612853573d9167ffffffffffffffff831161283f57906127979392916040519261278a88601f19601f8401160185611665565b83523d868885013e61285d565b9081519083821592831561281d575b5050509050156127b35750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b8480929394500103126121c057508161283691016116f9565b803883816127a6565b602485634e487b7160e01b81526041600452fd5b9061279792916060915b919290156128be5750815115612871575090565b3b1561287a5790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156128d15750805190602001fd5b6040519062461bcd60e51b825281602080600483015282519283602484015260005b84811061291657505050601f19601f836000604480968601015201168101030190fd5b8181018301518682016044015285935082016128f3565b6001600160a01b031633036111ba5756fea164736f6c6343000813000a
Deployed Bytecode
0x6080604052600436101561001257600080fd5b60003560e01c8063173f9ea31461148d57806318518521146114355780632618d360146112a557806346c96aac1461127e5780634f2bfe5b14611257578063537afe4d1461121d5780635650ba77146110b55780636386c1c714610e99578063902f039d14610623578063992cb9c3146104c2578063a4015b4c1461046e578063bd6464ce14610433578063bf4cd7ba146103f8578063c0c53b8b146101f8578063c8856fd7146101b7578063fc0c546a14610190578063fe3a04c4146101215763ff88710c146100e257600080fd5b3461011c57602060031936011261011c576001600160a01b036101036115d9565b16600052603a6020526020604060002054604051908152f35b600080fd5b3461011c57604060031936011261011c57604061013c6115d9565b6001600160a01b03602435911690816000526039602052610183670de0b6b3a7640000918261016f8287600020546117fd565b0493600052603a60205284600020546117fd565b0482519182526020820152f35b3461011c57600060031936011261011c5760206001600160a01b0360345416604051908152f35b3461011c57600060031936011261011c5760806036546037546038549060ff604051938181161515855260081c161515602084015260408301526060820152f35b3461011c57606060031936011261011c576102116115d9565b6024356001600160a01b039182821680920361011c576044359083821680920361011c576000549260ff8460081c1615938480956103eb575b80156103d4575b1561036a5784600160ff19831617600055610358575b506000549461028460ff8760081c1661027f81611688565b611688565b600180557fffffffffffffffffffffffff0000000000000000000000000000000000000000921682603354161760335581603454161760345560355416176035556040516102d181611635565b60008152600060208201526000606062eff10092836040820152015261ffff1960365416603655603755600060385561030657005b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166000557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160018152a1005b61ffff19166101011760005585610267565b608460405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b50303b1580156102515750600160ff821614610251565b50600160ff82161061024a565b3461011c57600060031936011261011c5760206040517f4b3b47150674b0de9e350179e12934407a716a86ccb0467b0468ca9fe61053148152f35b3461011c57600060031936011261011c5760206040517f689a3b4b571d498bd544dad28376fcbb1860a3763c569bee21a1bdf2da9580d28152f35b3461011c57602060031936011261011c57608061049161048c6115d9565b6119ec565b6104c0604051809260608091805115158452602081015115156020850152604081015160408501520151910152565bf35b3461011c57608060031936011261011c576104db611706565b1580610619575b8061060f575b80610600575b156105575733600052603b602052604060002060ff198154169055603c602052600060026040822082815582600182015501555b6040517f725f64dac887dde9519cdb1a010e7bb9fe6b8304f8ac9557798b56e9199e366e33918061055281611731565b0390a2005b6044358015806105f1575b6105c75733600052603b6020526040600020600160ff19825416179055603c602052604060002090610592611715565b1515825461ffff1960ff61ff006105a7611706565b151560081b16931691161717825560018201556002606435910155610522565b60046040517f5a4ea98b000000000000000000000000000000000000000000000000000000008152fd5b506105fa611706565b15610562565b50610609611715565b156104ee565b50606435156104e8565b50604435156104e2565b3461011c5760031960208136011261011c5767ffffffffffffffff6004351161011c5760e0906004353603011261011c5760246004350161066381611724565b15610e855761067c60a46004350160043560040161176a565b90505b61068d604460043501611724565b15610e71576106a660c46004350160043560040161176a565b90505b6106b7600435600401611724565b15610e5c5760646004350135915b6106d3600435600401611724565b15610e475760846004350135935b670de0b6b3a76400006106f486866117a0565b11610a8a5783151580928193610e3e575b8115610e23575b50610a8a5784151580938194610e1a575b8115610dff575b50610a8a5761073290611724565b9081610df7575b50610bb2575b61074d604460043501611724565b9081610baa575b506108a2575b610768600435600401611724565b61076e57005b801561083c575b81156107d6575b336000526039602052604060002055603a6020526040600020556040516064600435013581526084600435013560208201527fb585f7689006e053982f09109072da2f28d50b6786f32e09a1b2407ff002ff2660403392a2005b33600052603e6020526107ec6040600020611810565b6108347f928637e2e499fbf8cf92386e95156659014d26214df9547c8b0c7522aead1af261082460c46004350160043560040161176a565b604051339490928392908361196a565b0390a261077c565b33600052603d6020526108526040600020611810565b61089a7f87bbf83c4d9d2750a11d63e9a012db6d64650f43fe4dfbc51f2660e7164fcadb61088a60a46004350160043560040161176a565b6040513394909283929083611914565b0390a2610775565b6033546001600160a01b031691600090815b6108c860c46004350160043560040161176a565b9050831015610abc576108f76108f2846108ec60c46004350160043560040161176a565b906117c3565b611956565b906020610912856108ec60c46004350160043560040161176a565b0135916001600160a01b038116158015610ab4575b610a8a576001600160a01b03604051917faa3f22b80000000000000000000000000000000000000000000000000000000083521660048201526020816024818a5afa908115610a4457600091610a50575b506001600160a01b03604051917f1703e5f90000000000000000000000000000000000000000000000000000000083521660048201526020816024818a5afa908115610a4457600091610a0a575b50156109e0576001916109d8916117a0565b9201916108b4565b60046040517fd9f5b97a000000000000000000000000000000000000000000000000000000008152fd5b90506020813d602011610a3c575b81610a2560209383611665565b8101031261011c57610a36906116f9565b876109c6565b3d9150610a18565b6040513d6000823e3d90fd5b90506020813d602011610a82575b81610a6b60209383611665565b8101031261011c57610a7c906117e9565b87610978565b3d9150610a5e565b60046040517f6ab443c9000000000000000000000000000000000000000000000000000000008152fd5b508215610927565b909350670de0b6b3a7640000915003610a8a57610ae360c46004350160043560040161176a565b9033600052603e602052604060002090610afd838361188e565b9060005260206000206000915b838310610b555750505050610b4d7f928637e2e499fbf8cf92386e95156659014d26214df9547c8b0c7522aead1af261082460c46004350160043560040161176a565b0390a261075a565b60026040826001600160a01b03610b6d600195611956565b167fffffffffffffffffffffffff000000000000000000000000000000000000000086541617855560208101358486015501920192019190610b0a565b905083610754565b90916001600160a01b0360355416906000936000945b610bdc60a46004350160043560040161176a565b9050861015610d25576020610bff876108ec60a46004350160043560040161176a565b0135610c19876108ec60a46004350160043560040161176a565b35610c54575b15610a8a57610c4c6001916020610c44896108ec60a46004350160043560040161176a565b0135906117a0565b950194610bc8565b610c6c876108ec60a46004350160043560040161176a565b35604051907f6352211e0000000000000000000000000000000000000000000000000000000082526004820152602081602481895afa908115610a4457600091610ceb575b506001600160a01b03163314610c1f5760046040517f0b9378da000000000000000000000000000000000000000000000000000000008152fd5b90506020813d602011610d1d575b81610d0660209383611665565b8101031261011c57610d17906117e9565b88610cb1565b3d9150610cf9565b9194509291507ffffffffffffffffffffffffffffffffffffffffffffffffff21f494c589c000001610a8a57610d6560a46004350160043560040161176a565b9033600052603d602052604060002090610d7f838361188e565b9060005260206000206000915b838310610dd75750505050610dcf7f87bbf83c4d9d2750a11d63e9a012db6d64650f43fe4dfbc51f2660e7164fcadb61088a60a46004350160043560040161176a565b0390a261073f565b600260408260019335855560208101358486015501920192019190610d8c565b905084610739565b8615915081610e10575b5086610724565b9050151586610e09565b8015915061071d565b8515915081610e34575b508661070c565b9050151586610e2d565b80159150610705565b33600052603a602052604060002054936106e1565b336000526039602052604060002054916106c5565b33600052603e6020526040600020546106a9565b33600052603d60205260406000205461067f565b3461011c5760208060031936011261011c57610eb36115d9565b90610ebc6119c7565b506001600160a01b0391828116918260005260398152604060002054603a8252610eeb604060002054936119ec565b84600052603d8352604060002091825495610f0587611a62565b96610f136040519889611665565b8088528588018095600052866000206000915b8383106110835750505050600052603b845260ff6040600020541695603e8552604060002095865493610f5885611a62565b94610f666040519687611665565b8086528786018099600052886000206000915b8d84841061104e575050505050610fcf906040519961012093848c01968c52898c0152151560408b015260608a019060608091805115158452602081015115156020850152604081015160408501520151910152565b60e0880152518091526101408601929060005b81811061102f5750505084820361010086015251808252908201929160005b82811061100e5785850386f35b83518051881686528201518583015260409094019392810192600101611001565b8251805186528601518587015260409094019391850191600101610fe2565b8b60019260029260409e9d9e519061106582611603565b86541681528486015483820152815201920192019190999899610f79565b60028960019260409b9a9b5161109881611603565b855481528486015483820152815201920192019190979697610f26565b3461011c57602060031936011261011c5760043567ffffffffffffffff80821161011c573660238301121561011c57816004013590811161011c5760249160058184013684831b840186011161011c57604460206001600160a01b0360335416604051928380927f91d148540000000000000000000000000000000000000000000000000000000082527f4b3b47150674b0de9e350179e12934407a716a86ccb0467b0468ca9fe61053146004830152338b8301525afa908115610a44576000916111e4575b50156111ba57611189611aad565b60005b8481106111995760018055005b806111b46111af88600194871b88010185611a7a565b611b32565b0161118c565b60046040517f4ca88867000000000000000000000000000000000000000000000000000000008152fd5b90506020813d8211611215575b816111fe60209383611665565b8101031261011c5761120f906116f9565b8661117b565b3d91506111f1565b3461011c57602060031936011261011c576001600160a01b0361123e6115d9565b1660005260396020526020604060002054604051908152f35b3461011c57600060031936011261011c5760206001600160a01b0360355416604051908152f35b3461011c57600060031936011261011c5760206001600160a01b0360335416604051908152f35b3461011c57608060031936011261011c57604460206001600160a01b0360335416604051928380927f91d148540000000000000000000000000000000000000000000000000000000082527f689a3b4b571d498bd544dad28376fcbb1860a3763c569bee21a1bdf2da9580d260048301523360248301525afa908115610a44576000916113fc575b50156111ba5761133b611706565b15806113f2575b806113e8575b806113d9575b6105c7576044358015806113ca575b6105c757611369611715565b151560365461ffff1960ff61ff0061137f611706565b151560081b169316911617176036556037556064356038557f2dc661669d339e3e160448267a8c01c178b6193f5cb3b206c57f3732a6da0594604051806113c581611731565b0390a1005b506113d3611706565b1561135d565b506113e2611715565b1561134e565b5060643515611348565b5060443515611342565b90506020813d821161142d575b8161141660209383611665565b8101031261011c57611427906116f9565b8161132d565b3d9150611409565b3461011c5760031960208136011261011c576004359067ffffffffffffffff821161011c5760609082600401923603011261011c5761148790611476611aad565b6111af61148282611956565b61292d565b60018055005b3461011c57606060031936011261011c576114a66115d9565b60243590604435906114b6611aad565b6001600160a01b03906114cc826033541661292d565b16806000526020603981526040600020546114e75760018055005b603d80825260406000209485546114fd81611a62565b9661150b6040519889611665565b818852600090815284812085808a015b8484106115ab57505050505060005b865181101561159b5780826115416001938a611b02565b51511461154f575b0161152a565b8560005283855286611565826040600020611b16565b5055857f184c00bc284cb3ff998f3eed0fd31c5fdbeedca85afe6ef16d2db14715ee3faf604080518681528a89820152a2611549565b5050505050505080808080611487565b6001916002916040516115bd81611603565b855481528486015483820152815201920192019190869061151b565b600435906001600160a01b038216820361011c57565b35906001600160a01b038216820361011c57565b6040810190811067ffffffffffffffff82111761161f57604052565b634e487b7160e01b600052604160045260246000fd5b6080810190811067ffffffffffffffff82111761161f57604052565b67ffffffffffffffff811161161f57604052565b90601f601f19910116810190811067ffffffffffffffff82111761161f57604052565b1561168f57565b608460405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152fd5b5190811515820361011c57565b602435801515810361011c5790565b600435801515810361011c5790565b35801515810361011c5790565b90608082019160043580151580910361011c57815260243580151580910361011c57602082015260443560408201526060606435910152565b903590601e198136030182121561011c570180359067ffffffffffffffff821161011c57602001918160061b3603831361011c57565b919082018092116117ad57565b634e487b7160e01b600052601160045260246000fd5b91908110156117d35760061b0190565b634e487b7160e01b600052603260045260246000fd5b51906001600160a01b038216820361011c57565b818102929181159184041417156117ad57565b8054906000908181558261182357505050565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316830361187a5781526020812091600190811b8301925b8381106118695750505050565b80836002925583838201550161185c565b602482634e487b7160e01b81526011600452fd5b68010000000000000000821161161f5780548282558083106118af57505050565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80821682036117ad57831683036117ad57600091825260208220600191821b810193821b015b8381106119035750505050565b8083600292558383820155016118f6565b602080825280820184905260409182019390929160005b82811061193a57505050505090565b833586528484013586860152948101949281019260010161192b565b356001600160a01b038116810361011c5790565b602080825280820184905260409182019390929160005b82811061199057505050505090565b90919293959482806001926001600160a01b036119ac896115ef565b168152878a01358a8201529798970196950193929101611981565b604051906119d482611635565b60006060838281528260208201528260408201520152565b6001600160a01b03906119fd6119c7565b5016600052603b60205260ff60406000205416600014611a5b57603c60205260406000205b600260405191611a3183611635565b60ff81548181161515855260081c1615156020840152600181015460408401520154606082015290565b6036611a22565b67ffffffffffffffff811161161f5760051b60200190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18136030182121561011c570190565b600260015414611abe576002600155565b606460405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b80518210156117d35760209160051b010190565b80548210156117d35760005260206000209060011b0190600090565b6001600160a01b0360335416611b4782611956565b906020830135601e1990818536030181121561011c57840180359067ffffffffffffffff821161011c57602001918160051b3603831361011c57611b8e6040870187611a7a565b9180604051967f1e95542c0000000000000000000000000000000000000000000000000000000088526001600160a01b036064890191166004890152606060248901525260848601939060005b8181106125785750505060031985840301604486015281358352602082013560208401526040820135908236030181121561011c57019067ffffffffffffffff82351161011c57602082019180353603831361011c578482601f19601f608094604097606089879901528135606086015281359087860137600086823586010152350116010301816000855af1918215610a4457600090819361253e575b50611c8483826117a0565b15612538576001600160a01b0360345416906001600160a01b036035541692611caf61048c87611956565b91611cba86826117a0565b604051907f23b872dd0000000000000000000000000000000000000000000000000000000060208301528360248301523060448301526064820152606481528060a081011067ffffffffffffffff60a08301111761161f578060a0611d23920160405285612706565b80612277575b5084611d38575b505050505050565b6001600160a01b03611d4987611956565b16600052603e6020526040600020948554611d6381611a62565b96611d716040519889611665565b818852602088019060005260206000206000915b838310612241575050505085519560005b878110611da557505050611d30565b611daf8183611b02565b51906001600160a01b03825116604051907faa3f22b80000000000000000000000000000000000000000000000000000000082526004820152602081602481895afa908115610a4457600091612207575b50670de0b6b3a7640000611e188660208601516117fd565b04906040517f1703e5f90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526020816024818b5afa8015610a445789916000916121cb575b501561209c57506001600160a01b03604051917fe839663b000000000000000000000000000000000000000000000000000000008352166004820152610100816024818a5afa908115610a4457600091611fb2575b50606001516001600160a01b0316611ed682828b6125a0565b803b1561011c576040517fb66503cf0000000000000000000000000000000000000000000000000000000081526001600160a01b038a16600482015260248101839052906000908290604490829084905af1908115610a4457600194611f996001600160a01b038f611f719082957f89623c97a68fe5cdd5d4d093255dd12eb6f1a4c01666e3a15d062ad042216b9397611fa3575b50611956565b92511694604051938493169583602090939291936001600160a01b0360408201951681520152565b0390a25b01611d96565b611fac90611651565b38611f6b565b610100813d61010011612094575b81611fce6101009383611665565b8101031261209057604051918261010081011067ffffffffffffffff6101008501111761207c5750816060916101006001600160a01b039401604052612013816116f9565b8252612021602082016116f9565b6020830152612032604082016117e9565b60408301526120428382016117e9565b83830152612052608082016117e9565b608083015260a081015160a083015260c0808201519083015260e080910151908201529150611ebd565b80634e487b7160e01b602492526041600452fd5b5080fd5b3d9150611fc0565b939190506120ab818b8b6125a0565b60208c8b6120bd604088015192611956565b8751848901516060909901516040517f42c15c870000000000000000000000000000000000000000000000000000000081526004810188905260248101959095526001600160a01b0392909216604485015215156064840152961515608483015260a482019690965294859060c49082906000905af1918215610a44578c94600093612186575b506001600160a01b037fb3efb2d293d7d90e619d71b4c0cdc02c87368c44a86a4512fc4141924ade02dd9161217e82611f71600199611956565b0390a3611f9d565b91939450916020823d6020116121c3575b816121a460209383611665565b810103126121c05750518b939290916001600160a01b03612144565b80fd5b3d9150612197565b91506020823d6020116121ff575b816121e660209383611665565b810103126121c057506121f989916116f9565b38611e68565b3d91506121d9565b906020823d602011612239575b8161222160209383611665565b810103126121c05750612233906117e9565b38611e00565b3d9150612214565b6002602060019260405161225481611603565b6001600160a01b0386541681528486015483820152815201920192019190611d85565b6122828186866125a0565b6001600160a01b0361229388611956565b16600052603d60205260406000209081546122ad81611a62565b926122bb6040519485611665565b818452602084019060005260206000206000915b83831061250b575050505081519160005b8381106122ef57505050611d29565b6122f98183611b02565b51670de0b6b3a76400006123118560208401516117fd565b048151801560001461246057508760208d8c612331604085015192611956565b8451848601516060909601516040517f42c15c870000000000000000000000000000000000000000000000000000000081526004810189905260248101959095526001600160a01b0392909216604485015215156064840152931515608483015260a482019390935291829060c49082906000905af18015610a44578d9160009161242a575b5060206001600160a01b03612417869594847f32affd6196df3c9436a016339e74fc3bc660962e9f62816e1d5bee46377a6e149560019a9952836123fa83611956565b16600052603d8552612410896040600020611b16565b5055611956565b9451946040519485521692a35b016122e0565b91506020823d602011612458575b8161244560209383611665565b810103126121c05750518c9060206123b7565b3d9150612438565b9291908a3b1561011c5760008b6084829660405197889384927fbdb78cf600000000000000000000000000000000000000000000000000000000845260048401528660248401528160448401528160648401525af1938415610a445760206001600160a01b038f6124fa907fd4612a0ed11d22ae81a5e7a3372dec379d6bbc85813fa8cb55ec7064201351cf94600199611fa35750611956565b9451946040519485521692a3612424565b6002602060019260405161251e81611603565b8554815284860154838201528152019201920191906122cf565b50505050565b92506040833d604011612570575b8161255960409383611665565b810103126121c05750602082519201519138611c79565b3d915061254c565b9091946020806001926001600160a01b036125928a6115ef565b168152019601929101611bdb565b91909181158015612678575b1561260e576040517f095ea7b30000000000000000000000000000000000000000000000000000000060208201526001600160a01b0393909316602484015260448084019290925290825261260c9190612607606483611665565b612706565b565b608460405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152fd5b506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526020816044816001600160a01b03808916602483015286165afa908115610a44576000916126d5575b50156125ac565b906020823d82116126fe575b816126ee60209383611665565b810103126121c0575051386126ce565b3d91506126e1565b6001600160a01b03169060405161271c81611603565b6020928382527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564848301526000808486829651910182855af13d15612853573d9167ffffffffffffffff831161283f57906127979392916040519261278a88601f19601f8401160185611665565b83523d868885013e61285d565b9081519083821592831561281d575b5050509050156127b35750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b8480929394500103126121c057508161283691016116f9565b803883816127a6565b602485634e487b7160e01b81526041600452fd5b9061279792916060915b919290156128be5750815115612871575090565b3b1561287a5790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156128d15750805190602001fd5b6040519062461bcd60e51b825281602080600483015282519283602484015260005b84811061291657505050601f19601f836000604480968601015201168101030190fd5b8181018301518682016044015285935082016128f3565b6001600160a01b031633036111ba5756fea164736f6c6343000813000a
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in HYPE
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.