Contract Overview
Latest 1 from a total of 1 transactions
Txn Hash |
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|
fd1c619f95a922d6f52fbc50038e8bec61eb90a7ef34e2142055a5d0a4d540df | 26156732 | 7 months ago | io126au4ysx3jn2xq24uzsgm4nk4ppnychlzke8e3 | IN | Contract: StakeFactory | 0 IOTX | 0.039384 |
Contract Name:
StakeFactory
Compiler Version
v0.8.19+commit.7dd6d404
// SPDX-License-Identifier: BUSL-1.1 // // Copyright (c) 2023 ElkLabs // License terms: https://github.com/elkfinance/faas/blob/main/LICENSE // // Authors: // - Seth <[email protected]> // - Baal <[email protected]> // - Elijah <[email protected]> // - Snake <[email protected]> // File: contracts/interfaces/IStakeFactory.sol // // Copyright (c) 2023 ElkLabs // License terms: https://github.com/elkfinance/faas/blob/main/LICENSE // // Authors: // - Seth <[email protected]> // - Baal <[email protected]> pragma solidity >=0.8.0; interface IStakeFactory { event ContractCreated(address _newContract); event ManagerSet(address _farmManager); event FeeSet(uint256 _newFee); event FeesRecovered(uint256 _balanceRecovered); function getStake(address _creator, address _stakingToken) external view returns (address); function allFarms(uint _index) external view returns (address); function farmManager() external view returns (address); function getCreator(address _farmAddress) external view returns (address); function fee() external view returns (uint256); function maxFee() external view returns (uint256); function createNewStake( address _stakingTokenAddress, address[] memory _rewardTokenAddresses, uint256 _rewardsDuration, uint16 _depositFeeBps, uint16[] memory _withdrawalFeesBps, uint32[] memory _withdrawalFeeSchedule ) external; function allFarmsLength() external view returns (uint); function setManager(address _managerAddress) external; function setFee(uint256 _newFee) external; function withdrawFees() external; function overrideOwnership(address _farmAddress) external; } // File: @openzeppelin/contracts/security/ReentrancyGuard.sol // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } } // File: @openzeppelin/contracts/utils/Address.sol // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } // File: @openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); } // File: @openzeppelin/contracts/token/ERC20/IERC20.sol // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); } // File: contracts/interfaces/IStaking.sol // // Copyright (c) 2023 ElkLabs // License terms: https://github.com/elkfinance/faas/blob/main/LICENSE // // Authors: // - Seth <[email protected]> // - Baal <[email protected]> pragma solidity >=0.8.0; interface IStaking { /* ========== STATE VARIABLES ========== */ function stakingToken() external returns (IERC20); function totalSupply() external returns (uint256); function balances(address _account) external returns (uint256); /* ========== MUTATIVE FUNCTIONS ========== */ function stake(uint256 _amount) external; function withdraw(uint256 _amount) external; function exit() external; function recoverERC20(address _tokenAddress, address _recipient, uint256 _amount) external; /* ========== EVENTS ========== */ // Emitted on staking event Staked(address indexed account, uint256 amount); // Emitted on withdrawal (including exit) event Withdrawn(address indexed account, uint256 amount); // Emitted on token recovery event Recovered(address indexed token, address indexed recipient, uint256 amount); } // File: contracts/interfaces/IStakingFee.sol // // Copyright (c) 2023 ElkLabs // License terms: https://github.com/elkfinance/faas/blob/main/LICENSE // // Authors: // - Seth <[email protected]> // - Baal <[email protected]> pragma solidity >=0.8.0; interface IStakingFee is IStaking { /* ========== STATE VARIABLES ========== */ function feesUnit() external returns (uint256); function maxFee() external returns (uint256); function withdrawalFeeSchedule(uint256) external returns (uint256); function withdrawalFeesBps(uint256) external returns (uint256); function depositFeeBps() external returns (uint256); function collectedFees() external returns (uint256); function userLastStakedTime(address _user) external view returns (uint32); /* ========== VIEWS ========== */ function depositFee(uint256 _depositAmount) external view returns (uint256); function withdrawalFee(address _account, uint256 _withdrawalAmount) external view returns (uint256); /* ========== MUTATIVE FUNCTIONS ========== */ function recoverFees(address _recipient) external; /* ========== EVENTS ========== */ // Emitted when fees are (re)configured event FeesSet(uint16 _depositFeeBps, uint16[] _withdrawalFeesBps, uint32[] _feeSchedule); // Emitted when a deposit fee is collected event DepositFeesCollected(address indexed _user, uint256 _amount); // Emitted when a withdrawal fee is collected event WithdrawalFeesCollected(address indexed _user, uint256 _amount); // Emitted when fees are recovered by governance event FeesRecovered(uint256 _amount); } // File: contracts/interfaces/IStakingRewards.sol // // Copyright (c) 2023 ElkLabs // License terms: https://github.com/elkfinance/faas/blob/main/LICENSE // // Authors: // - Seth <[email protected]> // - Baal <[email protected]> pragma solidity >=0.8.0; interface IStakingRewards is IStakingFee { /* ========== STATE VARIABLES ========== */ function rewardTokens(uint256) external view returns (IERC20); function rewardTokenAddresses(address _rewardAddress) external view returns (bool); function periodFinish() external view returns (uint256); function rewardsDuration() external view returns (uint256); function lastUpdateTime() external view returns (uint256); function rewardRates(address _rewardAddress) external view returns (uint256); function rewardPerTokenStored(address _rewardAddress) external view returns (uint256); // wallet address => token address => amount function userRewardPerTokenPaid(address _walletAddress, address _tokenAddress) external view returns (uint256); function rewards(address _walletAddress, address _tokenAddress) external view returns (uint256); /* ========== VIEWS ========== */ function lastTimeRewardApplicable() external view returns (uint256); function rewardPerToken(address _tokenAddress) external view returns (uint256); function earned(address _tokenAddress, address _account) external view returns (uint256); /* ========== MUTATIVE FUNCTIONS ========== */ function getReward(address _tokenAddress, address _recipient) external; function getRewards(address _recipient) external; // Must send reward before calling this! function startEmission(uint256[] memory _rewards, uint256 _duration) external; function stopEmission(address _refundAddress) external; function recoverLeftoverReward(address _tokenAddress, address _recipient) external; function addRewardToken(address _tokenAddress) external; function rewardTokenIndex(address _tokenAddress) external view returns (int8); /* ========== EVENTS ========== */ // Emitted when a reward is paid to an account event RewardPaid(address indexed _token, address indexed _account, uint256 _reward); // Emitted when a leftover reward is recovered event LeftoverRewardRecovered(address indexed _recipient, uint256 _amount); // Emitted when rewards emission is started event RewardsEmissionStarted(uint256[] _rewards, uint256 _duration); // Emitted when rewards emission ends event RewardsEmissionEnded(); } // File: @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); } // File: @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File: @openzeppelin/contracts/utils/Context.sol // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } } // File: @openzeppelin/contracts/token/ERC20/ERC20.sol // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer( address from, address to, uint256 amount ) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance( address owner, address spender, uint256 amount ) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} } // File: @openzeppelin/contracts/access/Ownable.sol // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // File: contracts/Staking.sol // // Copyright (c) 2023 ElkLabs // License terms: https://github.com/elkfinance/faas/blob/main/LICENSE // // Authors: // - Seth <[email protected]> // - Baal <[email protected]> // - Elijah <[email protected]> // - Snake <[email protected]> // - Real-Hansolo <[email protected]> pragma solidity >=0.8.0; /** * Base contract implementing simple ERC20 token staking functionality (no staking rewards). */ contract Staking is ReentrancyGuard, Ownable, IStaking { using SafeERC20 for IERC20; /* ========== STATE VARIABLES ========== */ /// @notice Staking token interface IERC20 public immutable stakingToken; /// @notice Total supply of the staking token uint256 public totalSupply; /// @notice Account balances mapping(address account => uint256 balance) public balances; // Scaling factor: supply is always stored as wei (10**18) regardless of the token's decimals uint256 private supplyScalingFactor; /* ========== CONSTRUCTOR ========== */ /// @param _stakingTokenAddress address of the token used for staking (must be ERC20) constructor(address _stakingTokenAddress) { require(_stakingTokenAddress != address(0), "E1"); stakingToken = IERC20(_stakingTokenAddress); supplyScalingFactor = 1e18 / 10 ** ERC20(_stakingTokenAddress).decimals(); } /** * @dev Stake tokens. * Note: the contract must have sufficient allowance for the staking token. * @param _amount amount to stake */ function stake(uint256 _amount) public nonReentrant { uint256 originalAmount = _amount; _amount = _beforeStake(msg.sender, _amount); require(_amount > 0 && originalAmount > 0, "E2"); // Check after the hook totalSupply += _amount * supplyScalingFactor; balances[msg.sender] += _amount; stakingToken.safeTransferFrom(msg.sender, address(this), originalAmount); emit Staked(msg.sender, _amount); } /** * @dev Withdraw previously staked tokens. * @param _amount amount to withdraw */ function withdraw(uint256 _amount) public nonReentrant { uint256 originalAmount = _amount; _amount = _beforeWithdraw(msg.sender, _amount); // Check after the hook require(_amount > 0 && _amount <= balances[msg.sender] && originalAmount <= balances[msg.sender], "E3"); totalSupply -= originalAmount * supplyScalingFactor; balances[msg.sender] -= originalAmount; stakingToken.safeTransfer(msg.sender, _amount); emit Withdrawn(msg.sender, _amount); } /** * @dev Exit the farm, i.e., withdraw the entire token balance of the calling account */ function exit() external { _beforeExit(msg.sender); withdraw(balances[msg.sender]); } /* ========== RESTRICTED FUNCTIONS ========== */ /** * @dev Recover ERC20 tokens held in the contract. * Note: privileged governance function to recover tokens mistakenly sent to this contract address. * This function cannot be used to withdraw staking tokens. * @param _tokenAddress address of the token to recover * @param _recipient recovery address * @param _amount amount to withdraw * @ return withdrawn amount (may differ from input amount due to e.g., fees) */ function recoverERC20(address _tokenAddress, address _recipient, uint256 _amount) external nonReentrant onlyOwner { require(_tokenAddress != address(stakingToken), "E4"); _beforeRecoverERC20(_tokenAddress, _recipient, _amount); IERC20 token = IERC20(_tokenAddress); token.safeTransfer(_recipient, _amount); emit Recovered(_tokenAddress, _recipient, _amount); } /* ========== HOOKS ========== */ /** * @dev Internal hook called before staking (in the stake() function). * @ param _account staker address * @param _amount amount being staken * @return amount to stake (may be changed by the hook) */ function _beforeStake(address /*_account*/, uint256 _amount) internal virtual returns (uint256) { return _amount; } /** * @dev Internal hook called before withdrawing (in the withdraw() function). * @ param _account withdrawer address * @param _amount amount being withdrawn * @return amount to withdraw (may be changed by the hook) */ function _beforeWithdraw(address /*_account*/, uint256 _amount) internal virtual returns (uint256) { return _amount; } /** * @dev Internal hook called before exiting (in the exit() function). * Note: since exit() calls withdraw() internally, the _beforeWithdraw() hook fill fire too. * @param _account address exiting */ function _beforeExit(address _account) internal virtual {} /** * @dev Internal hook called before recovering tokens (in the recoverERC20() function). * @param _tokenAddress address of the token being recovered * @param _recipient recovery address * @param _amount amount being withdrawn */ function _beforeRecoverERC20(address _tokenAddress, address _recipient, uint256 _amount) internal virtual {} } // File: contracts/StakingFee.sol // // Copyright (c) 2023 ElkLabs // License terms: https://github.com/elkfinance/faas/blob/main/LICENSE // // Authors: // - Seth <[email protected]> // - Baal <[email protected]> // - Elijah <[email protected]> // - Snake <[email protected]> pragma solidity >=0.8.0; /** * Contract implementing simple ERC20 token staking functionality and supporting deposit/withdrawal fees (no staking rewards). */ contract StakingFee is Staking, IStakingFee { using SafeERC20 for IERC20; /* ========== STATE VARIABLES ========== */ /// @notice Constant Fee Unit (1e4) uint256 public constant feesUnit = 10000; /// @notice Maximum fee (20%) uint256 public constant maxFee = 2000; /// @notice Schedule of withdrawal fees represented as a sorted array of durations /// @dev example: 10% after 1 hour, 1% after a day, 0% after a week => [3600, 86400] uint256[] public withdrawalFeeSchedule; /// @notice Withdrawal fees described in basis points (fee unit) represented as an array of the same length as withdrawalFeeSchedule /// @dev example: 10% after 1 hour, 1% after a day, 0% after a week => [1000, 100] uint256[] public withdrawalFeesBps; /// @notice Deposit (staking) fee in basis points (fee unit) uint256 public depositFeeBps; /// @notice Counter of collected fees uint256 public collectedFees; /// @notice Last staking time for each user mapping(address => uint32) public userLastStakedTime; /* ========== CONSTRUCTOR ========== */ /** * @param _stakingTokenAddress address of the token used for staking (must be ERC20) * @param _depositFeeBps deposit fee in basis points * @param _withdrawalFeesBps aligned to fee schedule * @param _withdrawalFeeSchedule assumes a sorted array */ constructor( address _stakingTokenAddress, uint16 _depositFeeBps, uint16[] memory _withdrawalFeesBps, uint32[] memory _withdrawalFeeSchedule ) Staking(_stakingTokenAddress) { _setFees(_depositFeeBps, _withdrawalFeesBps, _withdrawalFeeSchedule); } /* ========== VIEWS ========== */ /** * @dev Calculate the deposit fee for a given amount. * @param _depositAmount amount to stake * @return fee paid upon deposit */ function depositFee(uint256 _depositAmount) public view returns (uint256) { return depositFeeBps > 0 ? (_depositAmount * depositFeeBps) / feesUnit : 0; } /** * @dev Calculate the withdrawal fee for a given amount. * @param _account user wallet address * @param _withdrawalAmount amount to withdraw * @return fee paid upon withdrawal */ function withdrawalFee(address _account, uint256 _withdrawalAmount) public view returns (uint256) { uint256 userLastStakedTimestampDiff = block.timestamp - userLastStakedTime[_account]; uint256 withdrawalFeeAmount; for (uint i = 0; i < withdrawalFeeSchedule.length; ++i) { if (userLastStakedTimestampDiff < withdrawalFeeSchedule[i]) { withdrawalFeeAmount = (_withdrawalAmount * withdrawalFeesBps[i]) / feesUnit; break; } } return withdrawalFeeAmount; } /* ========== RESTRICTED FUNCTIONS ========== */ /** * @dev Recover collected fees held in the contract. * Note: privileged function for governance * @param _recipient fee recovery address */ function recoverFees(address _recipient) external onlyOwner nonReentrant { _beforeRecoverFees(_recipient); uint256 previousFees = collectedFees; collectedFees = 0; emit FeesRecovered(previousFees); stakingToken.safeTransfer(_recipient, previousFees); } /* ========== PRIVATE FUNCTIONS ========== */ /** * @dev Configure the fees for this contract. * @param _depositFeeBps deposit fee in basis points * @param _withdrawalFeesBps withdrawal fees in basis points * @param _withdrawalFeeSchedule withdrawal fees schedule */ function _setFees( uint16 _depositFeeBps, uint16[] memory _withdrawalFeesBps, uint32[] memory _withdrawalFeeSchedule ) private { require( _withdrawalFeeSchedule.length == _withdrawalFeesBps.length && _withdrawalFeeSchedule.length <= 10 && _depositFeeBps <= maxFee, "E5" ); uint32 lastFeeSchedule = 0; uint256 lastWithdrawalFee = maxFee + 1; for (uint i = 0; i < _withdrawalFeeSchedule.length; ++i) { require(_withdrawalFeeSchedule[i] > lastFeeSchedule, "E7"); require(_withdrawalFeesBps[i] < lastWithdrawalFee, "E8"); lastFeeSchedule = _withdrawalFeeSchedule[i]; lastWithdrawalFee = _withdrawalFeesBps[i]; } withdrawalFeeSchedule = _withdrawalFeeSchedule; withdrawalFeesBps = _withdrawalFeesBps; depositFeeBps = _depositFeeBps; emit FeesSet(_depositFeeBps, _withdrawalFeesBps, _withdrawalFeeSchedule); } /* ========== HOOKS ========== */ /** * @dev Override _beforeStake() hook to collect the deposit fee and update associated state */ function _beforeStake(address _account, uint256 _amount) internal virtual override returns (uint256) { uint256 fee = depositFee(_amount); userLastStakedTime[_account] = uint32(block.timestamp); if (fee > 0) { collectedFees += fee; emit DepositFeesCollected(_account, fee); } return super._beforeStake(_account, _amount - fee); } /** * @dev Override _beforeWithdrawl() hook to collect the withdrawal fee and update associated state */ function _beforeWithdraw(address _account, uint256 _amount) internal virtual override returns (uint256) { uint256 fee = withdrawalFee(_account, _amount); if (fee > 0) { collectedFees += fee; emit WithdrawalFeesCollected(_account, fee); } return super._beforeWithdraw(_account, _amount - fee); } /** * @dev Internal hook called before recovering fees (in the recoverFees() function). * @param _recipient recovery address */ function _beforeRecoverFees(address _recipient) internal virtual {} } // File: contracts/StakingRewards.sol // // Copyright (c) 2023 ElkLabs // License terms: https://github.com/elkfinance/faas/blob/main/LICENSE // // Authors: // - Seth <[email protected]> // - Baal <[email protected]> // - Elijah <[email protected]> // - Snake <[email protected]> // - Real-Hansolo <[email protected]> pragma solidity >=0.8.0; /** * Contract implementing simple ERC20 token staking functionality with staking rewards and deposit/withdrawal fees. */ contract StakingRewards is StakingFee, IStakingRewards { using SafeERC20 for IERC20; /* ========== STATE VARIABLES ========== */ /// @notice List of reward token interfaces IERC20[] public rewardTokens; /// @notice Reward token addresses (maps every reward token address to true, others to false) mapping(address tokenAddress => bool isRewardToken) public rewardTokenAddresses; /// @notice Timestamp when rewards stop emitting uint256 public periodFinish; /// @notice Duration for reward emission uint256 public rewardsDuration; /// @notice Last time the rewards were updated uint256 public lastUpdateTime; /// @notice Reward token rates (maps every reward token to an emission rate, //i.e., how many tokens emitted per second) mapping(address token => uint256 emissionRate) public rewardRates; /// @notice How many tokens are emitted per staked token mapping(address token => uint256 emissionRate) public rewardPerTokenStored; /// @notice How many reward tokens were paid per user (token address => wallet address => amount) mapping(address token => mapping(address walletAddress => uint256 amount)) public userRewardPerTokenPaid; /// @notice Accumulator of reward tokens per user (token address => wallet address => amount) mapping(address token => mapping(address walletAddress => uint256 amount)) public rewards; /* ========== CONSTRUCTOR ========== */ /** * @param _stakingTokenAddress address of the token used for staking (must be ERC20) * @param _rewardTokenAddresses addresses the reward tokens (must be ERC20) * @param _rewardsDuration reward emission duration * @param _depositFeeBps deposit fee in basis points * @param _withdrawalFeesBps aligned to fee schedule * @param _withdrawalFeeSchedule assumes a sorted array */ constructor( address _stakingTokenAddress, address[] memory _rewardTokenAddresses, uint256 _rewardsDuration, uint16 _depositFeeBps, uint16[] memory _withdrawalFeesBps, uint32[] memory _withdrawalFeeSchedule ) StakingFee(_stakingTokenAddress, _depositFeeBps, _withdrawalFeesBps, _withdrawalFeeSchedule) { require(_rewardTokenAddresses.length > 0, "E9"); // update reward data structures for (uint i = 0; i < _rewardTokenAddresses.length; ++i) { address tokenAddress = _rewardTokenAddresses[i]; _addRewardToken(tokenAddress); } rewardsDuration = _rewardsDuration; } /* ========== VIEWS ========== */ /** * @notice Return the last time rewards are applicable (the lowest of the current timestamp and the rewards expiry timestamp). * @return timestamp */ function lastTimeRewardApplicable() public view returns (uint256) { return block.timestamp < periodFinish ? block.timestamp : periodFinish; } /** * @notice Return the reward per staked token for a given reward token address. * @param _tokenAddress reward token address * @return amount of reward per staked token */ function rewardPerToken(address _tokenAddress) public view returns (uint256) { if (totalSupply == 0) { return rewardPerTokenStored[_tokenAddress]; } return rewardPerTokenStored[_tokenAddress] + ((lastTimeRewardApplicable() - lastUpdateTime) * rewardRates[_tokenAddress] * 10 ** ERC20(_tokenAddress).decimals()) / totalSupply; } /** * @notice Return the total reward earned by a user for a given reward token address. * @param _tokenAddress reward token address * @param _account user wallet address * @return amount earned */ function earned(address _tokenAddress, address _account) public view returns (uint256) { return (balances[_account] * (rewardPerToken(_tokenAddress) - userRewardPerTokenPaid[_tokenAddress][_account])) / 10 ** ERC20(_tokenAddress).decimals() + rewards[_tokenAddress][_account]; } /* ========== MUTATIVE FUNCTIONS ========== */ /** * @dev claim the specified token reward for a staker * @param _tokenAddress the address of the reward token * @param _recipient the address of the staker that should receive the reward * @ return amount of reward received */ function getReward(address _tokenAddress, address _recipient) public nonReentrant updateRewards(_recipient) { return _getReward(_tokenAddress, _recipient); } /** * @dev claim rewards for all the reward tokens for the staker * @param _recipient address of the recipient to receive the rewards */ function getRewards(address _recipient) public nonReentrant updateRewards(_recipient) { for (uint i = 0; i < rewardTokens.length; ++i) { _getReward(address(rewardTokens[i]), _recipient); } } /** * @dev Start the emission of rewards to stakers. The owner must send reward tokens to the contract before calling this function. * Note: Can only be called by owner when the contract is not emitting rewards. * @param _rewards array of rewards amounts for each reward token * @param _duration duration in seconds for which rewards will be emitted */ function startEmission( uint256[] memory _rewards, uint256 _duration ) public virtual nonReentrant onlyOwner whenNotEmitting updateRewards(address(0)) { require(_duration > 0, "E10"); require(_rewards.length == rewardTokens.length, "E11"); _beforeStartEmission(_rewards, _duration); rewardsDuration = _duration; for (uint i = 0; i < rewardTokens.length; ++i) { IERC20 token = rewardTokens[i]; address tokenAddress = address(token); rewardRates[tokenAddress] = _rewards[i] / rewardsDuration; // Ensure the provided reward amount is not more than the balance in the contract. // This keeps the reward rate in the right range, preventing overflows due to // very high values of rewardRate in the earned and rewardsPerToken functions; // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. uint256 balance = rewardTokens[i].balanceOf(address(this)); if (tokenAddress != address(stakingToken)) { require(rewardRates[tokenAddress] <= balance / rewardsDuration, "E3"); } else { // Handle carefully where rewardsToken is the same as stakingToken (need to subtract total supply) require(rewardRates[tokenAddress] <= (balance - totalSupply) / rewardsDuration, "E3"); } } lastUpdateTime = block.timestamp; periodFinish = block.timestamp + rewardsDuration; emit RewardsEmissionStarted(_rewards, _duration); } /** * @dev stop the reward emission process and transfer the remaining reward tokens to a specified address * Note: can only be called by owner when the contract is currently emitting rewards * @param _refundAddress the address to receive the remaining reward tokens */ function stopEmission(address _refundAddress) external nonReentrant onlyOwner whenEmitting { _beforeStopEmission(_refundAddress); uint256 remaining = 0; if (periodFinish > block.timestamp) { remaining = periodFinish - block.timestamp; } periodFinish = block.timestamp; for (uint i = 0; i < rewardTokens.length; ++i) { IERC20 token = rewardTokens[i]; address tokenAddress = address(token); uint256 refund = rewardRates[tokenAddress] * remaining; if (refund > 0) { token.safeTransfer(_refundAddress, refund); } } emit RewardsEmissionEnded(); } /** * @dev recover leftover reward tokens and transfer them to a specified recipient * Note: can only be called by owner when the contract is not emitting rewards * @param _tokenAddress address of the reward token to be recovered * @param _recipient address to receive the recovered reward tokens */ function recoverLeftoverReward(address _tokenAddress, address _recipient) external onlyOwner whenNotEmitting { require(totalSupply == 0, "E12"); if (rewardTokenAddresses[_tokenAddress]) { _beforeRecoverLeftoverReward(_tokenAddress, _recipient); IERC20 token = IERC20(_tokenAddress); uint256 amount = token.balanceOf(address(this)); if (amount > 0) { token.safeTransfer(_recipient, amount); } emit LeftoverRewardRecovered(_recipient, amount); } } /** * @dev add a reward token to the contract * Note: can only be called by owner when the contract is not emitting rewards * @param _tokenAddress address of the new reward token */ function addRewardToken(address _tokenAddress) external onlyOwner whenNotEmitting { _addRewardToken(_tokenAddress); } /** * @dev Return the array index of the provided token address (if applicable) * @param _tokenAddress address of the LP token * @return the array index for _tokenAddress or -1 if it is not a reward token */ function rewardTokenIndex(address _tokenAddress) public view returns (int8) { if (rewardTokenAddresses[_tokenAddress]) { for (uint i = 0; i < rewardTokens.length; ++i) { if (address(rewardTokens[i]) == _tokenAddress) { return int8(int256(i)); } } } return -1; } /* ========== PRIVATE FUNCTIONS ========== */ /** * @dev Get the reward amount of a token for a specific recipient * @param _tokenAddress address of the token * @param _recipient address of the recipient */ function _getReward(address _tokenAddress, address _recipient) private { require(msg.sender == owner() || msg.sender == _recipient, "E14"); require(rewardTokenAddresses[_tokenAddress], "E13"); uint256 reward = rewards[_tokenAddress][_recipient]; if (reward > 0) { rewards[_tokenAddress][_recipient] = 0; IERC20(_tokenAddress).safeTransfer(_recipient, reward); emit RewardPaid(_tokenAddress, _recipient, reward); } } /** * @dev Add a token as a reward token * @param _tokenAddress address of the token to be added as a reward token */ function _addRewardToken(address _tokenAddress) private { require(rewardTokens.length <= 15, "E15"); require(_tokenAddress != address(0), "E1"); if (!rewardTokenAddresses[_tokenAddress]) { rewardTokens.push(IERC20(_tokenAddress)); rewardTokenAddresses[_tokenAddress] = true; } } /* ========== HOOKS ========== */ /** * @dev Override _beforeStake() hook to ensure staking is only possible when rewards are emitting and update the rewards */ function _beforeStake( address _account, uint256 _amount ) internal virtual override whenEmitting updateRewards(_account) returns (uint256) { return super._beforeStake(_account, _amount); } /** * @dev Override _beforeExit() hook to claim all rewards for the account exiting */ function _beforeExit(address _account) internal virtual override { // getRewards calls updateRewards so we don't need to call it explicitly again here getRewards(_account); super._beforeExit(_account); } /** * @dev Override _beforeRecoverERC20() hook to prevent recovery of a reward token */ function _beforeRecoverERC20(address _tokenAddress, address _recipient, uint256 _amount) internal virtual override { require(!rewardTokenAddresses[_tokenAddress], "E16"); super._beforeRecoverERC20(_tokenAddress, _recipient, _amount); } /** * @dev Internal hook called before starting the emission process (in the startEmission() function). * @param _rewards array of rewards per token. * @param _duration emission duration. */ function _beforeStartEmission(uint256[] memory _rewards, uint256 _duration) internal virtual {} /** * @dev Internal hook called before stopping the emission process (in the stopEmission() function). * @param _refundAddress address to refund the remaining reward to */ function _beforeStopEmission(address _refundAddress) internal virtual {} /** * @dev Internal hook called before recovering leftover rewards (in the recoverLeftoverRewards() function). * @param _tokenAddress address of the token to recover * @param _recipient address to recover the leftover rewards to */ function _beforeRecoverLeftoverReward(address _tokenAddress, address _recipient) internal virtual {} /* ========== MODIFIERS ========== */ /** * @dev Modifier to update rewards of a given account. * @param _account account to update rewards for */ modifier updateRewards(address _account) { for (uint i = 0; i < rewardTokens.length; ++i) { address tokenAddress = address(rewardTokens[i]); rewardPerTokenStored[tokenAddress] = rewardPerToken(tokenAddress); } lastUpdateTime = lastTimeRewardApplicable(); if (_account != address(0)) { for (uint i = 0; i < rewardTokens.length; ++i) { address tokenAddress = address(rewardTokens[i]); rewards[tokenAddress][_account] = earned(tokenAddress, _account); userRewardPerTokenPaid[tokenAddress][_account] = rewardPerTokenStored[tokenAddress]; } } _; } /** * @dev Modifier to check if rewards are emitting. */ modifier whenEmitting() { require(block.timestamp <= periodFinish, "E18"); _; } /** * @dev Modifier to check if rewards are not emitting. */ modifier whenNotEmitting() { require(block.timestamp > periodFinish, "E17"); _; } } // File: contracts/StakeFactory.sol // // Copyright (c) 2023 ElkLabs // License terms: https://github.com/elkfinance/faas/blob/main/LICENSE // // Authors: // - Seth <[email protected]> // - Baal <[email protected]> // - Elijah <[email protected]> // - Snake <[email protected]> // - Real-Hansolo <[email protected]> pragma solidity >=0.8.0; /** * Contract that is used by users to create StakingRewards contracts. * It stores each farm as it's created, as well as the current owner of each farm. * It also contains various uitlity functions for use by Elk. */ contract StakeFactory is IStakeFactory, Ownable { using SafeERC20 for IERC20; /* ========== STATE VARIABLES ========== */ /// @notice The address of the StakingRewards contract for each farm. mapping(address creator => mapping(address stakingTokenAddress => address farmAddress)) public override getStake; /// @notice The address of each farm for each creator. address[] public override allFarms; /// @notice The address of the farm manager. address public override farmManager; /// @notice The address of the creator of a given farm. mapping(address farmAddress => address creator) public override getCreator; /// @notice The address of the ElkToken contract. IERC20 public feeToken = IERC20(0xeEeEEb57642040bE42185f49C52F7E9B38f8eeeE); /// @notice fee amount for creating a farm; uint256 public fee = 1000 ether; /// @notice max allowed fee in ElkToken uint256 public maxFee = 1000000 ether; /* ========== CONSTRUCTOR ========== */ constructor() {} /** * @notice Creates a new StakingRewards contract, stores the farm address by creator and the given LP token. * @notice stores the creator of the contract by the new farm address. This is where the fee is taken from the user. * @param _stakingTokenAddress The address of the LP token to be staked. * @param _rewardTokenAddresses The addresses of the reward tokens to be distributed. * @param _rewardsDuration The duration of the rewards period. * @param _depositFeeBps The deposit fee in basis points. * @param _withdrawalFeesBps The withdrawal fee in basis points. * @param _withdrawalFeeSchedule The schedule for the withdrawal fee. */ function createNewStake( address _stakingTokenAddress, address[] memory _rewardTokenAddresses, uint256 _rewardsDuration, uint16 _depositFeeBps, uint16[] memory _withdrawalFeesBps, uint32[] memory _withdrawalFeeSchedule ) public override { // single check is sufficient require(getStake[msg.sender][_stakingTokenAddress] == address(0), "StakeFactory: FARM_EXISTS"); bytes memory creationCode = type(StakingRewards).creationCode; bytes memory bytecode = abi.encodePacked( creationCode, abi.encode( _stakingTokenAddress, _rewardTokenAddresses, _rewardsDuration, _depositFeeBps, _withdrawalFeesBps, _withdrawalFeeSchedule ) ); address addr; bytes32 salt = keccak256(abi.encodePacked(_stakingTokenAddress, msg.sender)); assembly { addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt) if iszero(extcodesize(addr)) { revert(0, 0) } } getStake[msg.sender][_stakingTokenAddress] = addr; getCreator[addr] = msg.sender; allFarms.push(addr); StakingRewards(addr).transferOwnership(farmManager); _takeFee(); emit ContractCreated(addr); } /** * @return the number of singe staking contracts created */ function allFarmsLength() external view override returns (uint) { return allFarms.length; } /** * @notice Utility function to be used by Elk. Changes which manager contract will be assigned ownership of each farm on creation. * @notice This is available in case any updates are made to the StakeManager contract. * @dev Ownership is not changed retroactively, so any created farms will always have the same manager contract. * @param _managerAddress The address of the new manager contract. */ function setManager(address _managerAddress) external override onlyOwner { require(_managerAddress != address(0), "StakeFactory: ZERO_ADDRESS"); farmManager = _managerAddress; emit ManagerSet(_managerAddress); } /** * @notice Takes fee for contract creation. * @dev StakeFactory must be approved to spend the feeToken before creating a new farm. */ function _takeFee() private { require(feeToken.balanceOf(msg.sender) >= fee, "StakeFactory: INSUFFICIENT_BALANCE"); feeToken.safeTransferFrom(msg.sender, address(this), fee); } /** * @notice Utility function used by Elk to change the fee amount charged on contract creation. * @dev Can never be more than the maxFee set stored in the contract. * @param _newFee The new fee amount. */ function setFee(uint256 _newFee) external onlyOwner { require(_newFee < maxFee, "StakeFactory: FEE_TOO_HIGH"); fee = _newFee; emit FeeSet(_newFee); } /** * @notice Utility function used by Elk to recover the fees gathered by the factory. */ function withdrawFees() external onlyOwner { uint256 balance = feeToken.balanceOf(address(this)); feeToken.safeTransfer(msg.sender, balance); emit FeesRecovered(balance); } /** * @notice Override ownership of a farm, only used by Elk. * @param _farmAddress The address of the farm to be changed. */ function overrideOwnership(address _farmAddress) external onlyOwner { _transferFarmOwnership(_farmAddress, msg.sender); } /** * @notice Transfer ownership of a farm (only possible from current owner). * @param _farmAddress The address of the farm to be changed. * @param _newOwner The address of the new farm owner. */ function transferFarmOwnership(address _farmAddress, address _newOwner) external { require(msg.sender == getCreator[_farmAddress], "StakeFactory: NOT_OWNER"); _transferFarmOwnership(_farmAddress, _newOwner); } /** * @notice Private function that performs a transfer of ownership for a farm. */ function _transferFarmOwnership(address _farmAddress, address _newOwner) private { address creatorAddress = getCreator[_farmAddress]; require(creatorAddress != address(0), "StakeFactory: UNKNOWN_FARM"); StakingRewards rewardsContract = StakingRewards(_farmAddress); address stakingToken = address(rewardsContract.stakingToken()); getStake[creatorAddress][stakingToken] = address(0); getStake[_newOwner][stakingToken] = _farmAddress; getCreator[_farmAddress] = _newOwner; } }
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_newContract","type":"address"}],"name":"ContractCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_newFee","type":"uint256"}],"name":"FeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_balanceRecovered","type":"uint256"}],"name":"FeesRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_farmManager","type":"address"}],"name":"ManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allFarms","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allFarmsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_stakingTokenAddress","type":"address"},{"internalType":"address[]","name":"_rewardTokenAddresses","type":"address[]"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"},{"internalType":"uint16","name":"_depositFeeBps","type":"uint16"},{"internalType":"uint16[]","name":"_withdrawalFeesBps","type":"uint16[]"},{"internalType":"uint32[]","name":"_withdrawalFeeSchedule","type":"uint32[]"}],"name":"createNewStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"farmManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"farmAddress","type":"address"}],"name":"getCreator","outputs":[{"internalType":"address","name":"creator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"stakingTokenAddress","type":"address"}],"name":"getStake","outputs":[{"internalType":"address","name":"farmAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_farmAddress","type":"address"}],"name":"overrideOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newFee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_managerAddress","type":"address"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_farmAddress","type":"address"},{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferFarmOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b5060043610620001325760003560e01c8063715018a611620000c0578063a0210309116200008b578063ddca3f43116200006e578063ddca3f43146200029c578063e654740914620002a6578063f2fde38b14620002bd57600080fd5b8063a02103091462000259578063d0ebdbe7146200028557600080fd5b8063715018a614620001fd5780637bc6d443146200020757806382dda22d14620002105780638da5cb5b146200024757600080fd5b8063476343ee1162000101578063476343ee14620001b45780634b2d07cf14620001be578063647846a514620001d257806369fe0e2d14620001e657600080fd5b806301f59d1614620001375780632092578714620001545780633ae21bb1146200016d57806343acb9a91462000184575b600080fd5b6200014160075481565b6040519081526020015b60405180910390f35b6200016b6200016536600462001072565b620002d4565b005b6200016b6200017e366004620010b0565b62000353565b6200019b62000195366004620010d7565b6200036c565b6040516001600160a01b0390911681526020016200014b565b6200016b62000397565b6003546200019b906001600160a01b031681565b6005546200019b906001600160a01b031681565b6200016b620001f7366004620010d7565b62000464565b6200016b620004f7565b60025462000141565b6200019b6200022136600462001072565b60016020908152600092835260408084209091529082529020546001600160a01b031681565b6000546001600160a01b03166200019b565b6200019b6200026a366004620010b0565b6004602052600090815260409020546001600160a01b031681565b6200016b62000296366004620010b0565b6200050f565b6200014160065481565b6200016b620002b736600462001285565b620005cd565b6200016b620002ce366004620010b0565b620008f1565b6001600160a01b03828116600090815260046020526040902054163314620003435760405162461bcd60e51b815260206004820152601760248201527f5374616b65466163746f72793a204e4f545f4f574e455200000000000000000060448201526064015b60405180910390fd5b6200034f828262000984565b5050565b6200035d62000add565b62000369813362000984565b50565b600281815481106200037d57600080fd5b6000918252602090912001546001600160a01b0316905081565b620003a162000add565b6005546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015620003eb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004119190620013ae565b6005549091506200042d906001600160a01b0316338362000b39565b6040518181527f6857c770f3cb43e9c19050a37dd914ec876241c1f4b487d26a1d4f5d3054f49b906020015b60405180910390a150565b6200046e62000add565b6007548110620004c15760405162461bcd60e51b815260206004820152601a60248201527f5374616b65466163746f72793a204645455f544f4f5f4849474800000000000060448201526064016200033a565b60068190556040518181527f20461e09b8e557b77e107939f9ce6544698123aad0fc964ac5cc59b7df2e608f9060200162000459565b6200050162000add565b6200050d600062000be9565b565b6200051962000add565b6001600160a01b038116620005715760405162461bcd60e51b815260206004820152601a60248201527f5374616b65466163746f72793a205a45524f5f4144445245535300000000000060448201526064016200033a565b6003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527f60a0f5b9f9e81e98216071b85826681c796256fe3d1354ecb675580fba64fa699060200162000459565b3360009081526001602090815260408083206001600160a01b038a811685529252909120541615620006425760405162461bcd60e51b815260206004820152601960248201527f5374616b65466163746f72793a204641524d5f4558495354530000000000000060448201526064016200033a565b60006040518060200162000656906200104e565b6020820181038252601f19601f8201166040525090506000818888888888886040516020016200068c9695949392919062001441565b60408051601f1981840301815290829052620006ac929160200162001509565b60408051601f19818403018152908290526bffffffffffffffffffffffff1960608b811b8216602085015233901b16603483015291506000908190604801604051602081830303815290604052805190602001209050808351602085016000f59150813b6200071a57600080fd5b8160016000336001600160a01b03166001600160a01b0316815260200190815260200160002060008c6001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055503360046000846001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055506002829080600181540180825580915050600190039060005260206000200160009091909190916101000a8154816001600160a01b0302191690836001600160a01b03160217905550816001600160a01b031663f2fde38b600360009054906101000a90046001600160a01b03166040518263ffffffff1660e01b81526004016200086b91906001600160a01b0391909116815260200190565b600060405180830381600087803b1580156200088657600080fd5b505af11580156200089b573d6000803e3d6000fd5b50505050620008a962000c46565b6040516001600160a01b03831681527fcf78cf0d6f3d8371e1075c69c492ab4ec5d8cf23a1a239b6a51a1d00be7ca3129060200160405180910390a150505050505050505050565b620008fb62000add565b6001600160a01b038116620009795760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016200033a565b620003698162000be9565b6001600160a01b038083166000908152600460205260409020541680620009ee5760405162461bcd60e51b815260206004820152601a60248201527f5374616b65466163746f72793a20554e4b4e4f574e5f4641524d00000000000060448201526064016200033a565b60008390506000816001600160a01b03166372f702f36040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000a34573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000a5a91906200153c565b6001600160a01b039384166000908152600160208181526040808420948816808552948252808420805473ffffffffffffffffffffffffffffffffffffffff199081169091559888168085529282528084209484529381528383208054999097169888168917909655968152600490945290922080549093169093179091555050565b6000546001600160a01b031633146200050d5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016200033a565b6040516001600160a01b03831660248201526044810182905262000be49084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915262000d4f565b505050565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6006546005546040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801562000c92573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000cb89190620013ae565b101562000d2e5760405162461bcd60e51b815260206004820152602260248201527f5374616b65466163746f72793a20494e53554646494349454e545f42414c414e60448201527f434500000000000000000000000000000000000000000000000000000000000060648201526084016200033a565b6006546005546200050d916001600160a01b03909116903390309062000e3b565b600062000da6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031662000e949092919063ffffffff16565b80519091501562000be4578080602001905181019062000dc791906200155c565b62000be45760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016200033a565b6040516001600160a01b038085166024830152831660448201526064810182905262000e8e9085907f23b872dd000000000000000000000000000000000000000000000000000000009060840162000b7f565b50505050565b606062000ea5848460008562000ead565b949350505050565b60608247101562000f275760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016200033a565b600080866001600160a01b0316858760405162000f45919062001580565b60006040518083038185875af1925050503d806000811462000f84576040519150601f19603f3d011682016040523d82523d6000602084013e62000f89565b606091505b509150915062000f9c8783838762000fa7565b979650505050505050565b606083156200101b57825160000362001013576001600160a01b0385163b620010135760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200033a565b508162000ea5565b62000ea58383815115620010325781518083602001fd5b8060405162461bcd60e51b81526004016200033a91906200159e565b6132c080620015d483390190565b6001600160a01b03811681146200036957600080fd5b600080604083850312156200108657600080fd5b823562001093816200105c565b91506020830135620010a5816200105c565b809150509250929050565b600060208284031215620010c357600080fd5b8135620010d0816200105c565b9392505050565b600060208284031215620010ea57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156200114c576200114c620010f1565b604052919050565b600067ffffffffffffffff821115620011715762001171620010f1565b5060051b60200190565b803561ffff811681146200118e57600080fd5b919050565b600082601f830112620011a557600080fd5b81356020620011be620011b88362001154565b62001120565b82815260059290921b84018101918181019086841115620011de57600080fd5b8286015b848110156200120457620011f6816200117b565b8352918301918301620011e2565b509695505050505050565b600082601f8301126200122157600080fd5b8135602062001234620011b88362001154565b82815260059290921b840181019181810190868411156200125457600080fd5b8286015b848110156200120457803563ffffffff81168114620012775760008081fd5b835291830191830162001258565b60008060008060008060c087890312156200129f57600080fd5b8635620012ac816200105c565b955060208781013567ffffffffffffffff80821115620012cb57600080fd5b818a0191508a601f830112620012e057600080fd5b8135620012f1620011b88262001154565b81815260059190911b8301840190848101908d8311156200131157600080fd5b938501935b828510156200133c5784356200132c816200105c565b8252938501939085019062001316565b9950505060408a013596506200135560608b016200117b565b955060808a01359250808311156200136c57600080fd5b6200137a8b848c0162001193565b945060a08a01359250808311156200139157600080fd5b5050620013a189828a016200120f565b9150509295509295509295565b600060208284031215620013c157600080fd5b5051919050565b600081518084526020808501945080840160005b83811015620013fe57815161ffff1687529582019590820190600101620013dc565b509495945050505050565b600081518084526020808501945080840160005b83811015620013fe57815163ffffffff16875295820195908201906001016200141d565b600060c082016001600160a01b03808a168452602060c081860152828a5180855260e087019150828c01945060005b818110156200149057855185168352948301949183019160010162001470565b5050896040870152620014a9606087018a61ffff169052565b8581036080870152620014bd8189620013c8565b935050505082810360a0840152620014d6818562001409565b9998505050505050505050565b60005b8381101562001500578181015183820152602001620014e6565b50506000910152565b600083516200151d818460208801620014e3565b83519083019062001533818360208801620014e3565b01949350505050565b6000602082840312156200154f57600080fd5b8151620010d0816200105c565b6000602082840312156200156f57600080fd5b81518015158114620010d057600080fd5b6000825162001594818460208701620014e3565b9190910192915050565b6020815260008251806020840152620015bf816040850160208701620014e3565b601f01601f1916919091016040019291505056fe60a06040523480156200001157600080fd5b50604051620032c0380380620032c0833981016040819052620000349162000764565b600160005585838383836200004933620001cf565b6001600160a01b0381166200008a5760405162461bcd60e51b8152602060048201526002602482015261453160f01b60448201526064015b60405180910390fd5b6001600160a01b03811660808190526040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa158015620000d5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000fb91906200088c565b6200010890600a620009cd565b6200011c90670de0b6b3a7640000620009de565b600455506200012d83838362000221565b505050506000855111620001695760405162461bcd60e51b8152602060048201526002602482015261453960f01b604482015260640162000081565b60005b8551811015620001bc5760008682815181106200018d576200018d62000a01565b60200260200101519050620001a8816200042060201b60201c565b50620001b48162000a17565b90506200016c565b505050600d919091555062000ae1915050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b81518151148015620002355750600a815111155b80156200024857506107d08361ffff1611155b6200027b5760405162461bcd60e51b8152602060048201526002602482015261453560f01b604482015260640162000081565b6000806200028d6107d0600162000a33565b905060005b8351811015620003a6578263ffffffff16848281518110620002b857620002b862000a01565b602002602001015163ffffffff1611620002fa5760405162461bcd60e51b8152602060048201526002602482015261453760f01b604482015260640162000081565b8185828151811062000310576200031062000a01565b602002602001015161ffff1610620003505760405162461bcd60e51b815260206004820152600260248201526108a760f31b604482015260640162000081565b83818151811062000365576200036562000a01565b6020026020010151925084818151811062000384576200038462000a01565b602002602001015161ffff169150806200039e9062000a17565b905062000292565b508251620003bc90600590602086019062000520565b508351620003d290600690602087019062000578565b5061ffff85166007556040517fab4c36b25b04e6f8ac9915203aba1048c17841782324a583e98128b8c1c964f390620004119087908790879062000a49565b60405180910390a15050505050565b600a54600f10156200045b5760405162461bcd60e51b815260206004820152600360248201526245313560e81b604482015260640162000081565b6001600160a01b038116620004985760405162461bcd60e51b8152602060048201526002602482015261453160f01b604482015260640162000081565b6001600160a01b0381166000908152600b602052604090205460ff166200051d57600a805460018082019092557fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80180546001600160a01b0319166001600160a01b0384169081179091556000908152600b60205260409020805460ff191690911790555b50565b82805482825590600052602060002090810192821562000566579160200282015b8281111562000566578251829063ffffffff1690559160200191906001019062000541565b5062000574929150620005bc565b5090565b82805482825590600052602060002090810192821562000566579160200282015b8281111562000566578251829061ffff1690559160200191906001019062000599565b5b80821115620005745760008155600101620005bd565b80516001600160a01b0381168114620005eb57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715620006315762000631620005f0565b604052919050565b60006001600160401b03821115620006555762000655620005f0565b5060051b60200190565b805161ffff81168114620005eb57600080fd5b600082601f8301126200068457600080fd5b815160206200069d620006978362000639565b62000606565b82815260059290921b84018101918181019086841115620006bd57600080fd5b8286015b84811015620006e357620006d5816200065f565b8352918301918301620006c1565b509695505050505050565b600082601f8301126200070057600080fd5b8151602062000713620006978362000639565b82815260059290921b840181019181810190868411156200073357600080fd5b8286015b84811015620006e357805163ffffffff81168114620007565760008081fd5b835291830191830162000737565b60008060008060008060c087890312156200077e57600080fd5b6200078987620005d3565b602088810151919750906001600160401b0380821115620007a957600080fd5b818a0191508a601f830112620007be57600080fd5b8151620007cf620006978262000639565b81815260059190911b8301840190848101908d831115620007ef57600080fd5b938501935b8285101562000818576200080885620005d3565b82529385019390850190620007f4565b809a5050505060408a015196506200083360608b016200065f565b955060808a01519250808311156200084a57600080fd5b620008588b848c0162000672565b945060a08a01519250808311156200086f57600080fd5b50506200087f89828a01620006ee565b9150509295509295509295565b6000602082840312156200089f57600080fd5b815160ff81168114620008b157600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b600181815b808511156200090f578160001904821115620008f357620008f3620008b8565b808516156200090157918102915b93841c9390800290620008d3565b509250929050565b6000826200092857506001620009c7565b816200093757506000620009c7565b81600181146200095057600281146200095b576200097b565b6001915050620009c7565b60ff8411156200096f576200096f620008b8565b50506001821b620009c7565b5060208310610133831016604e8410600b8410161715620009a0575081810a620009c7565b620009ac8383620008ce565b8060001904821115620009c357620009c3620008b8565b0290505b92915050565b6000620008b160ff84168362000917565b600082620009fc57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b60006001820162000a2c5762000a2c620008b8565b5060010190565b80820180821115620009c757620009c7620008b8565b60006060820161ffff80871684526020606081860152828751808552608087019150828901945060005b8181101562000a9357855185168352948301949183019160010162000a73565b5050858103604087015286518082529082019350915080860160005b8381101562000ad357815163ffffffff168552938201939082019060010162000aaf565b509298975050505050505050565b6080516127a062000b206000396000818161045e015281816106290152818161096e01528181610cf701528181611489015261159101526127a06000f3fe608060405234801561001057600080fd5b50600436106102ad5760003560e01c806379ee54f71161017b578063a694fc3a116100d8578063e9fad8ee1161008c578063f122977711610071578063f1229777146105de578063f2d17639146105f1578063f2fde38b1461060457600080fd5b8063e9fad8ee146105cd578063ebe2b12b146105d557600080fd5b8063ab879827116100bd578063ab87982714610590578063c8f33c9114610599578063e70b9e27146105a257600080fd5b8063a694fc3a14610542578063a7309d7d1461055557600080fd5b806387e7ed3a1161012f5780639003adfe116101145780639003adfe146105065780639ce43f901461050f5780639e3582c81461052f57600080fd5b806387e7ed3a146104e25780638da5cb5b146104f557600080fd5b80637bb7bed1116101605780637bb7bed1146104be5780637beb3d9f146104d157806380faa57d146104da57600080fd5b806379ee54f7146104985780637aaeaf7f146104ab57600080fd5b8063386a9525116102295780636b091695116101dd5780637035ab98116101c25780637035ab9814610426578063715018a61461045157806372f702f31461045957600080fd5b80636b091695146104005780636da9c58e1461041357600080fd5b8063415be3b51161020e578063415be3b5146103b4578063423c485a146103da57806354feec3e146103ed57600080fd5b8063386a95251461038b5780633d3b26031461039457600080fd5b80631db7efd81161028057806327e235e31161026557806327e235e3146103455780632e1a7d4d146103655780632e9f06021461037857600080fd5b80631db7efd8146102ff578063211dc32d1461033257600080fd5b806301f59d16146102b25780631171bda9146102ce57806318160ddd146102e35780631c03e6cc146102ec575b600080fd5b6102bb6107d081565b6040519081526020015b60405180910390f35b6102e16102dc36600461230c565b610617565b005b6102bb60025481565b6102e16102fa366004612348565b61072a565b61032261030d366004612348565b600b6020526000908152604090205460ff1681565b60405190151581526020016102c5565b6102bb610340366004612363565b610775565b6102bb610353366004612348565b60036020526000908152604090205481565b6102e1610373366004612396565b610899565b6102e16103863660046123c5565b6109d6565b6102bb600d5481565b6102bb6103a2366004612348565b600f6020526000908152604090205481565b6103c76103c2366004612348565b610e65565b60405160009190910b81526020016102c5565b6102bb6103e8366004612396565b610eeb565b6102bb6103fb366004612396565b610f18565b6102e161040e366004612363565b610f39565b6102e1610421366004612348565b61107a565b6102bb610434366004612363565b601160209081526000928352604080842090915290825290205481565b6102e161119f565b6104807f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102c5565b6102e16104a6366004612348565b6111b3565b6102bb6104b9366004612489565b611339565b6104806104cc366004612396565b6113ef565b6102bb61271081565b6102bb611419565b6102e16104f0366004612348565b611430565b6001546001600160a01b0316610480565b6102bb60085481565b6102bb61051d366004612348565b60106020526000908152604090205481565b6102bb61053d366004612396565b6114bb565b6102e1610550366004612396565b6114cb565b61057b610563366004612348565b60096020526000908152604090205463ffffffff1681565b60405163ffffffff90911681526020016102c5565b6102bb60075481565b6102bb600e5481565b6102bb6105b0366004612363565b601260209081526000928352604080842090915290825290205481565b6102e16115eb565b6102bb600c5481565b6102bb6105ec366004612348565b61160d565b6102e16105ff366004612363565b611714565b6102e1610612366004612348565b611896565b61061f611923565b61062761197c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316036106ad5760405162461bcd60e51b815260206004820152600260248201527f453400000000000000000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b6106b88383836119d6565b826106cd6001600160a01b0382168484611a3f565b826001600160a01b0316846001600160a01b03167ffff3b3844276f57024e0b42afec1a37f75db36511e43819a4f2a63ab7862b6488460405161071291815260200190565b60405180910390a3506107256001600055565b505050565b61073261197c565b600c5442116107695760405162461bcd60e51b815260206004820152600360248201526245313760e81b60448201526064016106a4565b61077281611ae8565b50565b6001600160a01b03808316600081815260126020908152604080832094861683529381528382205484517f313ce5670000000000000000000000000000000000000000000000000000000081529451929490939263313ce567926004808401939192918290030181865afa1580156107f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081591906124b3565b61082090600a6125d7565b6001600160a01b0380861660009081526011602090815260408083209388168352929052205461084f8661160d565b61085991906125e6565b6001600160a01b03851660009081526003602052604090205461087c91906125f9565b6108869190612610565b6108909190612632565b90505b92915050565b6108a1611923565b806108ac3382611c25565b91506000821180156108cd5750336000908152600360205260409020548211155b80156108e85750336000908152600360205260409020548111155b6109195760405162461bcd60e51b8152602060048201526002602482015261453360f01b60448201526064016106a4565b60045461092690826125f9565b6002600082825461093791906125e6565b9091555050336000908152600360205260408120805483929061095b9084906125e6565b9091555061099590506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163384611a3f565b60405182815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5906020015b60405180910390a2506107726001600055565b6109de611923565b6109e661197c565b600c544211610a1d5760405162461bcd60e51b815260206004820152600360248201526245313760e81b60448201526064016106a4565b6000805b600a54811015610a8a576000600a8281548110610a4057610a40612645565b6000918252602090912001546001600160a01b03169050610a608161160d565b6001600160a01b03909116600090815260106020526040902055610a838161265b565b9050610a21565b50610a93611419565b600e556001600160a01b03811615610b415760005b600a54811015610b3f576000600a8281548110610ac757610ac7612645565b6000918252602090912001546001600160a01b03169050610ae88184610775565b6001600160a01b03918216600081815260126020908152604080832095881680845295825280832094909455918152601082528281205460118352838220948252939091522055610b388161265b565b9050610aa8565b505b60008211610b915760405162461bcd60e51b815260206004820152600360248201527f453130000000000000000000000000000000000000000000000000000000000060448201526064016106a4565b600a54835114610be35760405162461bcd60e51b815260206004820152600360248201527f453131000000000000000000000000000000000000000000000000000000000060448201526064016106a4565b600d82905560005b600a54811015610e06576000600a8281548110610c0a57610c0a612645565b600091825260209091200154600d5486516001600160a01b0390921692508291879085908110610c3c57610c3c612645565b6020026020010151610c4e9190612610565b6001600160a01b0382166000908152600f6020526040812091909155600a805485908110610c7e57610c7e612645565b6000918252602090912001546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015610ccf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf39190612674565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031614610d8c57600d54610d3b9082612610565b6001600160a01b0383166000908152600f60205260409020541115610d875760405162461bcd60e51b8152602060048201526002602482015261453360f01b60448201526064016106a4565b610df2565b600d54600254610d9c90836125e6565b610da69190612610565b6001600160a01b0383166000908152600f60205260409020541115610df25760405162461bcd60e51b8152602060048201526002602482015261453360f01b60448201526064016106a4565b50505080610dff9061265b565b9050610beb565b5042600e819055600d54610e1991612632565b600c556040517faab1f55dce0d0e628e283ce1061d0afccffcf61f9c14391c899b5952492ce82190610e4e908590859061268d565b60405180910390a150610e616001600055565b5050565b6001600160a01b0381166000908152600b602052604081205460ff1615610ee25760005b600a54811015610ee057826001600160a01b0316600a8281548110610eb057610eb0612645565b6000918252602090912001546001600160a01b031603610ed05792915050565b610ed98161265b565b9050610e89565b505b50600019919050565b60008060075411610efd576000610893565b61271060075483610f0e91906125f9565b6108939190612610565b60058181548110610f2857600080fd5b600091825260209091200154905081565b610f41611923565b8060005b600a54811015610fae576000600a8281548110610f6457610f64612645565b6000918252602090912001546001600160a01b03169050610f848161160d565b6001600160a01b03909116600090815260106020526040902055610fa78161265b565b9050610f45565b50610fb7611419565b600e556001600160a01b038116156110655760005b600a54811015611063576000600a8281548110610feb57610feb612645565b6000918252602090912001546001600160a01b0316905061100c8184610775565b6001600160a01b0391821660008181526012602090815260408083209588168084529582528083209490945591815260108252828120546011835283822094825293909152205561105c8161265b565b9050610fcc565b505b61106f8383611cac565b50610e616001600055565b611082611923565b61108a61197c565b600c544211156110c25760405162461bcd60e51b815260206004820152600360248201526208a62760eb1b60448201526064016106a4565b600042600c5411156110df5742600c546110dc91906125e6565b90505b42600c5560005b600a5481101561116a576000600a828154811061110557611105612645565b60009182526020808320909101546001600160a01b0316808352600f909152604082205490925082919061113a9086906125f9565b90508015611156576111566001600160a01b0384168783611a3f565b505050806111639061265b565b90506110e6565b506040517f9bad5e1e43bc35e89725967a54f4bc384078248a1ea5c315be3b260a68cbb17a90600090a1506107726001600055565b6111a761197c565b6111b16000611e35565b565b6111bb611923565b8060005b600a54811015611228576000600a82815481106111de576111de612645565b6000918252602090912001546001600160a01b031690506111fe8161160d565b6001600160a01b039091166000908152601060205260409020556112218161265b565b90506111bf565b50611231611419565b600e556001600160a01b038116156112df5760005b600a548110156112dd576000600a828154811061126557611265612645565b6000918252602090912001546001600160a01b031690506112868184610775565b6001600160a01b039182166000818152601260209081526040808320958816808452958252808320949094559181526010825282812054601183528382209482529390915220556112d68161265b565b9050611246565b505b60005b600a5481101561132d5761131d600a828154811061130257611302612645565b6000918252602090912001546001600160a01b031684611cac565b6113268161265b565b90506112e2565b50506107726001600055565b6001600160a01b03821660009081526009602052604081205481906113649063ffffffff16426125e6565b90506000805b6005548110156113e6576005818154811061138757611387612645565b90600052602060002001548310156113d657612710600682815481106113af576113af612645565b9060005260206000200154866113c591906125f9565b6113cf9190612610565b91506113e6565b6113df8161265b565b905061136a565b50949350505050565b600a81815481106113ff57600080fd5b6000918252602090912001546001600160a01b0316905081565b6000600c54421061142b5750600c5490565b504290565b61143861197c565b611440611923565b6008805460009091556040518181527f6857c770f3cb43e9c19050a37dd914ec876241c1f4b487d26a1d4f5d3054f49b9060200160405180910390a16114b06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168383611a3f565b506107726001600055565b60068181548110610f2857600080fd5b6114d3611923565b806114de3382611e94565b91506000821180156114f05750600081115b61153c5760405162461bcd60e51b815260206004820152600260248201527f453200000000000000000000000000000000000000000000000000000000000060448201526064016106a4565b60045461154990836125f9565b6002600082825461155a9190612632565b9091555050336000908152600360205260408120805484929061157e908490612632565b909155506115b990506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333084611ffc565b60405182815233907f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d906020016109c3565b6115f433612053565b336000908152600360205260409020546111b190610899565b600060025460000361163557506001600160a01b031660009081526010602052604090205490565b600254826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611676573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169a91906124b3565b6116a590600a6125d7565b6001600160a01b0384166000908152600f6020526040902054600e546116c9611419565b6116d391906125e6565b6116dd91906125f9565b6116e791906125f9565b6116f19190612610565b6001600160a01b0383166000908152601060205260409020546108939190612632565b61171c61197c565b600c5442116117535760405162461bcd60e51b815260206004820152600360248201526245313760e81b60448201526064016106a4565b600254156117a35760405162461bcd60e51b815260206004820152600360248201527f453132000000000000000000000000000000000000000000000000000000000060448201526064016106a4565b6001600160a01b0382166000908152600b602052604090205460ff1615610e61576040516370a0823160e01b815230600482015282906000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561180d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118319190612674565b9050801561184d5761184d6001600160a01b0383168483611a3f565b826001600160a01b03167fcaa95c7b01f93ffe197f5e7316a1a2f387c5bfff8cb445095f2110ff5c1b29958260405161188891815260200190565b60405180910390a250505050565b61189e61197c565b6001600160a01b03811661191a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016106a4565b61077281611e35565b6002600054036119755760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016106a4565b6002600055565b6001546001600160a01b031633146111b15760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106a4565b6001600160a01b0383166000908152600b602052604090205460ff16156107255760405162461bcd60e51b815260206004820152600360248201527f453136000000000000000000000000000000000000000000000000000000000060448201526064016106a4565b6040516001600160a01b0383166024820152604481018290526107259084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261205c565b600a54600f1015611b3b5760405162461bcd60e51b815260206004820152600360248201527f453135000000000000000000000000000000000000000000000000000000000060448201526064016106a4565b6001600160a01b038116611b915760405162461bcd60e51b815260206004820152600260248201527f453100000000000000000000000000000000000000000000000000000000000060448201526064016106a4565b6001600160a01b0381166000908152600b602052604090205460ff1661077257600a805460018181019092557fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80180546001600160a01b03841673ffffffffffffffffffffffffffffffffffffffff1990911681179091556000908152600b60205260409020805460ff1916909117905550565b600080611c328484611339565b90508015611c91578060086000828254611c4c9190612632565b90915550506040518181526001600160a01b038516907fd0b34aaed5c558a8df736a5aaf9a49b539c4e86fb3ee5a1ac76e0bec23cbdd03906020015b60405180910390a25b611ca484611c9f83866125e6565b919050565b949350505050565b6001546001600160a01b0316331480611ccd5750336001600160a01b038216145b611d195760405162461bcd60e51b815260206004820152600360248201527f453134000000000000000000000000000000000000000000000000000000000060448201526064016106a4565b6001600160a01b0382166000908152600b602052604090205460ff16611d815760405162461bcd60e51b815260206004820152600360248201527f453133000000000000000000000000000000000000000000000000000000000060448201526064016106a4565b6001600160a01b038083166000908152601260209081526040808320938516835292905220548015610725576001600160a01b038084166000818152601260209081526040808320948716835293905291822091909155611de3908383611a3f565b816001600160a01b0316836001600160a01b03167f540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e83604051611e2891815260200190565b60405180910390a3505050565b600180546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000600c54421115611ece5760405162461bcd60e51b815260206004820152600360248201526208a62760eb1b60448201526064016106a4565b8260005b600a54811015611f3b576000600a8281548110611ef157611ef1612645565b6000918252602090912001546001600160a01b03169050611f118161160d565b6001600160a01b03909116600090815260106020526040902055611f348161265b565b9050611ed2565b50611f44611419565b600e556001600160a01b03811615611ff25760005b600a54811015611ff0576000600a8281548110611f7857611f78612645565b6000918252602090912001546001600160a01b03169050611f998184610775565b6001600160a01b03918216600081815260126020908152604080832095881680845295825280832094909455918152601082528281205460118352838220948252939091522055611fe98161265b565b9050611f59565b505b611ca48484612141565b6040516001600160a01b038085166024830152831660448201526064810182905261204d9085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611a84565b50505050565b610772816111b3565b60006120b1826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166121d29092919063ffffffff16565b80519091501561072557808060200190518101906120cf91906126d5565b6107255760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016106a4565b60008061214d83610eeb565b6001600160a01b0385166000908152600960205260409020805463ffffffff19164263ffffffff1617905590508015611c915780600860008282546121929190612632565b90915550506040518181526001600160a01b038516907f34f2a7363b1ef64b0b62a223c88cf3f54a68686acfcb9531d7deb46004f37c4690602001611c88565b6060611ca4848460008585600080866001600160a01b031685876040516121f9919061271b565b60006040518083038185875af1925050503d8060008114612236576040519150601f19603f3d011682016040523d82523d6000602084013e61223b565b606091505b509150915061224c87838387612257565b979650505050505050565b606083156122c65782516000036122bf576001600160a01b0385163b6122bf5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016106a4565b5081611ca4565b611ca483838151156122db5781518083602001fd5b8060405162461bcd60e51b81526004016106a49190612737565b80356001600160a01b0381168114611c9f57600080fd5b60008060006060848603121561232157600080fd5b61232a846122f5565b9250612338602085016122f5565b9150604084013590509250925092565b60006020828403121561235a57600080fd5b610890826122f5565b6000806040838503121561237657600080fd5b61237f836122f5565b915061238d602084016122f5565b90509250929050565b6000602082840312156123a857600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156123d857600080fd5b823567ffffffffffffffff808211156123f057600080fd5b818501915085601f83011261240457600080fd5b8135602082821115612418576124186123af565b8160051b604051601f19603f8301168101818110868211171561243d5761243d6123af565b60405292835281830193508481018201928984111561245b57600080fd5b948201945b8386101561247957853585529482019493820193612460565b9997909101359750505050505050565b6000806040838503121561249c57600080fd5b6124a5836122f5565b946020939093013593505050565b6000602082840312156124c557600080fd5b815160ff811681146124d657600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b600181815b8085111561252e578160001904821115612514576125146124dd565b8085161561252157918102915b93841c93908002906124f8565b509250929050565b60008261254557506001610893565b8161255257506000610893565b816001811461256857600281146125725761258e565b6001915050610893565b60ff841115612583576125836124dd565b50506001821b610893565b5060208310610133831016604e8410600b84101617156125b1575081810a610893565b6125bb83836124f3565b80600019048211156125cf576125cf6124dd565b029392505050565b600061089060ff841683612536565b81810381811115610893576108936124dd565b8082028115828204841417610893576108936124dd565b60008261262d57634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610893576108936124dd565b634e487b7160e01b600052603260045260246000fd5b60006001820161266d5761266d6124dd565b5060010190565b60006020828403121561268657600080fd5b5051919050565b604080825283519082018190526000906020906060840190828701845b828110156126c6578151845292840192908401906001016126aa565b50505092019290925292915050565b6000602082840312156126e757600080fd5b815180151581146124d657600080fd5b60005b838110156127125781810151838201526020016126fa565b50506000910152565b6000825161272d8184602087016126f7565b9190910192915050565b60208152600082518060208401526127568160408501602087016126f7565b601f01601f1916919091016040019291505056fea26469706673582212207c456537ff43ea8766aaa9f58ed3242eb96744167a0888b9ba5a6f9ae49206ab64736f6c63430008130033a26469706673582212206e4e11d036b36b56fa46684dfe46e46786c74944a47b98f99903a915a5bb544e64736f6c63430008130033
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for
interesting conversations.