Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
TrackingRewardStreams
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 20000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {Set, SetStorage} from "evc/Set.sol"; import {BaseRewardStreams} from "./BaseRewardStreams.sol"; import {ITrackingRewardStreams} from "./interfaces/IRewardStreams.sol"; /// @title TrackingRewardStreams /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This contract inherits from `BaseRewardStreams` and implements `ITrackingRewardStreams`. /// It allows for the rewards to be distributed to the rewarded token holders without requiring explicit staking. /// The rewarded token contract must implement `IBalanceTracker` and the `balanceTrackerHook` function. /// `balanceTrackerHook` must be called with: /// - the account's new balance when account's balance changes, /// - the current account's balance when the balance forwarding is enabled, /// - the account's balance of 0 when the balance forwarding is disabled. contract TrackingRewardStreams is BaseRewardStreams, ITrackingRewardStreams { using Set for SetStorage; /// @notice Constructor for the TrackingRewardStreams contract. /// @param evc The Ethereum Vault Connector contract. /// @param epochDuration The duration of an epoch. constructor(address evc, uint48 epochDuration) BaseRewardStreams(evc, epochDuration) {} /// @notice Executes the balance tracking hook for an account /// @param account The account address to execute the hook for /// @param newAccountBalance The new balance of the account /// @param forfeitRecentReward Whether to forfeit the most recent reward and not update the accumulator. Ignored /// when the new balance is greater than the current balance. function balanceTrackerHook( address account, uint256 newAccountBalance, bool forfeitRecentReward ) external override { address rewarded = msg.sender; AccountStorage storage accountStorage = accounts[account][rewarded]; uint256 currentAccountBalance = accountStorage.balance; address[] memory rewards = accountStorage.enabledRewards.get(); if (newAccountBalance > currentAccountBalance) forfeitRecentReward = false; for (uint256 i = 0; i < rewards.length; ++i) { address reward = rewards[i]; DistributionStorage storage distributionStorage = distributions[rewarded][reward]; // We always allocate rewards before updating any balances. updateRewardInternal( distributionStorage, accountStorage.earned[reward], rewarded, reward, currentAccountBalance, forfeitRecentReward ); distributionStorage.totalEligible = distributionStorage.totalEligible + newAccountBalance - currentAccountBalance; } accountStorage.balance = newAccountBalance; emit BalanceUpdated(account, rewarded, currentAccountBalance, newAccountBalance); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @dev Represents the maximum number of elements that can be stored in the set. /// Must not exceed 255 due to the uint8 data type limit. uint8 constant SET_MAX_ELEMENTS = 10; /// @title ElementStorage /// @notice This struct is used to store the value and stamp of an element. /// @dev The stamp field is used to keep the storage slot non-zero when the element is removed. /// @dev It allows for cheaper SSTORE when an element is inserted. struct ElementStorage { /// @notice The value of the element. address value; /// @notice The stamp of the element. uint96 stamp; } /// @title SetStorage /// @notice This struct is used to store the set data. /// @dev To optimize the gas consumption, firstElement is stored in the same storage slot as the numElements /// @dev so that for sets with one element, only one storage slot has to be read/written. To keep the elements /// @dev array indexing consistent and because the first element is stored outside of the array, the elements[0] /// @dev is not utilized. The stamp field is used to keep the storage slot non-zero when the element is removed. /// @dev It allows for cheaper SSTORE when an element is inserted. struct SetStorage { /// @notice The number of elements in the set. uint8 numElements; /// @notice The first element in the set. address firstElement; /// @notice The metadata of the set. uint80 metadata; /// @notice The stamp of the set. uint8 stamp; /// @notice The array of elements in the set. Stores the elements starting from index 1. ElementStorage[SET_MAX_ELEMENTS] elements; } /// @title Set /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This library provides functions for managing sets of addresses. /// @dev The maximum number of elements in the set is defined by the constant SET_MAX_ELEMENTS. library Set { error TooManyElements(); error InvalidIndex(); uint8 internal constant EMPTY_ELEMENT_OFFSET = 1; // must be 1 uint8 internal constant DUMMY_STAMP = 1; /// @notice Initializes the set by setting the stamp field of the SetStorage and the stamp field of elements to /// DUMMY_STAMP. /// @dev The stamp field is used to keep the storage slot non-zero when the element is removed. It allows for /// cheaper SSTORE when an element is inserted. /// @param setStorage The set storage whose stamp fields will be initialized. function initialize(SetStorage storage setStorage) internal { setStorage.stamp = DUMMY_STAMP; for (uint256 i = EMPTY_ELEMENT_OFFSET; i < SET_MAX_ELEMENTS; ++i) { setStorage.elements[i].stamp = DUMMY_STAMP; } } /// @notice Inserts an element and returns information whether the element was inserted or not. /// @dev Reverts if the set is full but the element is not in the set storage. /// @param setStorage The set storage to which the element will be inserted. /// @param element The element to be inserted. /// @return A boolean value that indicates whether the element was inserted or not. If the element was already in /// the set storage, it returns false. function insert(SetStorage storage setStorage, address element) internal returns (bool) { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; uint80 metadata = setStorage.metadata; if (numElements == 0) { // gas optimization: // on the first element insertion, set the stamp to non-zero value to keep the storage slot non-zero when // the element is removed. when a new element is inserted after the removal, it should be cheaper setStorage.numElements = 1; setStorage.firstElement = element; setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; return true; } if (firstElement == element) return false; for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { if (setStorage.elements[i].value == element) return false; } if (numElements == SET_MAX_ELEMENTS) revert TooManyElements(); setStorage.elements[numElements].value = element; unchecked { setStorage.numElements = uint8(numElements + 1); } return true; } /// @notice Removes an element and returns information whether the element was removed or not. /// @dev This operation may affect the order of elements in the array of elements obtained using get() function. This /// function does not modify the metadata of the set, even if it becomes empty as a result of invoking this /// function. /// @param setStorage The set storage from which the element will be removed. /// @param element The element to be removed. /// @return A boolean value that indicates whether the element was removed or not. If the element was not in the set /// storage, it returns false. function remove(SetStorage storage setStorage, address element) internal returns (bool) { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; uint80 metadata = setStorage.metadata; if (numElements == 0) return false; uint256 searchIndex; if (firstElement != element) { for (searchIndex = EMPTY_ELEMENT_OFFSET; searchIndex < numElements; ++searchIndex) { if (setStorage.elements[searchIndex].value == element) break; } if (searchIndex == numElements) return false; } // write full slot at once to avoid SLOAD and bit masking if (numElements == 1) { setStorage.numElements = 0; setStorage.firstElement = address(0); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; return true; } uint256 lastIndex; unchecked { lastIndex = numElements - 1; } // set numElements for every execution path to avoid SSTORE and bit masking when the element removed is // firstElement ElementStorage storage lastElement = setStorage.elements[lastIndex]; if (searchIndex != lastIndex) { if (searchIndex == 0) { setStorage.firstElement = lastElement.value; setStorage.numElements = uint8(lastIndex); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; } else { setStorage.elements[searchIndex].value = lastElement.value; setStorage.firstElement = firstElement; setStorage.numElements = uint8(lastIndex); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; } } else { setStorage.firstElement = firstElement; setStorage.numElements = uint8(lastIndex); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; } lastElement.value = address(0); return true; } /// @notice Swaps the position of two elements so that they appear switched in the array of elements obtained using /// get() function. /// @dev The first index must not be greater than or equal to the second index. Indices must not be out of bounds. /// The function will revert if the indices are invalid. /// @param setStorage The set storage for which the elements will be swapped. /// @param index1 The index of the first element to be swapped. /// @param index2 The index of the second element to be swapped. function reorder(SetStorage storage setStorage, uint8 index1, uint8 index2) internal { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; if (index1 >= index2 || index2 >= numElements) { revert InvalidIndex(); } if (index1 == 0) { (setStorage.firstElement, setStorage.elements[index2].value) = (setStorage.elements[index2].value, firstElement); } else { (setStorage.elements[index1].value, setStorage.elements[index2].value) = (setStorage.elements[index2].value, setStorage.elements[index1].value); } } /// @notice Sets the metadata for the set storage. /// @param setStorage The storage structure where metadata will be set. /// @param metadata The metadata value to set. function setMetadata(SetStorage storage setStorage, uint80 metadata) internal { setStorage.metadata = metadata; } /// @notice Returns an array of elements contained in the storage. /// @dev The order of the elements in the array may be affected by performing operations on the set. /// @param setStorage The set storage to be processed. /// @return An array that contains the same elements as the set storage. function get(SetStorage storage setStorage) internal view returns (address[] memory) { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; address[] memory output = new address[](numElements); if (numElements == 0) return output; output[0] = firstElement; for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { output[i] = setStorage.elements[i].value; } return output; } /// @notice Retrieves the metadata from the set storage. /// @param setStorage The storage structure from which metadata is retrieved. /// @return The metadata value. function getMetadata(SetStorage storage setStorage) internal view returns (uint80) { return setStorage.metadata; } /// @notice Checks if the set storage contains a given element and returns a boolean value that indicates the /// result. /// @param setStorage The set storage to be searched. /// @param element The element to be searched for. /// @return A boolean value that indicates whether the set storage includes the element or not. function contains(SetStorage storage setStorage, address element) internal view returns (bool) { address firstElement = setStorage.firstElement; uint256 numElements = setStorage.numElements; if (numElements == 0) return false; if (firstElement == element) return true; for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { if (setStorage.elements[i].value == element) return true; } return false; } /// @notice Iterates over each element in the set and applies the callback function to it. /// @dev The set is cleared as a result of this call. Considering that this function does not follow the /// Checks-Effects-Interactions pattern, the function using it must prevent re-entrancy. This function does not /// modify the metadata of the set. /// @param setStorage The set storage to be processed. /// @param callback The function to be applied to each element. function forEachAndClear(SetStorage storage setStorage, function(address) callback) internal { uint256 numElements = setStorage.numElements; address firstElement = setStorage.firstElement; uint80 metadata = setStorage.metadata; if (numElements == 0) return; setStorage.numElements = 0; setStorage.firstElement = address(0); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; callback(firstElement); for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { address element = setStorage.elements[i].value; setStorage.elements[i] = ElementStorage({value: address(0), stamp: DUMMY_STAMP}); callback(element); } } /// @notice Iterates over each element in the set and applies the callback function to it, returning the array of /// callback results. /// @dev The set is cleared as a result of this call. Considering that this function does not follow the /// Checks-Effects-Interactions pattern, the function using it must prevent re-entrancy. This function does not /// modify the metadata of the set. /// @param setStorage The set storage to be processed. /// @param callback The function to be applied to each element. /// @return result An array of encoded bytes that are the addresses passed to the callback function and results of /// calling it. function forEachAndClearWithResult( SetStorage storage setStorage, function(address) returns (bool, bytes memory) callback ) internal returns (bytes[] memory) { uint256 numElements = setStorage.numElements; address firstElement = setStorage.firstElement; uint80 metadata = setStorage.metadata; bytes[] memory results = new bytes[](numElements); if (numElements == 0) return results; setStorage.numElements = 0; setStorage.firstElement = address(0); setStorage.metadata = metadata; setStorage.stamp = DUMMY_STAMP; (bool success, bytes memory result) = callback(firstElement); results[0] = abi.encode(firstElement, success, result); for (uint256 i = EMPTY_ELEMENT_OFFSET; i < numElements; ++i) { address element = setStorage.elements[i].value; setStorage.elements[i] = ElementStorage({value: address(0), stamp: DUMMY_STAMP}); (success, result) = callback(element); results[i] = abi.encode(element, success, result); } return results; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {ReentrancyGuard} from "openzeppelin-contracts/utils/ReentrancyGuard.sol"; import {SafeERC20, IERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; import {EVCUtil} from "evc/utils/EVCUtil.sol"; import {Set, SetStorage} from "evc/Set.sol"; import {IRewardStreams} from "./interfaces/IRewardStreams.sol"; /// @title BaseRewardStreams /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice Base class that allows anyone to register a reward distribution stream for a given token. abstract contract BaseRewardStreams is IRewardStreams, EVCUtil, ReentrancyGuard { using SafeERC20 for IERC20; using Set for SetStorage; /// @notice The duration of a reward epoch. /// @dev Must be between 1 and 10 weeks, inclusive. uint256 public immutable EPOCH_DURATION; /// @notice The maximum number of epochs in the future that newly registered reward streams can begin. uint256 public constant MAX_EPOCHS_AHEAD = 5; /// @notice The maximum number of epochs that a reward stream can live for. uint256 public constant MAX_DISTRIBUTION_LENGTH = 25; /// @notice The maximumum number of reward tokens enabled per account and rewarded token. uint256 public constant MAX_REWARDS_ENABLED = 5; /// @notice The minimum duration of an epoch. uint256 internal constant MIN_EPOCH_DURATION = 7 days; /// @notice The maximum duration of an epoch. uint256 internal constant MAX_EPOCH_DURATION = 10 * 7 days; /// @notice The number of epoch distribution amounts packed in a storage slot. uint256 internal constant EPOCHS_PER_SLOT = 2; /// @notice A scaling factor for precise amount calculation. /// @dev Increases precision when dividing an account's distributed amount by the total eligible amount. /// Allows a sufficiently large total registered amount per rewarded and reward token pair without overflowing. /// Value carefully selected to avoid overflow in the user claimable amount. uint256 internal constant SCALER = 2e19; /// @notice Struct to store distribution data per rewarded and reward tokens. struct DistributionStorage { /// @notice The last timestamp when the distribution was updated. uint48 lastUpdated; /// @notice The most recent accumulator value. uint160 accumulator; /// @notice Total rewarded token that are eligible for rewards. uint256 totalEligible; /// @notice Total reward token that have been transferred into this contract for rewards. uint128 totalRegistered; /// @notice Total reward token that have been transferred out from this contract for rewards. uint128 totalClaimed; /// @notice Distribution amounts per epoch. mapping(uint256 storageIndex => uint128[EPOCHS_PER_SLOT]) amounts; } /// @notice Struct to store earned data. struct EarnStorage { /// @notice Claimable amount, not total earned. uint96 claimable; /// @notice Snapshot of the accumulator at the time of the last data update. uint160 accumulator; } /// @notice Struct to store account data per account and rewarded token. struct AccountStorage { /// @notice The account's rewarded token balance. uint256 balance; /// @notice The account's set of enabled reward tokens. SetStorage enabledRewards; /// @notice The account's earnings per reward token. mapping(address reward => EarnStorage) earned; } /// @notice Stored distribution data per rewarded token and reward token. mapping(address rewarded => mapping(address reward => DistributionStorage)) internal distributions; /// @notice Stored account data per address and rewarded token. mapping(address account => mapping(address rewarded => AccountStorage)) internal accounts; /// @notice Event emitted when a reward stream is registered. event RewardRegistered( address indexed caller, address indexed rewarded, address indexed reward, uint256 startEpoch, uint128[] amounts ); /// @notice Event emitted when a user enables a reward token. event RewardEnabled(address indexed account, address indexed rewarded, address indexed reward); /// @notice Event emitted when a user disables a reward token. event RewardDisabled(address indexed account, address indexed rewarded, address indexed reward); /// @notice Event emitted when a reward token is claimed. event RewardClaimed(address indexed account, address indexed rewarded, address indexed reward, uint256 amount); /// @notice Event emitted when the balance of the rewarded token for the account is updated. event BalanceUpdated(address indexed account, address indexed rewarded, uint256 oldBalance, uint256 newBalance); /// @notice Epoch-related error. Thrown when epoch duration or start epoch is invalid. error InvalidEpoch(); /// @notice Amount-related error. Thrown when the reward amount is invalid or invalid amount of tokens was /// transferred. error InvalidAmount(); /// @notice Distribution-related error. Thrown when the reward distribution length is invalid. error InvalidDistribution(); /// @notice Accumulator-related error. Thrown when the registered reward amount may cause an accumulator overflow. error AccumulatorOverflow(); /// @notice Rewards-related error. Thrown when user tries to enable too many rewards. error TooManyRewardsEnabled(); /// @notice Recipient-related error. Thrown when the recipient is invalid. error InvalidRecipient(); /// @notice Constructor for the BaseRewardStreams contract. /// @param _evc The Ethereum Vault Connector contract. /// @param _epochDuration The duration of an epoch. constructor(address _evc, uint48 _epochDuration) EVCUtil(_evc) { if (_epochDuration < MIN_EPOCH_DURATION || _epochDuration > MAX_EPOCH_DURATION) { revert InvalidEpoch(); } EPOCH_DURATION = _epochDuration; } /// @notice Registers a new reward stream. /// @param rewarded The rewarded token. /// @param reward The reward token. /// @param startEpoch The epoch to start the reward stream from. /// @param rewardAmounts The reward token amounts for each epoch of the reward stream. function registerReward( address rewarded, address reward, uint48 startEpoch, uint128[] calldata rewardAmounts ) external virtual override nonReentrant { uint48 epoch = currentEpoch(); // If start epoch is 0, set it to the next epoch. if (startEpoch == 0) { startEpoch = epoch + 1; } // Start should be at most MAX_EPOCHS_AHEAD epochs in the future. if (!(startEpoch > epoch && startEpoch <= epoch + MAX_EPOCHS_AHEAD)) { revert InvalidEpoch(); } // Distribution stream should be at most MAX_DISTRIBUTION_LENGTH epochs long. if (rewardAmounts.length > MAX_DISTRIBUTION_LENGTH) { revert InvalidDistribution(); } // Calculate the total amount to be distributed in this distribution stream. uint256 totalAmount; for (uint256 i = 0; i < rewardAmounts.length; ++i) { totalAmount += rewardAmounts[i]; } if (totalAmount == 0) { revert InvalidAmount(); } // Initialize or update the distribution and reward data. DistributionStorage storage distributionStorage = distributions[rewarded][reward]; if (distributionStorage.lastUpdated == 0) { distributionStorage.lastUpdated = uint48(block.timestamp); } else { updateReward(rewarded, reward, address(0)); } // Sanity check for overflow (assumes total eligible supply of 1 which is the worst case scenario). uint256 totalRegistered = uint256(distributionStorage.totalRegistered) + totalAmount; if (SCALER * totalRegistered > type(uint160).max) { revert AccumulatorOverflow(); } // Update the total registered amount. // Downcasting is safe because the `type(uint160).max / SCALER < type(uint128).max`. distributionStorage.totalRegistered = uint128(totalRegistered); // Store the amounts to be distributed. increaseRewardAmounts(rewarded, reward, startEpoch, rewardAmounts); // Transfer the total amount to be distributed to the contract. address msgSender = _msgSender(); pullToken(IERC20(reward), msgSender, totalAmount); emit RewardRegistered(msgSender, rewarded, reward, startEpoch, rewardAmounts); } /// @notice Updates the reward token data. In reward for updating the reward token data, the function allows to /// claim splillover rewards virtually accrued to address(0). /// @dev The spillover rewards are only claimed if the receiver address provided is non-zero. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @param recipient The address to receive the spillover reward tokens. /// @return The amount of the spillover reward tokens claimed. function updateReward( address rewarded, address reward, address recipient ) public virtual override returns (uint256) { address msgSender = _msgSender(); // If the account disables the rewards we pass an account balance of zero to not accrue any. AccountStorage storage accountStorage = accounts[msgSender][rewarded]; uint256 currentAccountBalance = accountStorage.enabledRewards.contains(reward) ? accountStorage.balance : 0; updateRewardInternal( distributions[rewarded][reward], accountStorage.earned[reward], rewarded, reward, currentAccountBalance, false ); if (recipient != address(0)) { return claim(address(0), rewarded, reward, recipient); } return 0; } /// @notice Claims earned reward. /// @dev Rewards are only transferred to the recipient if the recipient is non-zero. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @param recipient The address to receive the claimed reward tokens. /// @param ignoreRecentReward Whether to ignore the most recent reward and not update the accumulator. /// @return The amount of the claimed reward tokens. function claimReward( address rewarded, address reward, address recipient, bool ignoreRecentReward ) external virtual override nonReentrant returns (uint256) { address msgSender = _msgSender(); // If the account disables the rewards we pass an account balance of zero to not accrue any. AccountStorage storage accountStorage = accounts[msgSender][rewarded]; uint256 currentAccountBalance = accountStorage.enabledRewards.contains(reward) ? accountStorage.balance : 0; updateRewardInternal( distributions[rewarded][reward], accountStorage.earned[reward], rewarded, reward, currentAccountBalance, ignoreRecentReward ); return claim(msgSender, rewarded, reward, recipient); } /// @notice Enable reward token. /// @dev There can be at most MAX_REWARDS_ENABLED rewards enabled for the rewarded token and the account. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @return Whether the reward token was enabled. function enableReward(address rewarded, address reward) external virtual override returns (bool) { address msgSender = _msgSender(); AccountStorage storage accountStorage = accounts[msgSender][rewarded]; SetStorage storage accountEnabledRewards = accountStorage.enabledRewards; bool wasEnabled = accountEnabledRewards.insert(reward); if (wasEnabled) { if (accountEnabledRewards.numElements > MAX_REWARDS_ENABLED) { revert TooManyRewardsEnabled(); } DistributionStorage storage distributionStorage = distributions[rewarded][reward]; uint256 currentAccountBalance = accountStorage.balance; // We pass zero as `currentAccountBalance` to not distribute rewards for the period before the account // enabled them. updateRewardInternal(distributionStorage, accountStorage.earned[reward], rewarded, reward, 0, false); distributionStorage.totalEligible += currentAccountBalance; emit RewardEnabled(msgSender, rewarded, reward); } return wasEnabled; } /// @notice Disable reward token. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @param forfeitRecentReward Whether to forfeit the recent rewards and not update the accumulator. /// @return Whether the reward token was disabled. function disableReward( address rewarded, address reward, bool forfeitRecentReward ) external virtual override returns (bool) { address msgSender = _msgSender(); AccountStorage storage accountStorage = accounts[msgSender][rewarded]; bool wasDisabled = accountStorage.enabledRewards.remove(reward); if (wasDisabled) { DistributionStorage storage distributionStorage = distributions[rewarded][reward]; uint256 currentAccountBalance = accountStorage.balance; updateRewardInternal( distributionStorage, accountStorage.earned[reward], rewarded, reward, currentAccountBalance, forfeitRecentReward ); distributionStorage.totalEligible -= currentAccountBalance; emit RewardDisabled(msgSender, rewarded, reward); } return wasDisabled; } /// @notice Returns the earned reward token amount for a specific account and rewarded token. /// @param account The address of the account. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @param ignoreRecentReward Whether to ignore the most recent reward and not update the accumulator. /// @return The earned reward token amount for the account and rewarded token. function earnedReward( address account, address rewarded, address reward, bool ignoreRecentReward ) external view virtual override returns (uint256) { // If the account disables the rewards we pass an account balance of zero to not accrue any. AccountStorage storage accountStorage = accounts[account][rewarded]; uint256 currentAccountBalance = accountStorage.enabledRewards.contains(reward) ? accountStorage.balance : 0; (,, uint96 claimable, uint96 deltaAccountZero) = calculateRewards( distributions[rewarded][reward], accountStorage.earned[reward], currentAccountBalance, ignoreRecentReward ); // If we have spillover rewards, we add them to `address(0)`. if (account == address(0) && deltaAccountZero != 0) { return claimable + deltaAccountZero; } return claimable; } /// @notice Returns enabled reward tokens for a specific account. /// @param account The address of the account. /// @param rewarded The address of the rewarded token. /// @return An array of addresses representing the enabled reward tokens. function enabledRewards( address account, address rewarded ) external view virtual override returns (address[] memory) { return accounts[account][rewarded].enabledRewards.get(); } /// @notice Checks if a specific reward token is enabled for an account and rewarded token. /// @param account The address of the account. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token to check if enabled. /// @return Whether the reward token is enabled for the account and rewarded token. function isRewardEnabled( address account, address rewarded, address reward ) external view virtual override returns (bool) { return accounts[account][rewarded].enabledRewards.contains(reward); } /// @notice Returns the rewarded token balance of a specific account. /// @param account The address of the account. /// @param rewarded The address of the rewarded token. /// @return The rewarded token balance of the account. function balanceOf(address account, address rewarded) external view virtual override returns (uint256) { return accounts[account][rewarded].balance; } /// @notice Returns the reward token amount for a specific rewarded token and current epoch. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @return The reward token amount for the rewarded token and current epoch. function rewardAmount(address rewarded, address reward) external view virtual override returns (uint256) { return rewardAmount(rewarded, reward, currentEpoch()); } /// @notice Returns the total supply of the rewarded token enabled and eligible to receive the reward token. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @return The total supply of the rewarded token enabled and eligible to receive the reward token. function totalRewardedEligible(address rewarded, address reward) external view virtual override returns (uint256) { return distributions[rewarded][reward].totalEligible; } /// @notice Returns the total reward token amount registered to be distributed for a specific rewarded token. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @return The total reward token amount distributed for the rewarded token. function totalRewardRegistered(address rewarded, address reward) external view returns (uint256) { return distributions[rewarded][reward].totalRegistered; } /// @notice Returns the total reward token amount claimed for a specific rewarded token. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @return The total reward token amount claimed for the rewarded token. function totalRewardClaimed(address rewarded, address reward) external view returns (uint256) { return distributions[rewarded][reward].totalClaimed; } /// @notice Returns the reward token amount for a specific rewarded token and epoch. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @param epoch The epoch to get the reward token amount for. /// @return The reward token amount for the rewarded token and epoch. function rewardAmount( address rewarded, address reward, uint48 epoch ) public view virtual override returns (uint256) { return rewardAmount(distributions[rewarded][reward], epoch); } /// @notice Returns the current epoch based on the block timestamp. /// @return The current epoch. function currentEpoch() public view override returns (uint48) { return getEpoch(uint48(block.timestamp)); } /// @notice Returns the epoch for a given timestamp. /// @param timestamp The timestamp to get the epoch for. /// @return The epoch for the given timestamp. function getEpoch(uint48 timestamp) public view override returns (uint48) { return uint48(timestamp / EPOCH_DURATION); } /// @notice Returns the start timestamp for a given epoch. /// @param epoch The epoch to get the start timestamp for. /// @return The start timestamp for the given epoch. function getEpochStartTimestamp(uint48 epoch) public view override returns (uint48) { return uint48(epoch * EPOCH_DURATION); } /// @notice Returns the end timestamp for a given epoch. /// @dev The end timestamp is just after its epoch, and is the same as the start timestamp from the next epoch. /// @param epoch The epoch to get the end timestamp for. /// @return The end timestamp for the given epoch. function getEpochEndTimestamp(uint48 epoch) public view override returns (uint48) { return uint48(getEpochStartTimestamp(epoch) + EPOCH_DURATION); } /// @notice Transfers a specified amount of a token from a given address to this contract. /// @dev This function uses `SafeERC20.safeTransferFrom` function to move tokens. /// It checks the balance before and after the transfer to ensure the correct amount has been transferred. /// If the transferred amount does not match the expected amount, it reverts. /// @param token The ERC20 token to transfer. /// @param from The address to transfer the tokens from. /// @param amount The amount of tokens to transfer. function pullToken(IERC20 token, address from, uint256 amount) internal { uint256 preBalance = token.balanceOf(address(this)); token.safeTransferFrom(from, address(this), amount); if (token.balanceOf(address(this)) - preBalance != amount) { revert InvalidAmount(); } } /// @notice Transfers a specified amount of a token to a given address. /// @dev This function uses `IERC20.safeTransfer` to move tokens. /// @dev This function reverts if the recipient is zero address OR is a known non-owner EVC account and the token is /// not EVC compatible /// @param token The ERC20 token to transfer. /// @param to The address to transfer the tokens to. /// @param amount The amount of tokens to transfer. function pushToken(IERC20 token, address to, uint256 amount) internal { address owner = evc.getAccountOwner(to); if (to == address(0) || (owner != address(0) && owner != to && !isEVCCompatibleAsset(token))) { revert InvalidRecipient(); } token.safeTransfer(to, amount); } /// @notice Returns the reward token amount for an epoch, given a pre-computed distribution storage pointer. /// @param distributionStorage Pre-computed distribution storage pointer. /// @param epoch The epoch to get the reward token amount for. /// @return The reward token amount. function rewardAmount( DistributionStorage storage distributionStorage, uint48 epoch ) internal view returns (uint256) { return distributionStorage.amounts[epoch / EPOCHS_PER_SLOT][epoch % EPOCHS_PER_SLOT]; } /// @notice Increases the reward token amounts for a specific rewarded token. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @param startEpoch The starting epoch to increase the reward token amount for. /// @param amounts The token amounts to increase by. function increaseRewardAmounts( address rewarded, address reward, uint48 startEpoch, uint128[] memory amounts ) internal virtual { mapping(uint256 => uint128[EPOCHS_PER_SLOT]) storage storageAmounts = distributions[rewarded][reward].amounts; for (uint48 i = 0; i < amounts.length; ++i) { // Overflow safe because `totalRegistered <= type(uint160).max / SCALER < type(uint128).max`. unchecked { uint48 epoch = startEpoch + i; storageAmounts[epoch / EPOCHS_PER_SLOT][epoch % EPOCHS_PER_SLOT] += amounts[i]; } } } /// @notice Claims the earned reward for a specific account, rewarded token, and reward token, and transfers it to /// the recipient. /// @dev This function reverts if the recipient is zero address or is a known non-owner EVC account. /// @dev If there is no reward to claim, this function does nothing. /// @param account The address of the account claiming the reward. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @param recipient The address to which the claimed reward will be transferred. /// @return The amount of the claimed reward tokens. function claim( address account, address rewarded, address reward, address recipient ) internal virtual returns (uint256) { EarnStorage storage accountEarned = accounts[account][rewarded].earned[reward]; uint128 amount = accountEarned.claimable; // If there is a reward token to claim, transfer it to the recipient and emit an event. if (amount != 0) { DistributionStorage storage distributionStorage = distributions[rewarded][reward]; uint128 totalRegistered = distributionStorage.totalRegistered; uint128 totalClaimed = distributionStorage.totalClaimed; uint128 newTotalClaimed = totalClaimed + amount; assert(totalRegistered >= newTotalClaimed); distributionStorage.totalClaimed = newTotalClaimed; accountEarned.claimable = 0; pushToken(IERC20(reward), recipient, amount); emit RewardClaimed(account, rewarded, reward, amount); } return amount; } /// @notice Updates the data for a specific account, rewarded token and reward token. /// @dev If required, this function artificially accumulates rewards for the address(0) to avoid loss of rewards /// that wouldn't be claimable by anyone else. /// @dev This function does not update total eligible amount or account balances. /// @param distributionStorage Pointer to the storage of the distribution. /// @param accountEarnStorage Pointer to the storage of the account's earned amount and accumulator. /// @param rewarded The address of the rewarded token. /// @param reward The address of the reward token. /// @param currentAccountBalance The current rewarded token balance of the account. /// @param ignoreRecentReward Whether to ignore the most recent reward and not update the accumulator. function updateRewardInternal( DistributionStorage storage distributionStorage, EarnStorage storage accountEarnStorage, address rewarded, address reward, uint256 currentAccountBalance, bool ignoreRecentReward ) internal virtual { (uint48 lastUpdated, uint160 accumulator, uint96 claimable, uint96 deltaAccountZero) = calculateRewards(distributionStorage, accountEarnStorage, currentAccountBalance, ignoreRecentReward); // Update the distribution data. distributionStorage.lastUpdated = lastUpdated; distributionStorage.accumulator = accumulator; // Update the account's earned amount. Snapshot new accumulator value for the account. // Downcasting is safe because the `totalRegistered <= type(uint160).max / SCALER`. accountEarnStorage.claimable = claimable; accountEarnStorage.accumulator = accumulator; // If there were excess rewards, allocate them to address(0). // Overflow safe because `totalRegistered <= type(uint160).max / SCALER < type(uint96).max`. if (deltaAccountZero != 0) { unchecked { accounts[address(0)][rewarded].earned[reward].claimable += deltaAccountZero; } } } /// @notice Computes updated data for a specific account, rewarded token, and reward token. /// @param distributionStorage Pointer to the storage of the distribution. /// @param accountEarnStorage Pointer to the storage of the account's earned amount and accumulator. /// @param currentAccountBalance The current rewarded token balance of the account. /// @param ignoreRecentReward Whether to ignore the most recent reward and not update the accumulator. /// @return lastUpdated The next value for the last update timestamp. /// @return accumulator The next value for the distribution accumulator. /// @return claimable The next value for the account's claimable amount. /// @return deltaAccountZero Amount to be credited to `address(0)` in case rewards were to be lost. function calculateRewards( DistributionStorage storage distributionStorage, EarnStorage storage accountEarnStorage, uint256 currentAccountBalance, bool ignoreRecentReward ) internal view virtual returns (uint48 lastUpdated, uint160 accumulator, uint96 claimable, uint96 deltaAccountZero) { // If the distribution is not initialized, return. lastUpdated = distributionStorage.lastUpdated; accumulator = distributionStorage.accumulator; claimable = accountEarnStorage.claimable; if (lastUpdated == 0) { return (lastUpdated, accumulator, claimable, 0); } if (!ignoreRecentReward) { // Get the start and end epochs based on the last updated timestamp of the distribution. uint48 epochStart = getEpoch(lastUpdated); uint48 epochEnd = currentEpoch() + 1; uint256 delta; // Calculate the amount of tokens since the last update that should be distributed. for (uint48 epoch = epochStart; epoch < epochEnd; ++epoch) { // Overflow safe because: // `totalRegistered * MAX_EPOCH_DURATION <= type(uint160).max * MAX_EPOCH_DURATION / SCALER < // type(uint256).max`. unchecked { delta += rewardAmount(distributionStorage, epoch) * timeElapsedInEpoch(epoch, lastUpdated); } } // Increase the accumulator scaled by the total eligible amount earning reward. In case nobody earns // rewards, allocate them to address(0). Otherwise, some portion of the rewards might get lost. uint256 currentTotalEligible = distributionStorage.totalEligible; if (currentTotalEligible == 0) { // Downcasting is safe because the `totalRegistered <= type(uint160).max / SCALER < type(uint96).max`. deltaAccountZero = uint96(delta / EPOCH_DURATION); } else { // Overflow safe because `totalRegistered * SCALER <= type(uint160).max`. unchecked { accumulator += uint160(delta * SCALER / EPOCH_DURATION / currentTotalEligible); } } // Snapshot the timestamp. lastUpdated = uint48(block.timestamp); } // Update the account's earned amount. // Downcasting is safe because the `totalRegistered <= type(uint160).max / SCALER < type(uint96).max`. claimable += uint96(uint256(accumulator - accountEarnStorage.accumulator) * currentAccountBalance / SCALER); } /// @notice Calculates the time elapsed within a given epoch. /// @dev This function compares the current block timestamp with the start and end timestamps of the epoch. /// @dev If the epoch is ongoing, it calculates the time elapsed since the last update or the start of the epoch, /// whichever is smaller. /// @dev If the epoch has ended and there was an update since its start, it calculates the time elapsed since the /// last update to the end of the epoch. /// @dev If the epoch has ended and there wasn't an update since its start, it returns the epoch duration. /// @dev If the epoch hasn't started, then there can't be a later update yet, and we return zero. /// @param epoch The epoch for which to calculate the time elapsed. /// @param lastUpdated The timestamp of the last update. /// @return The time elapsed in the given epoch. function timeElapsedInEpoch(uint48 epoch, uint48 lastUpdated) internal view returns (uint256) { // Get the start and end timestamps for the given epoch. uint256 startTimestamp = getEpochStartTimestamp(epoch); uint256 endTimestamp = getEpochEndTimestamp(epoch); // Calculate the time elapsed in the given epoch. if (block.timestamp >= endTimestamp) { // The epoch has ended. // If the last update was in or after the given epoch, return the time elapsed between the last update to // the end of the given epoch. If the last update was before the start of the given epoch, return the epoch // duration. return lastUpdated > startTimestamp ? endTimestamp - lastUpdated : EPOCH_DURATION; } else if (block.timestamp >= startTimestamp && block.timestamp < endTimestamp) { // The epoch is ongoing. // If the last update was in or after the given epoch, return the time elapsed since the last update. // Otherwise return the time elapsed from the start of the given epoch. return lastUpdated > startTimestamp ? block.timestamp - lastUpdated : block.timestamp - startTimestamp; } else { // The epoch hasn't started yet. return 0; } } /// @notice Checks if a given ERC20 token is EVC compatible. /// @dev This function performs a static call to the token contract to check for the presence of the EVC function. /// @param token The ERC20 token to check for EVC compatibility. /// @return bool Returns true if the token is EVC compatible, false otherwise. function isEVCCompatibleAsset(IERC20 token) internal view returns (bool) { (bool success, bytes memory result) = address(token).staticcall(abi.encodeCall(EVCUtil.EVC, ())); return success && result.length == 32 && address(evc) == abi.decode(result, (address)); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.8.0; import "./IBalanceTracker.sol"; /// @title IRewardStreams /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice Interface for Reward Streams distributor contract. interface IRewardStreams { function EPOCH_DURATION() external view returns (uint256); function MAX_EPOCHS_AHEAD() external view returns (uint256); function MAX_DISTRIBUTION_LENGTH() external view returns (uint256); function MAX_REWARDS_ENABLED() external view returns (uint256); function registerReward(address rewarded, address reward, uint48 startEpoch, uint128[] calldata rewardAmounts) external; function updateReward(address rewarded, address reward, address recipient) external returns (uint256); function claimReward(address rewarded, address reward, address recipient, bool ignoreRecentReward) external returns (uint256); function enableReward(address rewarded, address reward) external returns (bool); function disableReward(address rewarded, address reward, bool forfeitRecentReward) external returns (bool); function earnedReward(address account, address rewarded, address reward, bool ignoreRecentReward) external view returns (uint256); function enabledRewards(address account, address rewarded) external view returns (address[] memory); function isRewardEnabled(address account, address rewarded, address reward) external view returns (bool); function balanceOf(address account, address rewarded) external view returns (uint256); function rewardAmount(address rewarded, address reward) external view returns (uint256); function totalRewardedEligible(address rewarded, address reward) external view returns (uint256); function totalRewardRegistered(address rewarded, address reward) external view returns (uint256); function totalRewardClaimed(address rewarded, address reward) external view returns (uint256); function rewardAmount(address rewarded, address reward, uint48 epoch) external view returns (uint256); function currentEpoch() external view returns (uint48); function getEpoch(uint48 timestamp) external view returns (uint48); function getEpochStartTimestamp(uint48 epoch) external view returns (uint48); function getEpochEndTimestamp(uint48 epoch) external view returns (uint48); } /// @title ITrackingRewardStreams /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice Interface for Tracking Reward Streams. Extends `IRewardStreams` and `IBalanceTracker`. interface ITrackingRewardStreams is IRewardStreams, IBalanceTracker {} /// @title IStakingRewardStreams /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice Interface for Staking Reward Streams. Extends `IRewardStreams` with staking functionality. interface IStakingRewardStreams is IRewardStreams { function stake(address rewarded, uint256 amount) external; function unstake(address rewarded, uint256 amount, address recipient, bool forfeitRecentReward) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at, * consider using {ReentrancyGuardTransient} instead. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; uint256 private _status; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC1363} from "../../../interfaces/IERC1363.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC-20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC-20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { safeTransfer(token, to, value); } else if (!token.transferAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferFromAndCallRelaxed( IERC1363 token, address from, address to, uint256 value, bytes memory data ) internal { if (to.code.length == 0) { safeTransferFrom(token, from, to, value); } else if (!token.transferFromAndCall(from, to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}. * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall} * once without retrying, and relies on the returned value to be true. * * Reverts if the returned value is other than `true`. */ function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { forceApprove(token, to, value); } else if (!token.approveAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IEVC} from "../interfaces/IEthereumVaultConnector.sol"; import {ExecutionContext, EC} from "../ExecutionContext.sol"; /// @title EVCUtil /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This contract is an abstract base contract for interacting with the Ethereum Vault Connector (EVC). /// It provides utility functions for authenticating the callers in the context of the EVC, a pattern for enforcing the /// contracts to be called through the EVC. abstract contract EVCUtil { using ExecutionContext for EC; uint160 internal constant ACCOUNT_ID_OFFSET = 8; IEVC internal immutable evc; error EVC_InvalidAddress(); error NotAuthorized(); error ControllerDisabled(); constructor(address _evc) { if (_evc == address(0)) revert EVC_InvalidAddress(); evc = IEVC(_evc); } /// @notice Returns the address of the Ethereum Vault Connector (EVC) used by this contract. /// @return The address of the EVC contract. function EVC() external view returns (address) { return address(evc); } /// @notice Ensures that the msg.sender is the EVC by using the EVC callback functionality if necessary. /// @dev Optional to use for functions requiring account and vault status checks to enforce predictable behavior. /// @dev If this modifier used in conjuction with any other modifier, it must appear as the first (outermost) /// modifier of the function. modifier callThroughEVC() virtual { _callThroughEVC(); _; } /// @notice Ensures that the caller is the EVC in the appropriate context. /// @dev Should be used for checkAccountStatus and checkVaultStatus functions. modifier onlyEVCWithChecksInProgress() virtual { _onlyEVCWithChecksInProgress(); _; } /// @notice Ensures a standard authentication path on the EVC. /// @dev This modifier checks if the caller is the EVC and if so, verifies the execution context. /// It reverts if the operator is authenticated, control collateral is in progress, or checks are in progress. /// It reverts if the authenticated account owner is known and it is not the account owner. /// @dev It assumes that if the caller is not the EVC, the caller is the account owner. /// @dev This modifier must not be used on functions utilized by liquidation flows, i.e. transfer or withdraw. /// @dev This modifier must not be used on checkAccountStatus and checkVaultStatus functions. /// @dev This modifier can be used on access controlled functions to prevent non-standard authentication paths on /// the EVC. modifier onlyEVCAccountOwner() virtual { _onlyEVCAccountOwner(); _; } /// @notice Checks whether the specified account and the other account have the same owner. /// @dev The function is used to check whether one account is authorized to perform operations on behalf of the /// other. Accounts are considered to have a common owner if they share the first 19 bytes of their address. /// @param account The address of the account that is being checked. /// @param otherAccount The address of the other account that is being checked. /// @return A boolean flag that indicates whether the accounts have the same owner. function _haveCommonOwner(address account, address otherAccount) internal pure returns (bool) { bool result; assembly { result := lt(xor(account, otherAccount), 0x100) } return result; } /// @notice Returns the address prefix of the specified account. /// @dev The address prefix is the first 19 bytes of the account address. /// @param account The address of the account whose address prefix is being retrieved. /// @return A bytes19 value that represents the address prefix of the account. function _getAddressPrefix(address account) internal pure returns (bytes19) { return bytes19(uint152(uint160(account) >> ACCOUNT_ID_OFFSET)); } /// @notice Retrieves the message sender in the context of the EVC. /// @dev This function returns the account on behalf of which the current operation is being performed, which is /// either msg.sender or the account authenticated by the EVC. /// @return The address of the message sender. function _msgSender() internal view virtual returns (address) { address sender = msg.sender; if (sender == address(evc)) { (sender,) = evc.getCurrentOnBehalfOfAccount(address(0)); } return sender; } /// @notice Retrieves the message sender in the context of the EVC for a borrow operation. /// @dev This function returns the account on behalf of which the current operation is being performed, which is /// either msg.sender or the account authenticated by the EVC. This function reverts if this contract is not enabled /// as a controller for the account on behalf of which the operation is being executed. /// @return The address of the message sender. function _msgSenderForBorrow() internal view virtual returns (address) { address sender = msg.sender; bool controllerEnabled; if (sender == address(evc)) { (sender, controllerEnabled) = evc.getCurrentOnBehalfOfAccount(address(this)); } else { controllerEnabled = evc.isControllerEnabled(sender, address(this)); } if (!controllerEnabled) { revert ControllerDisabled(); } return sender; } /// @notice Calls the current external function through the EVC. /// @dev This function is used to route the current call through the EVC if it's not already coming from the EVC. It /// makes the EVC set the execution context and call back this contract with unchanged calldata. msg.sender is used /// as the onBehalfOfAccount. /// @dev This function shall only be used by the callThroughEVC modifier. function _callThroughEVC() internal { address _evc = address(evc); if (msg.sender == _evc) return; assembly { mstore(0, 0x1f8b521500000000000000000000000000000000000000000000000000000000) // EVC.call selector mstore(4, address()) // EVC.call 1st argument - address(this) mstore(36, caller()) // EVC.call 2nd argument - msg.sender mstore(68, callvalue()) // EVC.call 3rd argument - msg.value mstore(100, 128) // EVC.call 4th argument - msg.data, offset to the start of encoding - 128 bytes mstore(132, calldatasize()) // msg.data length calldatacopy(164, 0, calldatasize()) // original calldata // abi encoded bytes array should be zero padded so its length is a multiple of 32 // store zero word after msg.data bytes and round up calldatasize to nearest multiple of 32 mstore(add(164, calldatasize()), 0) let result := call(gas(), _evc, callvalue(), 0, add(164, and(add(calldatasize(), 31), not(31))), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(64, sub(returndatasize(), 64)) } // strip bytes encoding from call return } } /// @notice Ensures that the function is called only by the EVC during the checks phase /// @dev Reverts if the caller is not the EVC or if checks are not in progress. function _onlyEVCWithChecksInProgress() internal view { if (msg.sender != address(evc) || !evc.areChecksInProgress()) { revert NotAuthorized(); } } /// @notice Ensures that the function is called only by the EVC account owner /// @dev This function checks if the caller is the EVC and if so, verifies that the execution context is not in a /// special state (operator authenticated, collateral control in progress, or checks in progress). If the owner was /// already registered on the EVC, it verifies that the onBehalfOfAccount is the owner. /// @dev Reverts if the caller is not the EVC or if the execution context is in a special state. function _onlyEVCAccountOwner() internal view { if (msg.sender == address(evc)) { EC ec = EC.wrap(evc.getRawExecutionContext()); if (ec.isOperatorAuthenticated() || ec.isControlCollateralInProgress() || ec.areChecksInProgress()) { revert NotAuthorized(); } address onBehalfOfAccount = ec.getOnBehalfOfAccount(); address owner = evc.getAccountOwner(onBehalfOfAccount); if (owner != address(0) && owner != onBehalfOfAccount) { revert NotAuthorized(); } } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.8.0; /// @title IBalanceTracker /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice Provides an interface for tracking the balance of accounts. interface IBalanceTracker { /// @notice Executes the balance tracking hook for an account. /// @dev This function must be called with the current balance of the account when enabling the balance forwarding /// for it. This function must be called with 0 balance of the account when disabling the balance forwarding for it. /// This function allows to be called on zero balance transfers, when the newAccountBalance is the same as the /// previous one. To prevent DOS attacks, forfeitRecentReward should be used appropriately. /// @param account The account address to execute the hook for. /// @param newAccountBalance The new balance of the account. /// @param forfeitRecentReward Whether to forfeit the most recent reward and not update the accumulator. function balanceTrackerHook(address account, uint256 newAccountBalance, bool forfeitRecentReward) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1363.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC165} from "./IERC165.sol"; /** * @title IERC1363 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. * * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction. */ interface IERC1363 is IERC20, IERC165 { /* * Note: the ERC-165 identifier for this interface is 0xb0202a11. * 0xb0202a11 === * bytes4(keccak256('transferAndCall(address,uint256)')) ^ * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ * bytes4(keccak256('approveAndCall(address,uint256)')) ^ * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) */ /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @param data Additional data with no specified format, sent in call to `spender`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert Errors.FailedCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {Errors.FailedCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case * of an unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {Errors.FailedCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert Errors.FailedCall(); } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.8.0; /// @title IEVC /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This interface defines the methods for the Ethereum Vault Connector. interface IEVC { /// @notice A struct representing a batch item. /// @dev Each batch item represents a single operation to be performed within a checks deferred context. struct BatchItem { /// @notice The target contract to be called. address targetContract; /// @notice The account on behalf of which the operation is to be performed. msg.sender must be authorized to /// act on behalf of this account. Must be address(0) if the target contract is the EVC itself. address onBehalfOfAccount; /// @notice The amount of value to be forwarded with the call. If the value is type(uint256).max, the whole /// balance of the EVC contract will be forwarded. Must be 0 if the target contract is the EVC itself. uint256 value; /// @notice The encoded data which is called on the target contract. bytes data; } /// @notice A struct representing the result of a batch item operation. /// @dev Used only for simulation purposes. struct BatchItemResult { /// @notice A boolean indicating whether the operation was successful. bool success; /// @notice The result of the operation. bytes result; } /// @notice A struct representing the result of the account or vault status check. /// @dev Used only for simulation purposes. struct StatusCheckResult { /// @notice The address of the account or vault for which the check was performed. address checkedAddress; /// @notice A boolean indicating whether the status of the account or vault is valid. bool isValid; /// @notice The result of the check. bytes result; } /// @notice Returns current raw execution context. /// @dev When checks in progress, on behalf of account is always address(0). /// @return context Current raw execution context. function getRawExecutionContext() external view returns (uint256 context); /// @notice Returns an account on behalf of which the operation is being executed at the moment and whether the /// controllerToCheck is an enabled controller for that account. /// @dev This function should only be used by external smart contracts if msg.sender is the EVC. Otherwise, the /// account address returned must not be trusted. /// @dev When checks in progress, on behalf of account is always address(0). When address is zero, the function /// reverts to protect the consumer from ever relying on the on behalf of account address which is in its default /// state. /// @param controllerToCheck The address of the controller for which it is checked whether it is an enabled /// controller for the account on behalf of which the operation is being executed at the moment. /// @return onBehalfOfAccount An account that has been authenticated and on behalf of which the operation is being /// executed at the moment. /// @return controllerEnabled A boolean value that indicates whether controllerToCheck is an enabled controller for /// the account on behalf of which the operation is being executed at the moment. Always false if controllerToCheck /// is address(0). function getCurrentOnBehalfOfAccount(address controllerToCheck) external view returns (address onBehalfOfAccount, bool controllerEnabled); /// @notice Checks if checks are deferred. /// @return A boolean indicating whether checks are deferred. function areChecksDeferred() external view returns (bool); /// @notice Checks if checks are in progress. /// @return A boolean indicating whether checks are in progress. function areChecksInProgress() external view returns (bool); /// @notice Checks if control collateral is in progress. /// @return A boolean indicating whether control collateral is in progress. function isControlCollateralInProgress() external view returns (bool); /// @notice Checks if an operator is authenticated. /// @return A boolean indicating whether an operator is authenticated. function isOperatorAuthenticated() external view returns (bool); /// @notice Checks if a simulation is in progress. /// @return A boolean indicating whether a simulation is in progress. function isSimulationInProgress() external view returns (bool); /// @notice Checks whether the specified account and the other account have the same owner. /// @dev The function is used to check whether one account is authorized to perform operations on behalf of the /// other. Accounts are considered to have a common owner if they share the first 19 bytes of their address. /// @param account The address of the account that is being checked. /// @param otherAccount The address of the other account that is being checked. /// @return A boolean flag that indicates whether the accounts have the same owner. function haveCommonOwner(address account, address otherAccount) external pure returns (bool); /// @notice Returns the address prefix of the specified account. /// @dev The address prefix is the first 19 bytes of the account address. /// @param account The address of the account whose address prefix is being retrieved. /// @return A bytes19 value that represents the address prefix of the account. function getAddressPrefix(address account) external pure returns (bytes19); /// @notice Returns the owner for the specified account. /// @dev The function returns address(0) if the owner is not registered. Registration of the owner happens on the /// initial /// interaction with the EVC that requires authentication of an owner. /// @param account The address of the account whose owner is being retrieved. /// @return owner The address of the account owner. An account owner is an EOA/smart contract which address matches /// the first 19 bytes of the account address. function getAccountOwner(address account) external view returns (address); /// @notice Checks if lockdown mode is enabled for a given address prefix. /// @param addressPrefix The address prefix to check for lockdown mode status. /// @return A boolean indicating whether lockdown mode is enabled. function isLockdownMode(bytes19 addressPrefix) external view returns (bool); /// @notice Checks if permit functionality is disabled for a given address prefix. /// @param addressPrefix The address prefix to check for permit functionality status. /// @return A boolean indicating whether permit functionality is disabled. function isPermitDisabledMode(bytes19 addressPrefix) external view returns (bool); /// @notice Returns the current nonce for a given address prefix and nonce namespace. /// @dev Each nonce namespace provides 256 bit nonce that has to be used sequentially. There's no requirement to use /// all the nonces for a given nonce namespace before moving to the next one which allows to use permit messages in /// a non-sequential manner. /// @param addressPrefix The address prefix for which the nonce is being retrieved. /// @param nonceNamespace The nonce namespace for which the nonce is being retrieved. /// @return nonce The current nonce for the given address prefix and nonce namespace. function getNonce(bytes19 addressPrefix, uint256 nonceNamespace) external view returns (uint256 nonce); /// @notice Returns the bit field for a given address prefix and operator. /// @dev The bit field is used to store information about authorized operators for a given address prefix. Each bit /// in the bit field corresponds to one account belonging to the same owner. If the bit is set, the operator is /// authorized for the account. /// @param addressPrefix The address prefix for which the bit field is being retrieved. /// @param operator The address of the operator for which the bit field is being retrieved. /// @return operatorBitField The bit field for the given address prefix and operator. The bit field defines which /// accounts the operator is authorized for. It is a 256-position binary array like 0...010...0, marking the account /// positionally in a uint256. The position in the bit field corresponds to the account ID (0-255), where 0 is the /// owner account's ID. function getOperator(bytes19 addressPrefix, address operator) external view returns (uint256 operatorBitField); /// @notice Returns whether a given operator has been authorized for a given account. /// @param account The address of the account whose operator is being checked. /// @param operator The address of the operator that is being checked. /// @return authorized A boolean value that indicates whether the operator is authorized for the account. function isAccountOperatorAuthorized(address account, address operator) external view returns (bool authorized); /// @notice Enables or disables lockdown mode for a given address prefix. /// @dev This function can only be called by the owner of the address prefix. To disable this mode, the EVC /// must be called directly. It is not possible to disable this mode by using checks-deferrable call or /// permit message. /// @param addressPrefix The address prefix for which the lockdown mode is being set. /// @param enabled A boolean indicating whether to enable or disable lockdown mode. function setLockdownMode(bytes19 addressPrefix, bool enabled) external payable; /// @notice Enables or disables permit functionality for a given address prefix. /// @dev This function can only be called by the owner of the address prefix. To disable this mode, the EVC /// must be called directly. It is not possible to disable this mode by using checks-deferrable call or (by /// definition) permit message. To support permit functionality by default, note that the logic was inverted here. To /// disable the permit functionality, one must pass true as the second argument. To enable the permit /// functionality, one must pass false as the second argument. /// @param addressPrefix The address prefix for which the permit functionality is being set. /// @param enabled A boolean indicating whether to enable or disable the disable-permit mode. function setPermitDisabledMode(bytes19 addressPrefix, bool enabled) external payable; /// @notice Sets the nonce for a given address prefix and nonce namespace. /// @dev This function can only be called by the owner of the address prefix. Each nonce namespace provides a 256 /// bit nonce that has to be used sequentially. There's no requirement to use all the nonces for a given nonce /// namespace before moving to the next one which allows the use of permit messages in a non-sequential manner. To /// invalidate signed permit messages, set the nonce for a given nonce namespace accordingly. To invalidate all the /// permit messages for a given nonce namespace, set the nonce to type(uint).max. /// @param addressPrefix The address prefix for which the nonce is being set. /// @param nonceNamespace The nonce namespace for which the nonce is being set. /// @param nonce The new nonce for the given address prefix and nonce namespace. function setNonce(bytes19 addressPrefix, uint256 nonceNamespace, uint256 nonce) external payable; /// @notice Sets the bit field for a given address prefix and operator. /// @dev This function can only be called by the owner of the address prefix. Each bit in the bit field corresponds /// to one account belonging to the same owner. If the bit is set, the operator is authorized for the account. /// @param addressPrefix The address prefix for which the bit field is being set. /// @param operator The address of the operator for which the bit field is being set. Can neither be the EVC address /// nor an address belonging to the same address prefix. /// @param operatorBitField The new bit field for the given address prefix and operator. Reverts if the provided /// value is equal to the currently stored value. function setOperator(bytes19 addressPrefix, address operator, uint256 operatorBitField) external payable; /// @notice Authorizes or deauthorizes an operator for the account. /// @dev Only the owner or authorized operator of the account can call this function. An operator is an address that /// can perform actions for an account on behalf of the owner. If it's an operator calling this function, it can /// only deauthorize itself. /// @param account The address of the account whose operator is being set or unset. /// @param operator The address of the operator that is being installed or uninstalled. Can neither be the EVC /// address nor an address belonging to the same owner as the account. /// @param authorized A boolean value that indicates whether the operator is being authorized or deauthorized. /// Reverts if the provided value is equal to the currently stored value. function setAccountOperator(address account, address operator, bool authorized) external payable; /// @notice Returns an array of collaterals enabled for an account. /// @dev A collateral is a vault for which an account's balances are under the control of the currently enabled /// controller vault. /// @param account The address of the account whose collaterals are being queried. /// @return An array of addresses that are enabled collaterals for the account. function getCollaterals(address account) external view returns (address[] memory); /// @notice Returns whether a collateral is enabled for an account. /// @dev A collateral is a vault for which account's balances are under the control of the currently enabled /// controller vault. /// @param account The address of the account that is being checked. /// @param vault The address of the collateral that is being checked. /// @return A boolean value that indicates whether the vault is an enabled collateral for the account or not. function isCollateralEnabled(address account, address vault) external view returns (bool); /// @notice Enables a collateral for an account. /// @dev A collaterals is a vault for which account's balances are under the control of the currently enabled /// controller vault. Only the owner or an operator of the account can call this function. Unless it's a duplicate, /// the collateral is added to the end of the array. There can be at most 10 unique collaterals enabled at a time. /// Account status checks are performed. /// @param account The account address for which the collateral is being enabled. /// @param vault The address being enabled as a collateral. function enableCollateral(address account, address vault) external payable; /// @notice Disables a collateral for an account. /// @dev This function does not preserve the order of collaterals in the array obtained using the getCollaterals /// function; the order may change. A collateral is a vault for which account’s balances are under the control of /// the currently enabled controller vault. Only the owner or an operator of the account can call this function. /// Disabling a collateral might change the order of collaterals in the array obtained using getCollaterals /// function. Account status checks are performed. /// @param account The account address for which the collateral is being disabled. /// @param vault The address of a collateral being disabled. function disableCollateral(address account, address vault) external payable; /// @notice Swaps the position of two collaterals so that they appear switched in the array of collaterals for a /// given account obtained by calling getCollaterals function. /// @dev A collateral is a vault for which account’s balances are under the control of the currently enabled /// controller vault. Only the owner or an operator of the account can call this function. The order of collaterals /// can be changed by specifying the indices of the two collaterals to be swapped. Indices are zero-based and must /// be in the range of 0 to the number of collaterals minus 1. index1 must be lower than index2. Account status /// checks are performed. /// @param account The address of the account for which the collaterals are being reordered. /// @param index1 The index of the first collateral to be swapped. /// @param index2 The index of the second collateral to be swapped. function reorderCollaterals(address account, uint8 index1, uint8 index2) external payable; /// @notice Returns an array of enabled controllers for an account. /// @dev A controller is a vault that has been chosen for an account to have special control over the account's /// balances in enabled collaterals vaults. A user can have multiple controllers during a call execution, but at /// most one can be selected when the account status check is performed. /// @param account The address of the account whose controllers are being queried. /// @return An array of addresses that are the enabled controllers for the account. function getControllers(address account) external view returns (address[] memory); /// @notice Returns whether a controller is enabled for an account. /// @dev A controller is a vault that has been chosen for an account to have special control over account’s /// balances in the enabled collaterals vaults. /// @param account The address of the account that is being checked. /// @param vault The address of the controller that is being checked. /// @return A boolean value that indicates whether the vault is enabled controller for the account or not. function isControllerEnabled(address account, address vault) external view returns (bool); /// @notice Enables a controller for an account. /// @dev A controller is a vault that has been chosen for an account to have special control over account’s /// balances in the enabled collaterals vaults. Only the owner or an operator of the account can call this function. /// Unless it's a duplicate, the controller is added to the end of the array. Transiently, there can be at most 10 /// unique controllers enabled at a time, but at most one can be enabled after the outermost checks-deferrable /// call concludes. Account status checks are performed. /// @param account The address for which the controller is being enabled. /// @param vault The address of the controller being enabled. function enableController(address account, address vault) external payable; /// @notice Disables a controller for an account. /// @dev A controller is a vault that has been chosen for an account to have special control over account’s /// balances in the enabled collaterals vaults. Only the vault itself can call this function. Disabling a controller /// might change the order of controllers in the array obtained using getControllers function. Account status checks /// are performed. /// @param account The address for which the calling controller is being disabled. function disableController(address account) external payable; /// @notice Executes signed arbitrary data by self-calling into the EVC. /// @dev Low-level call function is used to execute the arbitrary data signed by the owner or the operator on the /// EVC contract. During that call, EVC becomes msg.sender. /// @param signer The address signing the permit message (ECDSA) or verifying the permit message signature /// (ERC-1271). It's also the owner or the operator of all the accounts for which authentication will be needed /// during the execution of the arbitrary data call. /// @param sender The address of the msg.sender which is expected to execute the data signed by the signer. If /// address(0) is passed, the msg.sender is ignored. /// @param nonceNamespace The nonce namespace for which the nonce is being used. /// @param nonce The nonce for the given account and nonce namespace. A valid nonce value is considered to be the /// value currently stored and can take any value between 0 and type(uint256).max - 1. /// @param deadline The timestamp after which the permit is considered expired. /// @param value The amount of value to be forwarded with the call. If the value is type(uint256).max, the whole /// balance of the EVC contract will be forwarded. /// @param data The encoded data which is self-called on the EVC contract. /// @param signature The signature of the data signed by the signer. function permit( address signer, address sender, uint256 nonceNamespace, uint256 nonce, uint256 deadline, uint256 value, bytes calldata data, bytes calldata signature ) external payable; /// @notice Calls into a target contract as per data encoded. /// @dev This function defers the account and vault status checks (it's a checks-deferrable call). If the outermost /// call ends, the account and vault status checks are performed. /// @dev This function can be used to interact with any contract while checks are deferred. If the target contract /// is msg.sender, msg.sender is called back with the calldata provided and the context set up according to the /// account provided. If the target contract is not msg.sender, only the owner or the operator of the account /// provided can call this function. /// @dev This function can be used to recover the remaining value from the EVC contract. /// @param targetContract The address of the contract to be called. /// @param onBehalfOfAccount If the target contract is msg.sender, the address of the account which will be set /// in the context. It assumes msg.sender has authenticated the account themselves. If the target contract is /// not msg.sender, the address of the account for which it is checked whether msg.sender is authorized to act /// on behalf of. /// @param value The amount of value to be forwarded with the call. If the value is type(uint256).max, the whole /// balance of the EVC contract will be forwarded. /// @param data The encoded data which is called on the target contract. /// @return result The result of the call. function call( address targetContract, address onBehalfOfAccount, uint256 value, bytes calldata data ) external payable returns (bytes memory result); /// @notice For a given account, calls into one of the enabled collateral vaults from the currently enabled /// controller vault as per data encoded. /// @dev This function defers the account and vault status checks (it's a checks-deferrable call). If the outermost /// call ends, the account and vault status checks are performed. /// @dev This function can be used to interact with any contract while checks are deferred as long as the contract /// is enabled as a collateral of the account and the msg.sender is the only enabled controller of the account. /// @param targetCollateral The collateral address to be called. /// @param onBehalfOfAccount The address of the account for which it is checked whether msg.sender is authorized to /// act on behalf. /// @param value The amount of value to be forwarded with the call. If the value is type(uint256).max, the whole /// balance of the EVC contract will be forwarded. /// @param data The encoded data which is called on the target collateral. /// @return result The result of the call. function controlCollateral( address targetCollateral, address onBehalfOfAccount, uint256 value, bytes calldata data ) external payable returns (bytes memory result); /// @notice Executes multiple calls into the target contracts while checks deferred as per batch items provided. /// @dev This function defers the account and vault status checks (it's a checks-deferrable call). If the outermost /// call ends, the account and vault status checks are performed. /// @dev The authentication rules for each batch item are the same as for the call function. /// @param items An array of batch items to be executed. function batch(BatchItem[] calldata items) external payable; /// @notice Executes multiple calls into the target contracts while checks deferred as per batch items provided. /// @dev This function always reverts as it's only used for simulation purposes. This function cannot be called /// within a checks-deferrable call. /// @param items An array of batch items to be executed. function batchRevert(BatchItem[] calldata items) external payable; /// @notice Executes multiple calls into the target contracts while checks deferred as per batch items provided. /// @dev This function does not modify state and should only be used for simulation purposes. This function cannot /// be called within a checks-deferrable call. /// @param items An array of batch items to be executed. /// @return batchItemsResult An array of batch item results for each item. /// @return accountsStatusCheckResult An array of account status check results for each account. /// @return vaultsStatusCheckResult An array of vault status check results for each vault. function batchSimulation(BatchItem[] calldata items) external payable returns ( BatchItemResult[] memory batchItemsResult, StatusCheckResult[] memory accountsStatusCheckResult, StatusCheckResult[] memory vaultsStatusCheckResult ); /// @notice Retrieves the timestamp of the last successful account status check performed for a specific account. /// @dev This function reverts if the checks are in progress. /// @dev The account status check is considered to be successful if it calls into the selected controller vault and /// obtains expected magic value. This timestamp does not change if the account status is considered valid when no /// controller enabled. When consuming, one might need to ensure that the account status check is not deferred at /// the moment. /// @param account The address of the account for which the last status check timestamp is being queried. /// @return The timestamp of the last status check as a uint256. function getLastAccountStatusCheckTimestamp(address account) external view returns (uint256); /// @notice Checks whether the status check is deferred for a given account. /// @dev This function reverts if the checks are in progress. /// @param account The address of the account for which it is checked whether the status check is deferred. /// @return A boolean flag that indicates whether the status check is deferred or not. function isAccountStatusCheckDeferred(address account) external view returns (bool); /// @notice Checks the status of an account and reverts if it is not valid. /// @dev If checks deferred, the account is added to the set of accounts to be checked at the end of the outermost /// checks-deferrable call. There can be at most 10 unique accounts added to the set at a time. Account status /// check is performed by calling into the selected controller vault and passing the array of currently enabled /// collaterals. If controller is not selected, the account is always considered valid. /// @param account The address of the account to be checked. function requireAccountStatusCheck(address account) external payable; /// @notice Forgives previously deferred account status check. /// @dev Account address is removed from the set of addresses for which status checks are deferred. This function /// can only be called by the currently enabled controller of a given account. Depending on the vault /// implementation, may be needed in the liquidation flow. /// @param account The address of the account for which the status check is forgiven. function forgiveAccountStatusCheck(address account) external payable; /// @notice Checks whether the status check is deferred for a given vault. /// @dev This function reverts if the checks are in progress. /// @param vault The address of the vault for which it is checked whether the status check is deferred. /// @return A boolean flag that indicates whether the status check is deferred or not. function isVaultStatusCheckDeferred(address vault) external view returns (bool); /// @notice Checks the status of a vault and reverts if it is not valid. /// @dev If checks deferred, the vault is added to the set of vaults to be checked at the end of the outermost /// checks-deferrable call. There can be at most 10 unique vaults added to the set at a time. This function can /// only be called by the vault itself. function requireVaultStatusCheck() external payable; /// @notice Forgives previously deferred vault status check. /// @dev Vault address is removed from the set of addresses for which status checks are deferred. This function can /// only be called by the vault itself. function forgiveVaultStatusCheck() external payable; /// @notice Checks the status of an account and a vault and reverts if it is not valid. /// @dev If checks deferred, the account and the vault are added to the respective sets of accounts and vaults to be /// checked at the end of the outermost checks-deferrable call. Account status check is performed by calling into /// selected controller vault and passing the array of currently enabled collaterals. If controller is not selected, /// the account is always considered valid. This function can only be called by the vault itself. /// @param account The address of the account to be checked. function requireAccountAndVaultStatusCheck(address account) external payable; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; type EC is uint256; /// @title ExecutionContext /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice This library provides functions for managing the execution context in the Ethereum Vault Connector. /// @dev The execution context is a bit field that stores the following information: /// @dev - on behalf of account - an account on behalf of which the currently executed operation is being performed /// @dev - checks deferred flag - used to indicate whether checks are deferred /// @dev - checks in progress flag - used to indicate that the account/vault status checks are in progress. This flag is /// used to prevent re-entrancy. /// @dev - control collateral in progress flag - used to indicate that the control collateral is in progress. This flag /// is used to prevent re-entrancy. /// @dev - operator authenticated flag - used to indicate that the currently executed operation is being performed by /// the account operator /// @dev - simulation flag - used to indicate that the currently executed batch call is a simulation /// @dev - stamp - dummy value for optimization purposes library ExecutionContext { uint256 internal constant ON_BEHALF_OF_ACCOUNT_MASK = 0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; uint256 internal constant CHECKS_DEFERRED_MASK = 0x0000000000000000000000FF0000000000000000000000000000000000000000; uint256 internal constant CHECKS_IN_PROGRESS_MASK = 0x00000000000000000000FF000000000000000000000000000000000000000000; uint256 internal constant CONTROL_COLLATERAL_IN_PROGRESS_LOCK_MASK = 0x000000000000000000FF00000000000000000000000000000000000000000000; uint256 internal constant OPERATOR_AUTHENTICATED_MASK = 0x0000000000000000FF0000000000000000000000000000000000000000000000; uint256 internal constant SIMULATION_MASK = 0x00000000000000FF000000000000000000000000000000000000000000000000; uint256 internal constant STAMP_OFFSET = 200; // None of the functions below modifies the state. All the functions operate on the copy // of the execution context and return its modified value as a result. In order to update // one should use the result of the function call as a new execution context value. function getOnBehalfOfAccount(EC self) internal pure returns (address result) { result = address(uint160(EC.unwrap(self) & ON_BEHALF_OF_ACCOUNT_MASK)); } function setOnBehalfOfAccount(EC self, address account) internal pure returns (EC result) { result = EC.wrap((EC.unwrap(self) & ~ON_BEHALF_OF_ACCOUNT_MASK) | uint160(account)); } function areChecksDeferred(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & CHECKS_DEFERRED_MASK != 0; } function setChecksDeferred(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | CHECKS_DEFERRED_MASK); } function areChecksInProgress(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & CHECKS_IN_PROGRESS_MASK != 0; } function setChecksInProgress(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | CHECKS_IN_PROGRESS_MASK); } function isControlCollateralInProgress(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & CONTROL_COLLATERAL_IN_PROGRESS_LOCK_MASK != 0; } function setControlCollateralInProgress(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | CONTROL_COLLATERAL_IN_PROGRESS_LOCK_MASK); } function isOperatorAuthenticated(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & OPERATOR_AUTHENTICATED_MASK != 0; } function setOperatorAuthenticated(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | OPERATOR_AUTHENTICATED_MASK); } function clearOperatorAuthenticated(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) & ~OPERATOR_AUTHENTICATED_MASK); } function isSimulationInProgress(EC self) internal pure returns (bool result) { result = EC.unwrap(self) & SIMULATION_MASK != 0; } function setSimulationInProgress(EC self) internal pure returns (EC result) { result = EC.wrap(EC.unwrap(self) | SIMULATION_MASK); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC 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); }
{ "remappings": [ "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "ethereum-vault-connector/=lib/ethereum-vault-connector/src/", "evc/=lib/ethereum-vault-connector/src/", "evk/=lib/euler-vault-kit/src/", "evk-test/=lib/euler-vault-kit/test/", "euler-price-oracle/=lib/euler-price-oracle/src/", "euler-price-oracle-test/=lib/euler-price-oracle/test/", "fee-flow/=lib/fee-flow/src/", "reward-streams/=lib/reward-streams/src/", "@openzeppelin/contracts/utils/math/=lib/euler-price-oracle/lib/openzeppelin-contracts/contracts/utils/math/", "@pyth/=lib/euler-price-oracle/lib/pyth-sdk-solidity/", "@redstone/evm-connector/=lib/euler-price-oracle/lib/redstone-oracles-monorepo/packages/evm-connector/contracts/", "@solady/=lib/euler-price-oracle/lib/solady/src/", "@uniswap/v3-core/=lib/euler-price-oracle/lib/v3-core/", "@uniswap/v3-periphery/=lib/euler-price-oracle/lib/v3-periphery/", "ds-test/=lib/fee-flow/lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "euler-vault-kit/=lib/euler-vault-kit/src/", "forge-gas-snapshot/=lib/euler-vault-kit/lib/permit2/lib/forge-gas-snapshot/src/", "forge-std/=lib/forge-std/src/", "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/", "openzeppelin/=lib/ethereum-vault-connector/lib/openzeppelin-contracts/contracts/", "permit2/=lib/euler-vault-kit/lib/permit2/", "pyth-sdk-solidity/=lib/euler-price-oracle/lib/pyth-sdk-solidity/", "redstone-oracles-monorepo/=lib/euler-price-oracle/lib/", "solady/=lib/euler-price-oracle/lib/solady/src/", "solmate/=lib/fee-flow/lib/solmate/src/", "v3-core/=lib/euler-price-oracle/lib/v3-core/contracts/", "v3-periphery/=lib/euler-price-oracle/lib/v3-periphery/contracts/" ], "optimizer": { "enabled": true, "runs": 20000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"evc","type":"address"},{"internalType":"uint48","name":"epochDuration","type":"uint48"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccumulatorOverflow","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"ControllerDisabled","type":"error"},{"inputs":[],"name":"EVC_InvalidAddress","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidDistribution","type":"error"},{"inputs":[],"name":"InvalidEpoch","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TooManyElements","type":"error"},{"inputs":[],"name":"TooManyRewardsEnabled","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"rewarded","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"BalanceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"rewarded","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"rewarded","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"}],"name":"RewardDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"rewarded","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"}],"name":"RewardEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"rewarded","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"startEpoch","type":"uint256"},{"indexed":false,"internalType":"uint128[]","name":"amounts","type":"uint128[]"}],"name":"RewardRegistered","type":"event"},{"inputs":[],"name":"EPOCH_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EVC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DISTRIBUTION_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_EPOCHS_AHEAD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REWARDS_ENABLED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"rewarded","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"newAccountBalance","type":"uint256"},{"internalType":"bool","name":"forfeitRecentReward","type":"bool"}],"name":"balanceTrackerHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"ignoreRecentReward","type":"bool"}],"name":"claimReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"bool","name":"forfeitRecentReward","type":"bool"}],"name":"disableReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"bool","name":"ignoreRecentReward","type":"bool"}],"name":"earnedReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"enableReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"rewarded","type":"address"}],"name":"enabledRewards","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"timestamp","type":"uint48"}],"name":"getEpoch","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"epoch","type":"uint48"}],"name":"getEpochEndTimestamp","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"epoch","type":"uint48"}],"name":"getEpochStartTimestamp","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"isRewardEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"uint48","name":"startEpoch","type":"uint48"},{"internalType":"uint128[]","name":"rewardAmounts","type":"uint128[]"}],"name":"registerReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"rewardAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"uint48","name":"epoch","type":"uint48"}],"name":"rewardAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"totalRewardClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"totalRewardRegistered","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"totalRewardedEligible","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewarded","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"updateReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60c060405234801562000010575f80fd5b50604051620031a8380380620031a88339810160408190526200003391620000c6565b8181816001600160a01b0381166200005e57604051638133abd160e01b815260040160405180910390fd5b6001600160a01b031660805260015f5562093a8065ffffffffffff82161080620000925750625c49008165ffffffffffff16115b15620000b15760405163d5b25b6360e01b815260040160405180910390fd5b65ffffffffffff1660a0525062000116915050565b5f8060408385031215620000d8575f80fd5b82516001600160a01b0381168114620000ef575f80fd5b602084015190925065ffffffffffff811681146200010b575f80fd5b809150509250929050565b60805160a05161302a6200017e5f395f818161036b015281816104cb01528181610e3301528181610e73015281816112110152818161124001526123b501525f818161033c0152818161136f015281816113c301528181612489015261275b015261302a5ff3fe608060405234801561000f575f80fd5b506004361061018e575f3560e01c8063a70354a1116100dd578063cbf5ecb711610088578063ea5e43d311610063578063ea5e43d31461041f578063ee0223041461031a578063f7888aec14610432575f80fd5b8063cbf5ecb7146103d9578063d318f212146103ec578063e1f2560a146103ff575f80fd5b8063a975842f116100b8578063a975842f146103a0578063c29ad557146103b3578063c84705d4146103c6575f80fd5b8063a70354a114610322578063a70b9f0c14610366578063a8a1f5f41461038d575f80fd5b806333c359e31161013d57806381783f171161011857806381783f17146102e45780639308d4ca146102f7578063a4eb0d541461031a575f80fd5b806333c359e31461027e5780636fc4fdc1146102c757806376671808146102dc575f80fd5b806320768fc01161016d57806320768fc0146101f557806325ad1501146101fd578063263774e41461026b575f80fd5b80620b5556146101925780630756b010146101b8578063180be024146101e2575b5f80fd5b6101a56101a03660046129e5565b610477565b6040519081526020015b60405180910390f35b6101cb6101c6366004612a31565b6104c5565b60405165ffffffffffff90911681526020016101af565b6101a56101f0366004612a57565b6104f8565b6101a5601981565b6101a561020b3660046129e5565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815260016020908152604080832093909416825291909152206002015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690565b6101a5610279366004612a57565b610610565b6101a561028c3660046129e5565b73ffffffffffffffffffffffffffffffffffffffff8083165f9081526001602081815260408084209486168452939052919020015492915050565b6102da6102d5366004612ab0565b6106d9565b005b6101cb61082c565b6101a56102f23660046129e5565b61083b565b61030a6103053660046129e5565b610850565b60405190151581526020016101af565b6101a5600581565b60405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681526020016101af565b6101a57f000000000000000000000000000000000000000000000000000000000000000081565b6102da61039b366004612aef565b6109a6565b6101a56103ae366004612b90565b610d48565b6101cb6103c1366004612a31565b610e30565b6101cb6103d4366004612a31565b610e6d565b61030a6103e7366004612bcd565b610ea0565b6101a56103fa366004612c0a565b610fb0565b61041261040d3660046129e5565b610fed565b6040516101af9190612c4e565b61030a61042d366004612b90565b61102d565b6101a56104403660046129e5565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815260026020908152604080832093909416825291909152205490565b73ffffffffffffffffffffffffffffffffffffffff8083165f908152600160209081526040808320938516835292905220600201546fffffffffffffffffffffffffffffffff165b92915050565b5f6104bf7f000000000000000000000000000000000000000000000000000000000000000065ffffffffffff8416612d01565b73ffffffffffffffffffffffffffffffffffffffff8085165f9081526002602090815260408083209387168352929052908120816105396001830186611069565b610543575f610546565b81545b73ffffffffffffffffffffffffffffffffffffffff8088165f908152600160209081526040808320938a168352928152828220600c870190915291812092935091829161059491858961114e565b9094509250505073ffffffffffffffffffffffffffffffffffffffff89161580156105cc57506bffffffffffffffffffffffff811615155b156105f4576105db8183612d14565b6bffffffffffffffffffffffff16945050505050610608565b506bffffffffffffffffffffffff16925050505b949350505050565b5f610619611315565b5f610622611356565b73ffffffffffffffffffffffffffffffffffffffff8082165f908152600260209081526040808320938b1683529290529081209192506106656001830188611069565b61066f575f610672565b81545b73ffffffffffffffffffffffffffffffffffffffff808a165f908152600160209081526040808320938c168352928152828220600c87019091529190209192506106bf918a8a858a611449565b6106cb83898989611583565b935050505061060860015f55565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260026020908152604080832033808552925282208054919290919061071b6001840161176e565b905081861115610729575f94505b5f5b81518110156107c9575f82828151811061074757610747612d40565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8089165f9081526001845260408082209284168252918452818120600c8a019094522090925061079d9082908985898d611449565b848982600101546107ae9190612d6d565b6107b89190612d80565b60019182015591909101905061072b565b50858355604080518381526020810188905273ffffffffffffffffffffffffffffffffffffffff86811692908a16917f526824944047da5b81071fb6349412005c5da81380b336103fbe5dd34556c776910160405180910390a350505050505050565b5f610836426104c5565b905090565b5f61084983836103fa61082c565b9392505050565b5f8061085a611356565b73ffffffffffffffffffffffffffffffffffffffff8082165f9081526002602090815260408083209389168352929052908120919250600182019061089f82876118a4565b9050801561099c578154600560ff90911611156108e8576040517f1901b4d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8088165f908152600160209081526040808320938a1683529281528282208654600c880190925292822090916109399184918c908c9080611449565b80826001015f82825461094c9190612d6d565b909155505060405173ffffffffffffffffffffffffffffffffffffffff808a16918b8216918916907f08a6b6ed939ae3ee2858e2137cfb529aa7fb6e1372efe30b1f478e904123e6ad905f90a450505b9695505050505050565b6109ae611315565b5f6109b761082c565b90508365ffffffffffff165f036109d6576109d3816001612d93565b93505b8065ffffffffffff168465ffffffffffff16118015610a0f5750610a03600565ffffffffffff8316612d6d565b8465ffffffffffff1611155b610a45576040517fd5b25b6300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6019821115610a80576040517f68b5c19800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805b83811015610ad857848482818110610a9d57610a9d612d40565b9050602002016020810190610ab29190612dd1565b610ace906fffffffffffffffffffffffffffffffff1683612d6d565b9150600101610a83565b50805f03610b12576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8088165f908152600160209081526040808320938a1683529290529081208054909165ffffffffffff9091169003610b8e5780547fffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000164265ffffffffffff16178155610b9b565b610b9988885f610d48565b505b60028101545f90610bbf9084906fffffffffffffffffffffffffffffffff16612d6d565b905073ffffffffffffffffffffffffffffffffffffffff610be9826801158e460913d00000612dea565b1115610c21576040517ffbce66c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002820180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff83161790556040805160208781028083018201909352878252610c9d928c928c928c928c918c9182918501908490808284375f92019190915250611b6592505050565b5f610ca6611356565b9050610cb3898286611c80565b8873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f94d5941858c2721be2d67f19b407c04696f9eaa34e16abb742898248d5bace948b8b8b604051610d2b93929190612e01565b60405180910390a45050505050610d4160015f55565b5050505050565b5f80610d52611356565b73ffffffffffffffffffffffffffffffffffffffff8082165f908152600260209081526040808320938a168352929052908120919250610d956001830187611069565b610d9f575f610da2565b81545b73ffffffffffffffffffffffffffffffffffffffff8089165f908152600160209081526040808320938b168352928152828220600c8701909152918120929350610df292908a908a908690611449565b73ffffffffffffffffffffffffffffffffffffffff851615610e2457610e1a5f888888611583565b9350505050610849565b505f9695505050505050565b5f7f0000000000000000000000000000000000000000000000000000000000000000610e5b83610e6d565b65ffffffffffff166104bf9190612d6d565b5f6104bf7f000000000000000000000000000000000000000000000000000000000000000065ffffffffffff8416612dea565b5f80610eaa611356565b73ffffffffffffffffffffffffffffffffffffffff8082165f908152600260209081526040808320938a168352929052908120919250610eed6001830187611e09565b9050801561099c5773ffffffffffffffffffffffffffffffffffffffff8088165f908152600160209081526040808320938a1683529281528282208554600c870190925292909120610f449083908b8b858c611449565b80826001015f828254610f579190612d80565b909155505060405173ffffffffffffffffffffffffffffffffffffffff808a16918b8216918816907f3c7a1bb224d69e500abccb2db0335883596700abd7b166eb99f134bccdb6c81b905f90a450509695505050505050565b73ffffffffffffffffffffffffffffffffffffffff8084165f908152600160209081526040808320938616835292905290812061060890836122f9565b73ffffffffffffffffffffffffffffffffffffffff8083165f9081526002602090815260408083209385168352929052206060906108499060010161176e565b73ffffffffffffffffffffffffffffffffffffffff8084165f908152600260209081526040808320938616835292905290812061060890600101835b81545f9073ffffffffffffffffffffffffffffffffffffffff6101008204169060ff1680830361109d575f925050506104bf565b8373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036110db576001925050506104bf565b60015b81811015611143578473ffffffffffffffffffffffffffffffffffffffff168660010182600a811061111257611112612d40565b015473ffffffffffffffffffffffffffffffffffffffff160361113b57600193505050506104bf565b6001016110de565b505f95945050505050565b8354835465ffffffffffff8216916601000000000000900473ffffffffffffffffffffffffffffffffffffffff16906bffffffffffffffffffffffff165f83810361119a57505f61130a565b84611294575f6111a9856104c5565b90505f6111b461082c565b6111bf906001612d93565b90505f825b8265ffffffffffff168165ffffffffffff1610156111fe576111e68189612374565b6111f08d836122f9565b0291909101906001016111c4565b5060018b01545f81900361123d576112367f000000000000000000000000000000000000000000000000000000000000000083612d01565b945061128c565b807f00000000000000000000000000000000000000000000000000000000000000006801158e460913d0000084028161127857611278612ca7565b048161128657611286612ca7565b04870196505b429750505050505b86546801158e460913d000009087906112d3906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1686612e67565b73ffffffffffffffffffffffffffffffffffffffff166112f39190612dea565b6112fd9190612d01565b6113079083612d14565b91505b945094509450949050565b60025f5403611350576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025f55565b5f3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168103611444576040517f18503a1e0000000000000000000000000000000000000000000000000000000081525f60048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906318503a1e906024016040805180830381865afa15801561141c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114409190612e94565b5090505b919050565b5f805f806114598a8a888861114e565b8d5473ffffffffffffffffffffffffffffffffffffffff8416660100000000000081027fffffffffffff000000000000000000000000000000000000000000000000000090921665ffffffffffff871617919091178f556c01000000000000000000000000026bffffffffffffffffffffffff808416919091178e55939750919550935091508116156115775773ffffffffffffffffffffffffffffffffffffffff8881165f9081527fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b60209081526040808320938b168352600c90930190522080546bffffffffffffffffffffffff8082168401167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009091161790555b50505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8085165f908152600260209081526040808320878516845282528083209386168352600c909301905290812080546bffffffffffffffffffffffff1680156117525773ffffffffffffffffffffffffffffffffffffffff8087165f9081526001602090815260408083209389168352929052908120600281015490916fffffffffffffffffffffffffffffffff80831692700100000000000000000000000000000000900416906116498583612ec1565b9050806fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff16101561167f5761167f612eea565b6002840180546fffffffffffffffffffffffffffffffff908116700100000000000000000000000000000000848316021790915586547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001687556116e8908a908a908816612442565b6040516fffffffffffffffffffffffffffffffff8616815273ffffffffffffffffffffffffffffffffffffffff808b16918c8216918e16907f2422cac5e23c46c890fdcf42d0c64757409df6832174df639337558f09d99c689060200160405180910390a4505050505b6fffffffffffffffffffffffffffffffff169695505050505050565b805460609073ffffffffffffffffffffffffffffffffffffffff6101008204169060ff165f8167ffffffffffffffff8111156117ac576117ac612f17565b6040519080825280602002602001820160405280156117d5578160200160208202803683370190505b509050815f036117e757949350505050565b82815f815181106117fa576117fa612d40565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015260015b8281101561189b578560010181600a811061183f5761183f612d40565b0154825173ffffffffffffffffffffffffffffffffffffffff9091169083908390811061186e5761186e612d40565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152600101611822565b50949350505050565b81545f90610100810473ffffffffffffffffffffffffffffffffffffffff169060ff8116907501000000000000000000000000000000000000000000900469ffffffffffffffffffff168184036119d25785547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff9092167501000000000000000000000000000000000000000000029190911674ffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff8716610100027fffffffffffffffffffffff00000000000000000000000000000000000000000090931692909217600190811792909216177f010000000000000000000000000000000000000000000000000000000000000017865592506104bf915050565b8473ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611a10575f93505050506104bf565b60015b82811015611a78578573ffffffffffffffffffffffffffffffffffffffff168760010182600a8110611a4757611a47612d40565b015473ffffffffffffffffffffffffffffffffffffffff1603611a70575f9450505050506104bf565b600101611a13565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff68201611ad2576040517f3572cf8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848660010183600a8110611ae857611ae8612d40565b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790555084547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600191820160ff1617909455509192915050565b73ffffffffffffffffffffffffffffffffffffffff8085165f9081526001602090815260408083209387168352929052908120600301905b82518165ffffffffffff161015611c78575f8185019050838265ffffffffffff1681518110611bce57611bce612d40565b6020026020010151835f60028465ffffffffffff1681611bf057611bf0612ca7565b0481526020019081526020015f2060028365ffffffffffff1681611c1657611c16612ca7565b0660028110611c2757611c27612d40565b6002810490910180546fffffffffffffffffffffffffffffffff60106001909416939093026101000a808204841690940183168402929093021990921617905550611c7181612f44565b9050611b9d565b505050505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8516906370a0823190602401602060405180830381865afa158015611cea573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d0e9190612f68565b9050611d3273ffffffffffffffffffffffffffffffffffffffff85168430856125cf565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528290829073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015611d9e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dc29190612f68565b611dcc9190612d80565b14611e03576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b81545f90610100810473ffffffffffffffffffffffffffffffffffffffff169060ff8116907501000000000000000000000000000000000000000000900469ffffffffffffffffffff16818403611e65575f93505050506104bf565b5f8573ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614611f09575060015b82811015611ef6578573ffffffffffffffffffffffffffffffffffffffff168760010182600a8110611ed157611ed1612d40565b015473ffffffffffffffffffffffffffffffffffffffff1614611ef657600101611e9d565b828103611f09575f9450505050506104bf565b82600103611f8957507effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff909116750100000000000000000000000000000000000000000002167f010000000000000000000000000000000000000000000000000000000000000017855550600191506104bf9050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83015f6001890182600a8110611fc257611fc2612d40565b0190508183146121ed57825f036120b557805489547f010000000000000000000000000000000000000000000000000000000000000060ff85167fff0000000000000000000000000000000000000000000000000000000000000090921661010073ffffffffffffffffffffffffffffffffffffffff909416939093027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00169290921717750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161789556122c5565b805473ffffffffffffffffffffffffffffffffffffffff1660018a0184600a81106120e2576120e2612d40565b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92831617905589547f010000000000000000000000000000000000000000000000000000000000000060ff85167fff00000000000000000000000000000000000000000000000000000000000000909216610100938a16939093027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00169290921717750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161789556122c5565b88547f010000000000000000000000000000000000000000000000000000000000000060ff84167fff0000000000000000000000000000000000000000000000000000000000000090921661010073ffffffffffffffffffffffffffffffffffffffff8a16027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00161791909117750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161789555b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055506001979650505050505050565b5f6003830181612312600265ffffffffffff8616612d01565b81526020019081526020015f2060028365ffffffffffff166123349190612f7f565b6002811061234457612344612d40565b60028104919091015460019091166010026101000a90046fffffffffffffffffffffffffffffffff169392505050565b5f8061237f84610e6d565b65ffffffffffff1690505f61239385610e30565b65ffffffffffff1690508042106123f457818465ffffffffffff16116123d9577f00000000000000000000000000000000000000000000000000000000000000006123eb565b6123eb65ffffffffffff851682612d80565b925050506104bf565b81421015801561240357508042105b1561243857818465ffffffffffff1611612426576124218242612d80565b6123eb565b6123eb65ffffffffffff851642612d80565b5f925050506104bf565b6040517f442b172c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301525f917f00000000000000000000000000000000000000000000000000000000000000009091169063442b172c90602401602060405180830381865afa1580156124d0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124f49190612f92565b905073ffffffffffffffffffffffffffffffffffffffff83161580612577575073ffffffffffffffffffffffffffffffffffffffff81161580159061256557508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b8015612577575061257584612658565b155b156125ae576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e0373ffffffffffffffffffffffffffffffffffffffff85168484612799565b60405173ffffffffffffffffffffffffffffffffffffffff8481166024830152838116604483015260648201839052611e039186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506127dc565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa70354a10000000000000000000000000000000000000000000000000000000017905290515f918291829173ffffffffffffffffffffffffffffffffffffffff8616916126d89190612fad565b5f60405180830381855afa9150503d805f8114612710576040519150601f19603f3d011682016040523d82523d5f602084013e612715565b606091505b5091509150818015612728575080516020145b80156106085750808060200190518101906127439190612f92565b73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1614949350505050565b60405173ffffffffffffffffffffffffffffffffffffffff8381166024830152604482018390526127d791859182169063a9059cbb90606401612611565b505050565b5f6127fd73ffffffffffffffffffffffffffffffffffffffff841683612875565b905080515f1415801561282157508080602001905181019061281f9190612fd9565b155b156127d7576040517f5274afe700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024015b60405180910390fd5b606061084983835f845f808573ffffffffffffffffffffffffffffffffffffffff1684866040516128a69190612fad565b5f6040518083038185875af1925050503d805f81146128e0576040519150601f19603f3d011682016040523d82523d5f602084013e6128e5565b606091505b509150915061099c868383606082612905576129008261297f565b610849565b8151158015612929575073ffffffffffffffffffffffffffffffffffffffff84163b155b15612978576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015260240161286c565b5080610849565b80511561298f5780518082602001fd5b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b73ffffffffffffffffffffffffffffffffffffffff811681146129c1575f80fd5b5f80604083850312156129f6575f80fd5b8235612a01816129c4565b91506020830135612a11816129c4565b809150509250929050565b803565ffffffffffff81168114611444575f80fd5b5f60208284031215612a41575f80fd5b61084982612a1c565b80151581146129c1575f80fd5b5f805f8060808587031215612a6a575f80fd5b8435612a75816129c4565b93506020850135612a85816129c4565b92506040850135612a95816129c4565b91506060850135612aa581612a4a565b939692955090935050565b5f805f60608486031215612ac2575f80fd5b8335612acd816129c4565b9250602084013591506040840135612ae481612a4a565b809150509250925092565b5f805f805f60808688031215612b03575f80fd5b8535612b0e816129c4565b94506020860135612b1e816129c4565b9350612b2c60408701612a1c565b9250606086013567ffffffffffffffff80821115612b48575f80fd5b818801915088601f830112612b5b575f80fd5b813581811115612b69575f80fd5b8960208260051b8501011115612b7d575f80fd5b9699959850939650602001949392505050565b5f805f60608486031215612ba2575f80fd5b8335612bad816129c4565b92506020840135612bbd816129c4565b91506040840135612ae4816129c4565b5f805f60608486031215612bdf575f80fd5b8335612bea816129c4565b92506020840135612bfa816129c4565b91506040840135612ae481612a4a565b5f805f60608486031215612c1c575f80fd5b8335612c27816129c4565b92506020840135612c37816129c4565b9150612c4560408501612a1c565b90509250925092565b602080825282518282018190525f9190848201906040850190845b81811015612c9b57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101612c69565b50909695505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f82612d0f57612d0f612ca7565b500490565b6bffffffffffffffffffffffff818116838216019080821115612d3957612d39612cd4565b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b808201808211156104bf576104bf612cd4565b818103818111156104bf576104bf612cd4565b65ffffffffffff818116838216019080821115612d3957612d39612cd4565b80356fffffffffffffffffffffffffffffffff81168114611444575f80fd5b5f60208284031215612de1575f80fd5b61084982612db2565b80820281158282048414176104bf576104bf612cd4565b65ffffffffffff84168152604060208083018290529082018390525f90849060608401835b86811015612e5b576fffffffffffffffffffffffffffffffff612e4885612db2565b1682529282019290820190600101612e26565b50979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff828116828216039080821115612d3957612d39612cd4565b5f8060408385031215612ea5575f80fd5b8251612eb0816129c4565b6020840151909250612a1181612a4a565b6fffffffffffffffffffffffffffffffff818116838216019080821115612d3957612d39612cd4565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f65ffffffffffff808316818103612f5e57612f5e612cd4565b6001019392505050565b5f60208284031215612f78575f80fd5b5051919050565b5f82612f8d57612f8d612ca7565b500690565b5f60208284031215612fa2575f80fd5b8151610849816129c4565b5f82515f5b81811015612fcc5760208186018101518583015201612fb2565b505f920191825250919050565b5f60208284031215612fe9575f80fd5b815161084981612a4a56fea26469706673582212204ecb43fd31408f5d9e78053b84b956149088a29b87d5bdaa5494c52578a48dd964736f6c634300081800330000000000000000000000000c9a3dd6b8f28529d72d7f9ce918d493519ee3830000000000000000000000000000000000000000000000000000000000127500
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061018e575f3560e01c8063a70354a1116100dd578063cbf5ecb711610088578063ea5e43d311610063578063ea5e43d31461041f578063ee0223041461031a578063f7888aec14610432575f80fd5b8063cbf5ecb7146103d9578063d318f212146103ec578063e1f2560a146103ff575f80fd5b8063a975842f116100b8578063a975842f146103a0578063c29ad557146103b3578063c84705d4146103c6575f80fd5b8063a70354a114610322578063a70b9f0c14610366578063a8a1f5f41461038d575f80fd5b806333c359e31161013d57806381783f171161011857806381783f17146102e45780639308d4ca146102f7578063a4eb0d541461031a575f80fd5b806333c359e31461027e5780636fc4fdc1146102c757806376671808146102dc575f80fd5b806320768fc01161016d57806320768fc0146101f557806325ad1501146101fd578063263774e41461026b575f80fd5b80620b5556146101925780630756b010146101b8578063180be024146101e2575b5f80fd5b6101a56101a03660046129e5565b610477565b6040519081526020015b60405180910390f35b6101cb6101c6366004612a31565b6104c5565b60405165ffffffffffff90911681526020016101af565b6101a56101f0366004612a57565b6104f8565b6101a5601981565b6101a561020b3660046129e5565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815260016020908152604080832093909416825291909152206002015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690565b6101a5610279366004612a57565b610610565b6101a561028c3660046129e5565b73ffffffffffffffffffffffffffffffffffffffff8083165f9081526001602081815260408084209486168452939052919020015492915050565b6102da6102d5366004612ab0565b6106d9565b005b6101cb61082c565b6101a56102f23660046129e5565b61083b565b61030a6103053660046129e5565b610850565b60405190151581526020016101af565b6101a5600581565b60405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000c9a3dd6b8f28529d72d7f9ce918d493519ee3831681526020016101af565b6101a57f000000000000000000000000000000000000000000000000000000000012750081565b6102da61039b366004612aef565b6109a6565b6101a56103ae366004612b90565b610d48565b6101cb6103c1366004612a31565b610e30565b6101cb6103d4366004612a31565b610e6d565b61030a6103e7366004612bcd565b610ea0565b6101a56103fa366004612c0a565b610fb0565b61041261040d3660046129e5565b610fed565b6040516101af9190612c4e565b61030a61042d366004612b90565b61102d565b6101a56104403660046129e5565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815260026020908152604080832093909416825291909152205490565b73ffffffffffffffffffffffffffffffffffffffff8083165f908152600160209081526040808320938516835292905220600201546fffffffffffffffffffffffffffffffff165b92915050565b5f6104bf7f000000000000000000000000000000000000000000000000000000000012750065ffffffffffff8416612d01565b73ffffffffffffffffffffffffffffffffffffffff8085165f9081526002602090815260408083209387168352929052908120816105396001830186611069565b610543575f610546565b81545b73ffffffffffffffffffffffffffffffffffffffff8088165f908152600160209081526040808320938a168352928152828220600c870190915291812092935091829161059491858961114e565b9094509250505073ffffffffffffffffffffffffffffffffffffffff89161580156105cc57506bffffffffffffffffffffffff811615155b156105f4576105db8183612d14565b6bffffffffffffffffffffffff16945050505050610608565b506bffffffffffffffffffffffff16925050505b949350505050565b5f610619611315565b5f610622611356565b73ffffffffffffffffffffffffffffffffffffffff8082165f908152600260209081526040808320938b1683529290529081209192506106656001830188611069565b61066f575f610672565b81545b73ffffffffffffffffffffffffffffffffffffffff808a165f908152600160209081526040808320938c168352928152828220600c87019091529190209192506106bf918a8a858a611449565b6106cb83898989611583565b935050505061060860015f55565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260026020908152604080832033808552925282208054919290919061071b6001840161176e565b905081861115610729575f94505b5f5b81518110156107c9575f82828151811061074757610747612d40565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8089165f9081526001845260408082209284168252918452818120600c8a019094522090925061079d9082908985898d611449565b848982600101546107ae9190612d6d565b6107b89190612d80565b60019182015591909101905061072b565b50858355604080518381526020810188905273ffffffffffffffffffffffffffffffffffffffff86811692908a16917f526824944047da5b81071fb6349412005c5da81380b336103fbe5dd34556c776910160405180910390a350505050505050565b5f610836426104c5565b905090565b5f61084983836103fa61082c565b9392505050565b5f8061085a611356565b73ffffffffffffffffffffffffffffffffffffffff8082165f9081526002602090815260408083209389168352929052908120919250600182019061089f82876118a4565b9050801561099c578154600560ff90911611156108e8576040517f1901b4d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8088165f908152600160209081526040808320938a1683529281528282208654600c880190925292822090916109399184918c908c9080611449565b80826001015f82825461094c9190612d6d565b909155505060405173ffffffffffffffffffffffffffffffffffffffff808a16918b8216918916907f08a6b6ed939ae3ee2858e2137cfb529aa7fb6e1372efe30b1f478e904123e6ad905f90a450505b9695505050505050565b6109ae611315565b5f6109b761082c565b90508365ffffffffffff165f036109d6576109d3816001612d93565b93505b8065ffffffffffff168465ffffffffffff16118015610a0f5750610a03600565ffffffffffff8316612d6d565b8465ffffffffffff1611155b610a45576040517fd5b25b6300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6019821115610a80576040517f68b5c19800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805b83811015610ad857848482818110610a9d57610a9d612d40565b9050602002016020810190610ab29190612dd1565b610ace906fffffffffffffffffffffffffffffffff1683612d6d565b9150600101610a83565b50805f03610b12576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8088165f908152600160209081526040808320938a1683529290529081208054909165ffffffffffff9091169003610b8e5780547fffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000164265ffffffffffff16178155610b9b565b610b9988885f610d48565b505b60028101545f90610bbf9084906fffffffffffffffffffffffffffffffff16612d6d565b905073ffffffffffffffffffffffffffffffffffffffff610be9826801158e460913d00000612dea565b1115610c21576040517ffbce66c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002820180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff83161790556040805160208781028083018201909352878252610c9d928c928c928c928c918c9182918501908490808284375f92019190915250611b6592505050565b5f610ca6611356565b9050610cb3898286611c80565b8873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f94d5941858c2721be2d67f19b407c04696f9eaa34e16abb742898248d5bace948b8b8b604051610d2b93929190612e01565b60405180910390a45050505050610d4160015f55565b5050505050565b5f80610d52611356565b73ffffffffffffffffffffffffffffffffffffffff8082165f908152600260209081526040808320938a168352929052908120919250610d956001830187611069565b610d9f575f610da2565b81545b73ffffffffffffffffffffffffffffffffffffffff8089165f908152600160209081526040808320938b168352928152828220600c8701909152918120929350610df292908a908a908690611449565b73ffffffffffffffffffffffffffffffffffffffff851615610e2457610e1a5f888888611583565b9350505050610849565b505f9695505050505050565b5f7f0000000000000000000000000000000000000000000000000000000000127500610e5b83610e6d565b65ffffffffffff166104bf9190612d6d565b5f6104bf7f000000000000000000000000000000000000000000000000000000000012750065ffffffffffff8416612dea565b5f80610eaa611356565b73ffffffffffffffffffffffffffffffffffffffff8082165f908152600260209081526040808320938a168352929052908120919250610eed6001830187611e09565b9050801561099c5773ffffffffffffffffffffffffffffffffffffffff8088165f908152600160209081526040808320938a1683529281528282208554600c870190925292909120610f449083908b8b858c611449565b80826001015f828254610f579190612d80565b909155505060405173ffffffffffffffffffffffffffffffffffffffff808a16918b8216918816907f3c7a1bb224d69e500abccb2db0335883596700abd7b166eb99f134bccdb6c81b905f90a450509695505050505050565b73ffffffffffffffffffffffffffffffffffffffff8084165f908152600160209081526040808320938616835292905290812061060890836122f9565b73ffffffffffffffffffffffffffffffffffffffff8083165f9081526002602090815260408083209385168352929052206060906108499060010161176e565b73ffffffffffffffffffffffffffffffffffffffff8084165f908152600260209081526040808320938616835292905290812061060890600101835b81545f9073ffffffffffffffffffffffffffffffffffffffff6101008204169060ff1680830361109d575f925050506104bf565b8373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036110db576001925050506104bf565b60015b81811015611143578473ffffffffffffffffffffffffffffffffffffffff168660010182600a811061111257611112612d40565b015473ffffffffffffffffffffffffffffffffffffffff160361113b57600193505050506104bf565b6001016110de565b505f95945050505050565b8354835465ffffffffffff8216916601000000000000900473ffffffffffffffffffffffffffffffffffffffff16906bffffffffffffffffffffffff165f83810361119a57505f61130a565b84611294575f6111a9856104c5565b90505f6111b461082c565b6111bf906001612d93565b90505f825b8265ffffffffffff168165ffffffffffff1610156111fe576111e68189612374565b6111f08d836122f9565b0291909101906001016111c4565b5060018b01545f81900361123d576112367f000000000000000000000000000000000000000000000000000000000012750083612d01565b945061128c565b807f00000000000000000000000000000000000000000000000000000000001275006801158e460913d0000084028161127857611278612ca7565b048161128657611286612ca7565b04870196505b429750505050505b86546801158e460913d000009087906112d3906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1686612e67565b73ffffffffffffffffffffffffffffffffffffffff166112f39190612dea565b6112fd9190612d01565b6113079083612d14565b91505b945094509450949050565b60025f5403611350576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025f55565b5f3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000c9a3dd6b8f28529d72d7f9ce918d493519ee383168103611444576040517f18503a1e0000000000000000000000000000000000000000000000000000000081525f60048201527f0000000000000000000000000c9a3dd6b8f28529d72d7f9ce918d493519ee38373ffffffffffffffffffffffffffffffffffffffff16906318503a1e906024016040805180830381865afa15801561141c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114409190612e94565b5090505b919050565b5f805f806114598a8a888861114e565b8d5473ffffffffffffffffffffffffffffffffffffffff8416660100000000000081027fffffffffffff000000000000000000000000000000000000000000000000000090921665ffffffffffff871617919091178f556c01000000000000000000000000026bffffffffffffffffffffffff808416919091178e55939750919550935091508116156115775773ffffffffffffffffffffffffffffffffffffffff8881165f9081527fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b60209081526040808320938b168352600c90930190522080546bffffffffffffffffffffffff8082168401167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009091161790555b50505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8085165f908152600260209081526040808320878516845282528083209386168352600c909301905290812080546bffffffffffffffffffffffff1680156117525773ffffffffffffffffffffffffffffffffffffffff8087165f9081526001602090815260408083209389168352929052908120600281015490916fffffffffffffffffffffffffffffffff80831692700100000000000000000000000000000000900416906116498583612ec1565b9050806fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff16101561167f5761167f612eea565b6002840180546fffffffffffffffffffffffffffffffff908116700100000000000000000000000000000000848316021790915586547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001687556116e8908a908a908816612442565b6040516fffffffffffffffffffffffffffffffff8616815273ffffffffffffffffffffffffffffffffffffffff808b16918c8216918e16907f2422cac5e23c46c890fdcf42d0c64757409df6832174df639337558f09d99c689060200160405180910390a4505050505b6fffffffffffffffffffffffffffffffff169695505050505050565b805460609073ffffffffffffffffffffffffffffffffffffffff6101008204169060ff165f8167ffffffffffffffff8111156117ac576117ac612f17565b6040519080825280602002602001820160405280156117d5578160200160208202803683370190505b509050815f036117e757949350505050565b82815f815181106117fa576117fa612d40565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015260015b8281101561189b578560010181600a811061183f5761183f612d40565b0154825173ffffffffffffffffffffffffffffffffffffffff9091169083908390811061186e5761186e612d40565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152600101611822565b50949350505050565b81545f90610100810473ffffffffffffffffffffffffffffffffffffffff169060ff8116907501000000000000000000000000000000000000000000900469ffffffffffffffffffff168184036119d25785547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff9092167501000000000000000000000000000000000000000000029190911674ffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff8716610100027fffffffffffffffffffffff00000000000000000000000000000000000000000090931692909217600190811792909216177f010000000000000000000000000000000000000000000000000000000000000017865592506104bf915050565b8473ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611a10575f93505050506104bf565b60015b82811015611a78578573ffffffffffffffffffffffffffffffffffffffff168760010182600a8110611a4757611a47612d40565b015473ffffffffffffffffffffffffffffffffffffffff1603611a70575f9450505050506104bf565b600101611a13565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff68201611ad2576040517f3572cf8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848660010183600a8110611ae857611ae8612d40565b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790555084547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600191820160ff1617909455509192915050565b73ffffffffffffffffffffffffffffffffffffffff8085165f9081526001602090815260408083209387168352929052908120600301905b82518165ffffffffffff161015611c78575f8185019050838265ffffffffffff1681518110611bce57611bce612d40565b6020026020010151835f60028465ffffffffffff1681611bf057611bf0612ca7565b0481526020019081526020015f2060028365ffffffffffff1681611c1657611c16612ca7565b0660028110611c2757611c27612d40565b6002810490910180546fffffffffffffffffffffffffffffffff60106001909416939093026101000a808204841690940183168402929093021990921617905550611c7181612f44565b9050611b9d565b505050505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8516906370a0823190602401602060405180830381865afa158015611cea573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d0e9190612f68565b9050611d3273ffffffffffffffffffffffffffffffffffffffff85168430856125cf565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528290829073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015611d9e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dc29190612f68565b611dcc9190612d80565b14611e03576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b81545f90610100810473ffffffffffffffffffffffffffffffffffffffff169060ff8116907501000000000000000000000000000000000000000000900469ffffffffffffffffffff16818403611e65575f93505050506104bf565b5f8573ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614611f09575060015b82811015611ef6578573ffffffffffffffffffffffffffffffffffffffff168760010182600a8110611ed157611ed1612d40565b015473ffffffffffffffffffffffffffffffffffffffff1614611ef657600101611e9d565b828103611f09575f9450505050506104bf565b82600103611f8957507effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff909116750100000000000000000000000000000000000000000002167f010000000000000000000000000000000000000000000000000000000000000017855550600191506104bf9050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83015f6001890182600a8110611fc257611fc2612d40565b0190508183146121ed57825f036120b557805489547f010000000000000000000000000000000000000000000000000000000000000060ff85167fff0000000000000000000000000000000000000000000000000000000000000090921661010073ffffffffffffffffffffffffffffffffffffffff909416939093027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00169290921717750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161789556122c5565b805473ffffffffffffffffffffffffffffffffffffffff1660018a0184600a81106120e2576120e2612d40565b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92831617905589547f010000000000000000000000000000000000000000000000000000000000000060ff85167fff00000000000000000000000000000000000000000000000000000000000000909216610100938a16939093027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00169290921717750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161789556122c5565b88547f010000000000000000000000000000000000000000000000000000000000000060ff84167fff0000000000000000000000000000000000000000000000000000000000000090921661010073ffffffffffffffffffffffffffffffffffffffff8a16027fff00000000000000000000ffffffffffffffffffffffffffffffffffffffff00161791909117750100000000000000000000000000000000000000000069ffffffffffffffffffff871602177effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161789555b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055506001979650505050505050565b5f6003830181612312600265ffffffffffff8616612d01565b81526020019081526020015f2060028365ffffffffffff166123349190612f7f565b6002811061234457612344612d40565b60028104919091015460019091166010026101000a90046fffffffffffffffffffffffffffffffff169392505050565b5f8061237f84610e6d565b65ffffffffffff1690505f61239385610e30565b65ffffffffffff1690508042106123f457818465ffffffffffff16116123d9577f00000000000000000000000000000000000000000000000000000000001275006123eb565b6123eb65ffffffffffff851682612d80565b925050506104bf565b81421015801561240357508042105b1561243857818465ffffffffffff1611612426576124218242612d80565b6123eb565b6123eb65ffffffffffff851642612d80565b5f925050506104bf565b6040517f442b172c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301525f917f0000000000000000000000000c9a3dd6b8f28529d72d7f9ce918d493519ee3839091169063442b172c90602401602060405180830381865afa1580156124d0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124f49190612f92565b905073ffffffffffffffffffffffffffffffffffffffff83161580612577575073ffffffffffffffffffffffffffffffffffffffff81161580159061256557508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b8015612577575061257584612658565b155b156125ae576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e0373ffffffffffffffffffffffffffffffffffffffff85168484612799565b60405173ffffffffffffffffffffffffffffffffffffffff8481166024830152838116604483015260648201839052611e039186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506127dc565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa70354a10000000000000000000000000000000000000000000000000000000017905290515f918291829173ffffffffffffffffffffffffffffffffffffffff8616916126d89190612fad565b5f60405180830381855afa9150503d805f8114612710576040519150601f19603f3d011682016040523d82523d5f602084013e612715565b606091505b5091509150818015612728575080516020145b80156106085750808060200190518101906127439190612f92565b73ffffffffffffffffffffffffffffffffffffffff167f0000000000000000000000000c9a3dd6b8f28529d72d7f9ce918d493519ee38373ffffffffffffffffffffffffffffffffffffffff1614949350505050565b60405173ffffffffffffffffffffffffffffffffffffffff8381166024830152604482018390526127d791859182169063a9059cbb90606401612611565b505050565b5f6127fd73ffffffffffffffffffffffffffffffffffffffff841683612875565b905080515f1415801561282157508080602001905181019061281f9190612fd9565b155b156127d7576040517f5274afe700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024015b60405180910390fd5b606061084983835f845f808573ffffffffffffffffffffffffffffffffffffffff1684866040516128a69190612fad565b5f6040518083038185875af1925050503d805f81146128e0576040519150601f19603f3d011682016040523d82523d5f602084013e6128e5565b606091505b509150915061099c868383606082612905576129008261297f565b610849565b8151158015612929575073ffffffffffffffffffffffffffffffffffffffff84163b155b15612978576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015260240161286c565b5080610849565b80511561298f5780518082602001fd5b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b73ffffffffffffffffffffffffffffffffffffffff811681146129c1575f80fd5b5f80604083850312156129f6575f80fd5b8235612a01816129c4565b91506020830135612a11816129c4565b809150509250929050565b803565ffffffffffff81168114611444575f80fd5b5f60208284031215612a41575f80fd5b61084982612a1c565b80151581146129c1575f80fd5b5f805f8060808587031215612a6a575f80fd5b8435612a75816129c4565b93506020850135612a85816129c4565b92506040850135612a95816129c4565b91506060850135612aa581612a4a565b939692955090935050565b5f805f60608486031215612ac2575f80fd5b8335612acd816129c4565b9250602084013591506040840135612ae481612a4a565b809150509250925092565b5f805f805f60808688031215612b03575f80fd5b8535612b0e816129c4565b94506020860135612b1e816129c4565b9350612b2c60408701612a1c565b9250606086013567ffffffffffffffff80821115612b48575f80fd5b818801915088601f830112612b5b575f80fd5b813581811115612b69575f80fd5b8960208260051b8501011115612b7d575f80fd5b9699959850939650602001949392505050565b5f805f60608486031215612ba2575f80fd5b8335612bad816129c4565b92506020840135612bbd816129c4565b91506040840135612ae4816129c4565b5f805f60608486031215612bdf575f80fd5b8335612bea816129c4565b92506020840135612bfa816129c4565b91506040840135612ae481612a4a565b5f805f60608486031215612c1c575f80fd5b8335612c27816129c4565b92506020840135612c37816129c4565b9150612c4560408501612a1c565b90509250925092565b602080825282518282018190525f9190848201906040850190845b81811015612c9b57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101612c69565b50909695505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f82612d0f57612d0f612ca7565b500490565b6bffffffffffffffffffffffff818116838216019080821115612d3957612d39612cd4565b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b808201808211156104bf576104bf612cd4565b818103818111156104bf576104bf612cd4565b65ffffffffffff818116838216019080821115612d3957612d39612cd4565b80356fffffffffffffffffffffffffffffffff81168114611444575f80fd5b5f60208284031215612de1575f80fd5b61084982612db2565b80820281158282048414176104bf576104bf612cd4565b65ffffffffffff84168152604060208083018290529082018390525f90849060608401835b86811015612e5b576fffffffffffffffffffffffffffffffff612e4885612db2565b1682529282019290820190600101612e26565b50979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff828116828216039080821115612d3957612d39612cd4565b5f8060408385031215612ea5575f80fd5b8251612eb0816129c4565b6020840151909250612a1181612a4a565b6fffffffffffffffffffffffffffffffff818116838216019080821115612d3957612d39612cd4565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f65ffffffffffff808316818103612f5e57612f5e612cd4565b6001019392505050565b5f60208284031215612f78575f80fd5b5051919050565b5f82612f8d57612f8d612ca7565b500690565b5f60208284031215612fa2575f80fd5b8151610849816129c4565b5f82515f5b81811015612fcc5760208186018101518583015201612fb2565b505f920191825250919050565b5f60208284031215612fe9575f80fd5b815161084981612a4a56fea26469706673582212204ecb43fd31408f5d9e78053b84b956149088a29b87d5bdaa5494c52578a48dd964736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000c9a3dd6b8f28529d72d7f9ce918d493519ee3830000000000000000000000000000000000000000000000000000000000127500
-----Decoded View---------------
Arg [0] : evc (address): 0x0C9a3dd6b8F28529d72d7f9cE918D493519EE383
Arg [1] : epochDuration (uint48): 1209600
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000c9a3dd6b8f28529d72d7f9ce918d493519ee383
Arg [1] : 0000000000000000000000000000000000000000000000000000000000127500
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
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.